My post about BabySmash got hanselmanned. There are some questions in the comments on that post that are worth addressing in another couple posts, so here’s the first. For background, we have Scott’s follow up post, describing how he changed the code because of my post.
In Scott’s post, Configuration with DataBinding, he wrote the following:
Next, we tell the Grid that’s laying out the controls that we have some static data we’ll be using and we call it "settings" because that’s the class name.
<Grid DataContext="{StaticResource settings}">
There are a few misunderstandings in this statement that I’d like to clarify, since this is a learning experience and all.
There are two big WPF concepts here, DataContext and StaticResource. I’ll tackle StaticResource here. Consider the XAML below:
1: <Window x:Class="WpfApplication1.Window1"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:System="clr-namespace:System;assembly=mscorlib"
5: SizeToContent="WidthAndHeight">
6: <Window.Resources>
7: <System:String x:Key="s">Hello</System:String>
8: <System:String x:Key="i">42</System:String>
9: </Window.Resources>
10: <StackPanel>
11: <TextBlock Text="{StaticResource s}" />
12: <TextBlock Text="{DynamicResource i}" />
13: </StackPanel>
14: </Window>
This is the world’s tiniest window. I have two TextBlocks – think of TextBlock as a simple label, for now – one below the other, whose text is set to some string values. I’ve declared the strings in the window’s ResourceDictionary, through the Window.Resources property. The Resources property is declared on FrameworkElement, from which all controls in WPF inherit, so it’s on every WPF control. The StackPanel has a ResourceDictionary, the two TextBlock’s do, the App object (not shown) does, and the system does too.
A ResourceDictionary is basically a giant hashtable. You can store any .NET object you want, like I did above. You can do this from code as well, which perhaps may be more appropriate (since XAML tends to obscure things; remember: anything in XAML can be done in [way more verbose] code):
1: public class Window2 : Window
2: {
3: public Window2()
4: {
5: //declaring the same resources in code
6: this.Resources.Add("s", "Hello");
7: this.Resources.Add("i", "42");
8: }
9: }
I have two strings sitting in a ResourceDictionary. Now, I want to access them. Let’s do that in code first. In the code below, I create my StackPanel, add my TextBlocks to it, and set the values on them. Here’s the not-quite-equivalent code version of the XAML declared at the beginning.
1: public Window2()
2: {
3: //declaring the same resources in code
4: this.Resources.Add("s", "Hello");
5: this.Resources.Add("i", "42");
6:
7: StackPanel stackPanel = new StackPanel();
8: //setting the textblocks' text property to resources
9: TextBlock textBlock1 = new TextBlock();
10: textBlock1.Text = (string) this.Resources["s"];
11:
12: TextBlock textBlock2 = new TextBlock();
13: textBlock2.Text = (string) this.Resources["i"];
14:
15: stackPanel.Children.Add(textBlock1);
16: stackPanel.Children.Add(textBlock2);
17:
18: this.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
19: this.Content = stackPanel;
20: }
I say not quite equivalent because the XAML is doing something slightly different. In the code version, I access the resource directly. In XAML, I’m telling WPF that the values I want for the Text properties are resources, one static and one dynamic (which I’ll get to in a moment). WPF will then move up the control hierarchy (I’m not sure exactly, but I think it moves up the logical tree) and check each ResourceDictionary for the key given. So it’ll check the StackPanel’s resources, then the Window’s. If it wasn’t in the Window’s resources, it would move up to the App resources, and finally to System resources. Resources are really important in WPF; it is where things like Brushes, Colours, Styles and Templates are put. If you were making a skinnable app, you’d make heavy use of resources.
Now, let’s clear up what the StaticResource does. Here’s the relevant XAML again:
<TextBlock Text="{StaticResource s}" /> <TextBlock Text="{DynamicResource i}" />
The curly braces means this is a MarkupExtension, a XAML-only concept that allows us as developers to be slightly more expressive than plain old XML allows. The meaning of static in this case may be clearer when juxtaposed with it’s brother, they DynamicResource. There is a class behind these two, StaticResourceExtension and DynamicResourceExtension. You could try writing the equivalent code; I did, and it didn’t work, but in the docs there is the ominous warning that this is used by the XAML subsystem and shouldn’t be called by humans.
The difference between StaticResource and DynamicResource is how they are accessed by WPF. The semantics of StaticResource are "just read this value once, and don’t check for changes." It’s static as in not moving, not as it is in C#. Note that the two objects in the window’s resources are plain old string instances. StaticResources are good for things that don’t change, like Brushes or Styles, or read-only data. It’s unfortunate from a learning perspective that the data in BabySmash was static in the C# sense as well, but it’s just a coincidence, honest.
DynamicResource on the other hand is a little more intensive for WPF, because it will track changes to the ResourceDictionary. So if the value of a DynamicResource changes, WPF knows about it and updates the control accordingly. This is good for data that changes, obviously.
Let’s expand the XAML just a bit, and add a button with a click handler. In the click handler, let’s update the value keyed with "i". Run the code, click the button and watch as 42 becomes world. And you don’t have to write anything to make this happen.
<StackPanel> <TextBlock Text="{StaticResource s}" /> <TextBlock Text="{DynamicResource i}" /> <Button Click="Button_Click">Click me</Button> </StackPanel>
private void Button_Click(object sender, RoutedEventArgs e) { this.Resources["i"] = "world"; }
Actually the semantics for a static resource are check once AND MAKE SURE IT IS THERE at the launch of the executable otherwise throw an exception. Dynamic resources do not check until set to a dynamic resource and if you can’t find it don’t worry about it.
Thanks for this Nice Article.
Taking your example further, I tried creating a hashtable binding to a listbox (all in xaml). Code :
01
02
03
04
500
After adding to hashtable in codebehind on button click, listbox does not get updated. Any ideas ?
Thanks.