Styles in WPF

We’ve seen how to build a WPF application using Visual Studio (VS) and Expression Blend (EB), including a simple layout and event handlers. In very simple layouts with only a few controls, it makes sense to define the properties of each control by editing them individually. However, once we get into even moderately complex layouts with a number of controls, typically we find that a number of controls share some common properties. In that case, it is tedious (to say nothing of bad programming practice) to specify the same properties for each control. In such cases, WPF provides a style which can be defined and applied to a number of controls.

Actually, WPF also provides the template which can also be applied to controls but templates are a bit more advanced so we’ll leave them for now.

Given my aversion to writing raw XAML code, it is fortunate that EB can be used to define and edit styles. Perhaps my Googling skills are deficient, but proper, detailed tutorials on how to do this seem to be rather thin on the ground, so hopefully this post will fill a niche.

To make things definite, we’ll consider creating the interface for a simple four-function calculator. The final GUI should look like this:

We’re considering only integer arithmetic with non-negative numbers here, so there are no decimal point or ‘change sign’ buttons. The reader may want to add these as exercises.

So you can follow along with the post, the VS2010 project files can be downloaded here.

We can see that there are three groups of buttons that each have their own style: the C and CA buttons, the number buttons and the arithmetic operator buttons. Before we get into defining button styles, however, we need to consider the layout of the controls.

We’ll get into a detailed discussion of layouts later, but this application gives a good example of some of the thought required in determining the layout. Thinking through the layout is important,  since some of the layouts in WPF are awkward to change once you’ve written them. The Grid layout, probably the most popular, is one of these, since the location of each component added to a Grid must be hard-coded with a given row and column number. Inserting or deleting Grid rows or columns thus can involve a lot of editing to update the locations of controls.

Vertically, the layout splits into three rows. The top row contains the numeric display, the second row contains the C and CA buttons, and the bottom row contains all the other buttons. Since the bottom row contains two distinct sets of buttons, we can split this row into two columns, with the left column containing the number buttons and the right column containing the operator buttons. Thus a good top level layout is a Grid called LayoutRoot containing 3 rows and 2 columns. Using EB, we can create these rows and columns as described in the previous post. The XAML code generated by EB is:

		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="Auto"/>
			<ColumnDefinition/>
		</Grid.ColumnDefinitions>
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition Height="Auto"/>
			<RowDefinition/>
		</Grid.RowDefinitions>

Note that we’ve set column 0 and rows 0 and 1 to “Auto” width and height respectively. This makes them size themselves to fit their contents. If we don’t specify a width or height parameter for a column or row, it defaults to a setting which takes up all remaining space in the layout. Thus the bottom row, for example, will take up all space not used by rows 0 and 1.

We can now insert the controls into LayoutRoot. The number display in row 0 is a single TextBlock, and since only one of these appears in the layout, there’s no point in defining a style for it so we just specify all its properties by editing the appropriate bits and pieces in EB. The XAML code for the result is:

  <TextBlock x:Name="NumberDisplay" Text="0" d:LayoutOverrides="Height" TextAlignment="Right" FontSize="26.667" FontWeight="Bold" Background="White" Foreground="#FF2EAD2A" Grid.ColumnSpan="2"/>

Most of the properties defined here are a matter of personal preference, but there are a couple worthy of note. First, we need to give the TextBlock a Name (NumberDisplay) since we will need to refer to it in event handler code to update its display with the entered numbers and the results of operations.

Second, we need to specify that the TextBlock spans two columns in LayoutRoot, since we want it to extend over the full width of the window.

For the row containing the C and CA buttons, we could use another Grid, but we’ve chosen to use a DockPanel instead to illustrate some of its properties. In EB, we add a DockPanel to row 1 of the Grid, and create a couple of Buttons inside it:

