Resources and Styles
In the previous section I illustrated the basic XAML declaration of the AvalonLife main window, and talked about the resulting structure of the application’s interface both in terms of the layout and object hierarchy. The code that I presented, however, would not create anything of interest if it were compilable, because most of the detail had been removed for clarity’s sake. There’s no way I can cover all the details regarding how to instantiate, position, style, and interact with GUI elements in this article. But I do want to take a quick tour of a couple of very important subjects, with emphasis on how I applied the illustrated techniques in AvalonLife. These two topics are resources, and styles, and how they effect controls. To get going let’s take a look at a section of XAML that was redacted from the example posted in the last section. The code is takern from the Window.Resources section of the ALMainWin.xaml file. I have presented two resource declarations, and following them the declarations of the two controls that use the resources. Bear in mind that all the declarations of the containing panels and other controls have been removed. Here it is:
<Window x:Class="AvalonLife.ALMainWin" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="AvalonLife 1.0" Height="600" Width="600" Name="ALMainWindow" Background="#FFFFFFFF" Loaded="ALMainWin_OnLoaded"> <Window.Resources> <Style x:Key="RunSpeedSliderStyle" TargetType="{x:Type Slider}"> <Setter Property="Width" Value="120" /> <Setter Property="Value" Value="{Binding Path=TimerInterval, Mode=TwoWay}" /> <Setter Property="Orientation" Value="Horizontal" /> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="IsSnapToTickEnabled" Value="True" /> <Setter Property="Minimum" Value="100" /> <Setter Property="Maximum" Value="1000" /> <Setter Property="TickPlacement" Value="BottomRight" /> <Setter Property="TickFrequency" Value="100" /> <Setter Property="IsDirectionReversed" Value="True" /> <Setter Property="IsMoveToPointEnabled" Value="True" /> </Style> <Style x:Key="GenCountStyle" TargetType="{x:Type TextBlock}"> <Setter Property="Foreground" Value="Blue" /> <Setter Property="Text" Value="{Binding Path=Generation}" /> </Style> <!-- … --> </Window.Resources> <!-- stuff removed --> <StatusBar Background= "{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" Height="30" DockPanel.Dock="Bottom" Padding="4,0,4,0"> <!-- … --> <StatusBarItem> <TextBlock Name="StatusGenCount" Width="30" Style="{StaticResource GenCountStyle}" /> </StatusBarItem> <!-- … --> <StatusBarItem> <Slider Name="RunSpeedSlider" Style="{StaticResource RunSpeedSliderStyle}" /> </StatusBarItem> </StatusBar> <--! stuff removed --> </Window>
The first chunk of XAML in the example declares a block of resources, which happen to be Styles in this case. From the markup you can infer that the Windows class has a property, Resources, that is a collection, and in fact you’d be right. Every WPF object that derives from System.Windows.FrameworkElement, as Window does, inherits this property. The type of the property is ResourceDictionary, which is a derived class that implements IDictionary, among other interfaces. Each element contained in the dictionary is of type DictionaryEntry, which is accessible by a key, and wraps the actual contained object. At first the declarations above may appear puzzling, because there is nothing in them that recognizes the structure and interface of the ResourceDictionary itself. This is an example of implicit collection handling in XAML (so, for that matter, are the Menu and StatusBar declarations from the previous section). The XAML compiler knows that the Window.Resources property is a dictionary, and so it infers the <DictionaryEntry></DictionaryEntry> that might otherwise surround each of the Style declarations above. It also knows to look for the “x:Key” attribute in items being assigned to the dictionary. If you look up the declaration of the Style class you won’t find a Key property. The compiler is inferring that it might be there in this specific context. Once a resource has been assigned to a resource dictionary it can be referred to by its keyname anywhere within the XAML markup, as long as it is in scope. What does it mean for a resource to be in scope? The notion of scope becomes important in various areas of WPF, so it is worth spending a moment on. The best way to understand it is in terms of the object hierarchy we discussed at the end of the previous section.
A resource can be contained in the Resources property of any control derived from FrameworkElement. In the case of the XAML markup above the Style resources are owned by the Window class. Not surprisingly, a resource that is declared in the scope of an element is available to all the children of that element in the object tree. It is “in scope” within the declarations of all those children. If you put a resource in the Application.Resources dictionary it will be available anywhere in the application. If you put it in a Menu.Resources dictionary then it will be available to that Menu and its children (MenuItems and Separators). When a reference to a resource is encountered in XAML a search is first performed at the level of the current object, and if the target resource isn’t found there the search bubbles up through the tree until it reaches Application, at which point it will cause a compile-time error if the reference still hasn’t been found. For static resources these checks all happen when the XAML is compiled. However you can also have dynamic resources that are loaded at runtime, as I’ll show below. I’ve been using Styles as an example here, but any object can be a resource. Strings, instances of local or system objects, pretty much any .Net class that is accessible in the current set of namespace (namespaces other than the default might require a namespace name to be prefaced to the class name). Once again we have a mechanism that introduces a great deal of additional flexibility, and the design choices that go along with it. I haven’t been using WPF long enough to have developed firm opinions about what should go where, or how best to use WPF resources. I do still prefer to keep bulk resources like localizeable string databases in the .resx file, and fortunately there is nothing preventing you from doing that in a WPF application, and mixing the two kinds of resources any way that you wish.
I’ve purposely stayed away from saying anything more about the Style declarations above until I got the general notion of resources out of the way. Now that we have talked at a very high level about the mechanism for declaring resources we can look at how to apply these specific Style resources to controls. Note that the same mechanisms, in general, work for assigning any resource value to an object property, however since styles are so central to WPF GUI development, I chose to use them to explain both how to assign the value of a resource to a control property, as well as how Styles in particular affect the controls they are assigned to. This is another area where, if you are a web developer, your ears have probably pricked up. XAML styles perform something of the same role that CSS styles perform in HTML markup, but the mechanism is much more flexible and powerful than CSS. The best way to see how it works is to jump back up and take a look at the declarations of the controls in the example. I’ve only shown two styles, one that applies to a Slider control, and another that applies to a TextBlock. Before we look at them specifically let’s talk briefly about the controls.
What you see in this example is the declaration of a StatusBar, which is in the System.Windows.Controls.Primitives namespace. A StatusBar is a type of System.Windows.Control.ItemsControl. As such it is a container for StatusBarItems, the sole point of which is to wrap a control that you want to place in the StatusBar. The StatusBar itself has properties, such as Height, Width, and Background. That last property is of type System.Windows.Media.Brush, and represents the brush that will be used to paint the background of the StatusBar. I could have simply set this to a named color, such as “Gray.” Or I could have used a stringified RGB value, such as “#2e2e2e.” In this case what I want to happen with the StatusBar is that I want it to use the system-defined color for menu bars. That way, if the user changes the system menu bar color my program will react appropriately. Fortunately the System.Windows namespace gives us the SystemColors class (and there is also a System.Drawing.SystemColors class, naturally). I want to get the appropriate color out of that and link it to the Background property of my StatusBar. The following line of code accomplishes this.
Background=”{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}”
Let’s take a look at this from the outside in. The Background= part, obviously, tells the XAML compiler that what follows between the quotes should be assigned to the StatusBar.Background property. The markup extensions {DynamicResource ResourceKey} and {StaticResource ResourceKey} establish a link between a Property and the value of a specific object which is accessed using the ResourceKey. The difference between the two is that StaticResource is evaluated at compile time only, while DynamicResource creates a reference that is not resolved until runtime. Inside the {DynamicResource } extension you see another markup extension: {x:Static }. This extension is used to refer to a static by-value property of a type (or the value of an enumeration, a field, or a constant). In this case I have created a reference to the SystemColors.MenuBarBrushKey property. The value of this static property is the dictionary key that will return the MenuBarBrush during a resource lookup. If all I wanted was to make sure the StatusBar.Background property was set to whatever the system menu bar background brush happened to be at compile time, I could do this:
Background=”{x:Static SystemColors.MenuBarBrush}”
The difference here is that the resource lookup mechanism is not involved. All we have done is create a compile-time reference to a static member of the SystemColors class. Since there is no resource lookup involved I have used the MenuBarBrush member of SystemColors, not the MenuBarBrushKey. So far we’ve seen how you can create a dynamic resource link to a property of a system object, and we’ve seen how you can create a static reference to a property of that same system object. How do we create a resource link to the resources we declared and stored in Window.Resources? Assuming we had defined a brush and stored it in the resources collection of one of our parent controls, the following line would accomplish that:
Background=”{StaticResource MyBrushResourceKeyname}”
Likewise if I wanted the resource lookup to be done at runtime I would replace StaticResource in the above example with DynamicResource. So that’s the big picture with respect to resources: a resource can be any system object whose value is accessible; it can be any object that we insert into the Resources dictionary of a FrameworkElement; we can use the resource lookup mechanism to get the value of a resource and assign it to a compatible property; and we can do all of this from declarative XAML. Referring back to the XAML for the StatusBar and its children, you should now begin to see how that big picture functions with respect to our Style resources: in the Window.Resources collection we have stored several Style objects. In the declaration of the items in the StatusBar we are using the resource lookup mechanism to grab one of those Style objects and assign it to the Style property of a control. But what’s the result of doing that? What are styles? And how do they affect a control? Simply put, a style is a collection of values that will be assigned to properties of a control that applies the style. In that respect it does just what CSS class definitions do to HTML elements they are applied to. XAML styles mimic CSS styles in other ways. For example, we can select a style explicitly by keyname or allow it to “cascade” down through the control tree to apply to all instances of a particular type. I’ll talk about that in a minute, but for now let me pull out just one style declaration from the example, and talk specifically about what’s going on in it:
<Style x:Key="RunSpeedSliderStyle" TargetType="{x:Type Slider}"> <Setter Property="Width" Value="120" /> <Setter Property="Value" Value="{Binding Path=TimerInterval, Mode=TwoWay}" /> <Setter Property="Orientation" Value="Horizontal" /> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="IsSnapToTickEnabled" Value="True" /> <Setter Property="Minimum" Value="100" /> <Setter Property="Maximum" Value="1000" /> <Setter Property="TickPlacement" Value="BottomRight" /> <Setter Property="TickFrequency" Value="100" /> <Setter Property="IsDirectionReversed" Value="True" /> <Setter Property="IsMoveToPointEnabled" Value="True" /> </Style>
As the example shows, a Style has some attributes and a collection of Setter objects that are each responsible for putting a specified value into a specified property of the object the style is applied to. In the first Style declaration I have specified a key under which this style will be accessible in Window.Resources. In this case it is “RunSpeedSliderStyle” to indicate the control it will be applied to. You can see this key used in the StaticResource extension in the Slider control declaration. I have also specified a value for the Style.TargetType property. TargetType is similar to a CSS element in that it selects the type of object this style will be applicable to. The extension {x:Type } is the XAML equivalent of typeof(Type) in C#. The Key and TargetType properties have rather complicated effects on the markup. I’ll try to sort them out. You may omit either Key or TargetType, but not both. If a Style has a key, then it can only be applied to a control by assigning it to the control’s Style property using the resource keyname. If it has a Key, but no TargetType, then you have to preface the property names in the Setters with the class name, i.e. TextBlock.Foreground, otherwise the compiler doesn’t know what class you intend and whether it has that property. If it doesn’t have a Key, then it has a TargetType, and any child control below the point in the tree where the resource is stored, that is of the specified type, will have that style applied. That’s the cascade part I mentioned above. If a control of the specified type explicitly assigns a Style to its Style property, then it will override this default selection. It sounds complicated, but once it clicks you have to admire the consistency with which everything fits together.
The work of styling is done in the Setter objects. Each one has a property named Property, that specifies the name of the property on the target control that is to be set, and a property named Value that contains an object whose value is to be assigned to the target property. The simpler ones in the example are self-explanatory. It isn’t hard to see what a Setter with a Property of “Foreground” and a Value of “Blue” is going to do. The Setters that are interesting are the ones that specify data bindings for property values. You can see an example in the declaration of RunSpeedSliderStyle above. This style is applied to a Slider control, and that slider is used to control the simulation speed in AvalonLife. The following declaration creates a Setter that will apply a data binding to the Slider’s value property:
<Setter Property=”Value” Value=”{Binding Path=TimerInterval, Mode=TwoWay}” />
When this style is applied a Binding object will be created that references the TimerInterval property of… what? Most of the WPF controls that you use that can display content are derived from System.Windows.FrameworkContentElement, and inherit a property called DataContext. Data binding is overall too big a topic to go into here in any detail. If you are interested, and you should be, see Bea Costa’s blog. In brief: you can set a control’s data context to any object and when the Binding is evaluated the referenced property path will be looked for on that object. In the case of the example above the LifeSim class is going to be used as a data context for this slider. I set that up in the code-behind. It can be done in XAML, but in my case the LifeSim may be destroyed and recreated, and so I just set it programatically. If the data source is static and isn’t going to change at runtime, then declaring it in XAML has some benefits. One of them is that data contexts cascade down the object tree like resources.
Two other points about this example. First, the mode of the Binding is specified as TwoWay. What this means is that the control will read the TimerInterval property from the LifeSim class, and set itself, but it will also set that property on the LifeSim instance if the user changes the slider position. The value flows both ways. This means that the user can adjust the slider and the timer interval will be updated, or I can change the timer interval from code and the slider will be updated. Well, sorta. That’s the second thing. If you bind a target property to a property on a source object, then the target will read the value at initialization time. If you change that value later you have to fire a NotifyPropertyChanged event to alert the control that its bound data has changed. You do this by implementing INotifyPropertyChanged. I should mention that there is no reason a data binding has to be declared in a Style (as opposed to simply being declared and assigned directly to a property of a control), however I had problems getting it to work outside of style declarations. Most likely this is something I just don’t understand yet. Lastly, let’s zoom in on how the Style gets assigned in the declaration of the slider control:
<Slider Name=”RunSpeedSlider” Style=”{StaticResource RunSpeedSliderStyle}” />
Note that this property assignment follows the general outline of assigning a resource value to a property, but in this case the resource is a Style, and the property is the Slider.Style property. With this single assignment we set the slider’s orientation, range, width, height, and other properties, including the databinding discussed above. That’s all I’ll say about binding here, and styles and resources too for that matter. They are all more than enough meat for a series of articles in their own right. But I wanted to introduce these concepts, because in the next section we’re going to talk about some of ways these mechanisms were used to do the really fun part of programming AvalonLife: animate the model.
Introduction
Part 1 – The Game of Life
Part 2 – AvalonLife’s Architecture
Part 3 – XAML and the User Interface
Part 4 – Resources and Styles
Part 5 – Drawing the Grid
Part 6 – Conclusion