Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
An issue that many WPF developers come across is the need to reuse Xaml across several different Xaml files. For example snippet 1 shows some Xaml I want to reuse in several other Xaml files. I could just cut and paste the Label and every ComboBox controls, but that would result in a lot of bloat and when I wanted to change the look of my Label or ComboBox I would have to update ever instance of the controls.
Snippet 1. – Window1.xaml
<Window x:Class="WPFRefactoring.Window1" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" Title="WPFRefactoring">
<Grid>
<StackPanel Orientation="Horizontal">
<Label Foreground="Blue" FontSize="20" FontFamily="Edwardian Script ITC">Can you read this font?</Label>
<ComboBox Name="comboBox1" Background="Tomato">
<TextBlock Text="Yes"/>
<TextBlock Text="No"/>
<TextBlock Text="What's the questions?"/>
</ComboBox>
</StackPanel>
</Grid>
</Window>
Figure 1. – Window1
WPF provides a few different ways to mitigate this problem. The first is to use a local style as shown in snippet 2. I simply migrate the properties that I want to be reused into a style for the control.
Snippet 2. – Window1.xaml using Styles.
<Window x:Class="WPFRefactoring.Window1" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" Title="WPFRefactoring">
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Edwardian Script ITC"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Tomato"/>
</Style>
</Grid.Resources>
<StackPanel Orientation="Horizontal">
<Label>Can you read this font?</Label>
<ComboBox>
<TextBlock Text="Yes"/>
<TextBlock Text="No"/>
<TextBlock Text="What's the questions?"/>
</ComboBox>
<Label Content="This Label uses the Style too."/>
</StackPanel>
</Grid>
</Window>
Figure 2. - Window1
This works well as I didn’t have to write the properties for the second label I added , but I want every Label and every ComboBox in my application to look this way not just those displayed in Window1.xaml. To do this I need put my style in the <Application.Resources> section of their MyApp.xaml file. This is shown in snippet 3. and the change to Window1.xaml is shown in snippet 4. I simply cut and pasted the styles from Window1.xaml into MyApp.xaml and deleted the resource section from Window1.xaml.
Snippet 3. – MyApp.xaml
<Application x:Class="WPFRefactoring.MyApp" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" StartupUri="Window1.xaml">
<Application.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Edwardian Script ITC"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Tomato"/>
</Style>
</Application.Resources>
</Application>
Snippet 4. - Window1.xaml
<Window x:Class="WPFRefactoring.Window1" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" Title="WPFRefactoring">
<Grid>
<StackPanel Orientation="Horizontal">
<Label>Can you read this font?</Label>
<ComboBox>
<TextBlock Text="Yes"/>
<TextBlock Text="No"/>
<TextBlock Text="What's the questions?"/>
</ComboBox>
<Label Content="This Label uses the Style too."/>
</StackPanel>
</Grid>
</Window>
Not only does this allow me to reuse the Label and ComboBox Styles throughout my application, but it makes my Window1.Xaml file easier to read. Snippet 5 shows a new Xaml file that makes use of the same style. Notice that the writer of Window2.xaml doesn’t need to know about my application level styles.
Snippet 5. – Window2.xaml
<Window x:Class="WPFRefactoring.Window2" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" Title="WPFRefactoring">
<Canvas>
<StackPanel Orientation="Horizontal">
<Label>Style Still Works</Label>
<ComboBox>Same Here</ComboBox>
</StackPanel>
</Canvas>
</Window>
Figure 3. – Window 2
What if I wanted to have all of the dialogs in my application use a different style then the application level style? I could once again copy and paste, ever Label and every ComboBox style section, but this would still run into the update and bloat problem. The solution is to create a ResourceDictionary item and place my dialog styles in it. Then reference my style in my dialogs.
Snippet 6 shows my ResourceDictionary, note that it looks very similar to my application resources section in MyApp.xaml, but every Style must use a key, because this is a dictionary.
Snippet 6. – MyResourceDictionary.xaml
<ResourceDictionary xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005">
<Style x:Key="MyLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Tahoma"/>
</Style>
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Yellow"/>
</Style>
</ResourceDictionary>
Snippet 7. – MyApp.xaml
<Application x:Class="WPFRefactoring.MyApp" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" StartupUri="Dialog.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Edwardian Script ITC"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Background" Value="Tomato"/>
</Style>
</Application.Resources>
</Application>
Snippet 8. – Dialog.xaml
<Window x:Class="WPFRefactoring.Dialog" xmlns="https://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="https://schemas.microsoft.com/winfx/xaml/2005" Title="WPFRefactoring">
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource MyLabelStyle}"/>
</Grid.Resources>
<StackPanel Width="250">
<Label>Resources are fun.</Label>
<ComboBox>Application Level ComboBox</ComboBox>
<ComboBox Style="{StaticResource MyComboBoxStyle}">My Overriden Style</ComboBox>
</StackPanel>
</Grid>
</Window>
Figure 4. – Dialog
Note that in snippet 8 I show two different ways to reference the styles defined in the MyResourceDictionary.xaml file. If I planned on having multiple Label’s or ComboBox’s or I wanted to use both the application level style and the style in my ResourceDictionary I would define a new style that is based on the style in my ResourceDictionary as shown in snippet 9.
Snippet 9.
<Grid.Resources>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource MyLabelStyle}"/>
</Grid.Resources>
If I was only going to use a style once in a file I would just reference the style directly.
Snippet 10.
<ComboBox Style="{StaticResource MyComboBoxStyle}">My Overriden Style</ComboBox>
Styles are a great way to reuse Xaml in a WPF application. They make it easier to update the look of an application, reduce the size of an application and reduce the complexity of reading xaml files.
Comments
- Anonymous
June 13, 2009
PingBack from http://gardenstatuesgalore.info/story.php?id=1871