<DockPanel x:Name="MiscControlsPanel" Background="Black" Grid.Row="1" Margin="5" Grid.ColumnSpan="2" >
 <Button x:Name="ButtonClearAll" Content="CA" Style="{DynamicResource ButtonMiscStyle}" Margin="5,0" DockPanel.Dock="Right"/>
 <Button x:Name="ButtonClear" Style="{DynamicResource ButtonMiscStyle}" Click="ButtonClear_Click" Margin="5,0" DockPanel.Dock="Right" HorizontalAlignment="Right" />
 </DockPanel>

In EB, you can find the DockPanel and Button controls by clicking on the ‘Assets’ button (the double chevron button in the toolbar on the left, as described in the last post), then entering the control’s type in the search panel. Drag the control into the Objects and Timeline panel at the correct location. Note that the Buttons won’t look like those in the image above since we haven’t applied the style yet, but don’t panic – just leave them as they are and we’ll get to the styles in a minute.

A DockPanel allows components to be ‘docked’ to one of the four edges (or left to occupy the centre if no edge is specified). In the first line, we specify that the DockPanel is inserted in Row 1 of LayoutRoot, and that it spans 2 columns.

We’ve then added 2 Buttons to the DockPanel. You’ll see they have a Style attached, but you can ignore that for now. The important bit here is that both Buttons are given the property of DockPanel.Dock=”Right”. This means they are docked to the right edge of the panel. If more than one object is docked to the same edge, they stack up at that edge in the order in which they are added, so it’s important to add the CA button first (assuming you want it on the right), then the C button. In EB, the Dock property can be set in the Layout box on the right of the EB window.

Finally, we add the last row containing the number and operator buttons. To do this, we define one Grid for each set of buttons. The first Grid, which we’ll call GridNumberButtons, is added to Row 2, Column 0 of LayoutRoot, and the second Grid, named GridOperators,  is added to Row 2, Column 1 of LayoutRoot.

GridNumberButtons will need 4 rows and 3 columns, all of which have their height (for rows) or width (for columns) set to ‘Auto’, since we want them to size themselves to the Buttons we’ll be inserting in them. With the rows and columns defined, you can add the 10 number Buttons to GridNumberButtons, and set the row and column indexes for each Button so it goes in the right cell within the grid. You will also need to give each Button a name, so call them Button1 through Button0. Again, since we haven’t applied any styles yet, the Buttons will look pretty plain.

Finally, for GridOperators, give it 5 rows and one column, and add an operator Button to each cell in the grid. Name the Buttons ButtonPlus, ButtonMinus, ButtonTimes, ButtonDivide and ButtonEquals.

Now that we’ve defined the layout and placed all the controls within the various panels, we need to address the issue of the appearance of the various Buttons. This is where we introduce Styles.

To begin, select either the C or CA button. From EB’s Object menu, select Edit Style –> Create Empty to create a new style. In the ‘Create Style Resource’ dialog, enter a name (key) for the style (we’ve called it ButtonMiscStyle) and define the style in the Window element (This makes the style available to all controls in the current window. Styles can be saved in a Resource Dictionary for use in other projects, but we’ll not bother with those here.) Close the dialog.

You’ll note that the Objects and Timeline panel changes to show only the Style you’re currently editing, but that the Properties panel on the right still shows all the options available for editing a Button. At this point, you can edit all the properties you want to apply to your Button, so use your imagination. When you’re done, return to the main editor by clicking on the icon in the Objects and Timeline panel:

If you examine your XAML code, you’ll see that a Window.Resources section has been added to the Window tag near the top of the file. For the settings we’ve selected, this code is as follows.

	<Window.Resources>
		<Style x:Key="ButtonMiscStyle" TargetType="{x:Type Button}">
			<Setter Property="Margin" Value="5"/>
			<Setter Property="Background" Value="#FF1C2060"/>
			<Setter Property="Foreground" Value="White"/>
			<Setter Property="Content" Value="C"/>
			<Setter Property="Width" Value="50"/>
			<Setter Property="Height" Value="30"/>
			<Setter Property="FontSize" Value="16"/>
			<Setter Property="FontWeight" Value="Bold"/>
			<Setter Property="VerticalAlignment" Value="Stretch"/>
			<Setter Property="HorizontalAlignment" Value="Stretch"/>
			<Setter Property="FontStretch" Value="Normal"/>
			<Setter Property="Focusable" Value="False"/>
		</Style>
	</Window.Resources>

