Usually, the WPF controls are declared in the .xaml file and not in the code behind (.xaml.cs file). However, we might need to use some of those controls in the code behind in order to manipulate them. How can we get the handle of such a control if it “resides” in the xaml file? Take a look at the following xaml code:
<Window x:Class=”WpfApplication2.Window1″ xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” Title=”Window1″ Height=”300″ Width=”300″ MouseLeftButtonDown=”window_Clicked”> <Grid> <TextBlock x:Name=”fullNameControl” FontSize=”20″ Text=”{Binding Path=FullName}”> </TextBlock> </Grid> </Window>
There is a TextBlock called fullNameControl, in the code behind I change its background color to gold when the window is clicked:
public partial class Window1 : Window { public Window1() { InitializeComponent(); this.DataContext = new DemoData(); } void window_Clicked(object sender, MouseButtonEventArgs e) { this.fullNameControl.Background = Brushes.Gold; } }
This is very straightforward, fullNameControl becomes a property of the window class so it is very simple to manipulate it. But what if we decided to create a DataTemplate which contain the TextBlock control, like this:
<Window x:Class=”WpfApplication2.Window1″ xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:data=”clr-namespace:WpfApplication2″ Title=”Window1″ Height=”300″ Width=”300″ MouseLeftButtonDown=”window_Clicked”> <Window.Resources> <DataTemplate x:Name=”myTemplate” DataType=”{x:Type data:DemoData}”> <TextBlock x:Name=”fullNameControl” FontSize=”20″ Loaded=”fullNameControl_Loaded” Text=”{Binding Path=FullName}”> </TextBlock> </DataTemplate> </Window.Resources> <Grid> <Label Content=”{Binding}”></Label> </Grid> </Window>
Now, when trying to compile the code, I get the following error: “error CS1061: ‘WpfApplication2.Window1′ does not contain a definition for ‘fullNameControl’ and no extension method ‘fullNameControl’ accepting a first argument of type ‘WpfApplication2.Window1′ could be found (are you missing a using directive or an assembly reference?)”. OK, the window class doesn’t have fullNameControl property anymore because it is declared in the DataTemplate so there must be another way to do it.
Template.Find() doesn’t work with DataTemplates: Someone suggested that I can use the Template.Find() method, but it turns out that it doesn’t work for DataTemplates, Template.Find() is only good for ControlTemplates (read more about creation of new Control Templates in How to use Microsoft Expression Blend to modify a control).
So, I googled a little bit and didn’t find much about that issue, there were 2 entries where this issue is brought up:
-
http://forums.msdn.microsoft.com/en-US/wpf/thread/e06a1d2b-f458-4e9b-8c71-04df332d5299/ - If you read this thread till the end, you will find out that the solution suggested requires our DataTemplate to have a x:key which means that we can’t define it for a data type (as defined in the example above). That is an unacceptable solution…
-
http://webpurityltd.wordpress.com/2008/04/15/wpf-making-good-progress/ - the suggested solution is very specific and not general and it doesn’t work for me!
Finally, I came up with my own idea: subscribe to the ‘fullNameControl’ Load event (in the xaml) and save the sender as a TextBlock so from now on, we have our control handle as a class member called m_textBlock…
public partial class Window1 : Window { public Window1() { InitializeComponent(); this.DataContext = new DemoData(); } void fullNameControl_Loaded(object sender, RoutedEventArgs e) { m_textBlock = sender as TextBlock; } void window_Clicked(object sender, MouseButtonEventArgs e) { m_textBlock.Background = Brushes.Gold; } private TextBlock m_textBlock; }
When the window Click event is raised, the background color of the text block is changed to gold. This is how I can use controls which are defined inside a DataTemplate in the code behind… What do you think about my solution? Do you know any better idea? I will be glad to hear your opinions.
By Russell Mull on Aug 8, 2008 | Reply
A clever workaround. But if you have to use it, you’re probably doing things wrong. In my ~1 year of doing WPF, I haven’t seen anything I couldn’t solve via either more clever databinding, or by making a custom control and binding to it. The second strategy, in particular, can save large amounts of code and pain if used judiciously.
Really, if you’re using the XAML side correctly, you ought to be able to do anything you want without having to reach into the UI from the code-behind.
By Shahar Y on Aug 8, 2008 | Reply
@ Russell Mull
I totally agree with you, if you use MVC pattern, there shall be no need to reach into the UI from the code-behind !
But sometimes your bos can decide to give the ownership of a big control which was not designed by you, and you have no time for refactoring…
By Jon von Gillern on Aug 8, 2008 | Reply
So why not just add a trigger via a style to your template? The template doesn’t need to change (except where you want it) and you don’t have to touch code behind.
By Shahar Y on Aug 8, 2008 | Reply
@ Jon von Gillern
Sometimes you need that a control in your DataTemplate will react to something that is in another Template…
But, in general, I agree that you should do your best to avoid touching the code behind.
If you have no other choice, you can use my “technique”