Most of these settings should be obvious, and you may want to change some of them if you don’t like a colour or font type or whatever. The one that may be unclear is the Focusable property at the bottom. For some bizarre reason, the default behaviour of a Button in WPF is that it will blink off and on after it is selected. Setting Focusable to False is a quick and (very) dirty way of disabling this. (The Focusable property can be found by expanding the ‘Common Properties’ panel.) It’s not an ideal solution, since a control that is not focusable cannot be selected by tabbing through the controls on the keyboard, but we’ll leave it for now, since it’s unlikely we’ll want to tab through the calculator’s buttons.

Once you’ve defined the style, you need to apply it to all the Buttons you wish to have that style. You can do this by right-clicking on the Button object itself (or on the Button’s entry in Objects and Timeline), then selecting ‘Edit Template’, then ‘Apply Resource’ and then selecting the style you want. (Confusingly, for a Button, EB offers only Edit Template, rather than Edit Style, although for some other controls, an Edit Style option is offered. In either case, find the ‘Apply Resource’ option and then select the style you want.)

To apply a style to more than one object at a time, select the first object in Objects and Timeline, then hold down the Control key and select all the other objects. Then, in EB’s Properties box, scroll down to the bottom and open the Miscellaneous panel. Find ‘Style’ in the list and click on the little square at the right (you’ll get a tooltip called ‘Advanced Options’ if you hover the mouse over the square). Open the ‘Local Resources’ menu and select the style you want. The display should update all the objects with the new style.

You can now define styles for the number Buttons and operator Buttons in the same way. The styles I’ve used are, in XAML:

		<Style x:Key="NumberButtonStyle" TargetType="{x:Type Button}">
			<Setter Property="Width" Value="40"/>
			<Setter Property="Height" Value="40"/>
			<Setter Property="Background" Value="#FFDCD6D6"/>
			<Setter Property="Content" Value="1"/>
			<Setter Property="Focusable" Value="False"/>
			<Setter Property="FontSize" Value="18.667"/>
			<Setter Property="FontWeight" Value="Bold"/>
			<Setter Property="HorizontalAlignment" Value="Center"/>
			<Setter Property="VerticalAlignment" Value="Center"/>
			<Setter Property="Margin" Value="5"/>
		</Style>
		<Style x:Key="ButtonOperatorStyle" TargetType="{x:Type Button}">
			<Setter Property="Background" Value="#FF253F25"/>
			<Setter Property="Foreground" Value="White"/>
			<Setter Property="Width" Value="40"/>
			<Setter Property="Height" Value="25"/>
			<Setter Property="Margin" Value="5,8"/>
			<Setter Property="Focusable" Value="False"/>
			<Setter Property="FontSize" Value="13.333"/>
			<Setter Property="FontWeight" Value="Normal"/>
			<Setter Property="Content" Value="+"/>
		</Style>

You can see that having to specify all these properties repeatedly for each Button that uses them would make the XAML even more bloated than it already is, so it is quite a space and time saver.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Trackbacks

  • By WPF layouts – StackPanel « Programming tutorials on January 12, 2012 at 6:23 PM

    […] the post on Styles in WPF, we built the interface to a calculator by defining some controls (mostly Buttons) and then […]

  • […] the calculator example we’ve seen a simple example using WPF’s Grid layout. In that example, we saw how to […]

  • By Printing in WPF « Programming tutorials on June 12, 2012 at 8:26 PM

    […] location for each TextBox) objects. For each TextArea, we create a TextBox. I’ve defined a WPF Style in the XAML, and used data binding to connect the Text property of the TextBox to the text stored […]

  • By WPF Context Menus « Programming tutorials on June 16, 2012 at 5:36 PM

    […] drawn. The link between TextArea (a non-graphical class) and TextBox (the WPF Control) is done via styles and data binding, but we won’t go into the details here, since we want to concentrate on the […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: