"There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies and the other is to make it so complicated that there are no obvious deficiencies". C.A.R. Hoare
I had some trouble getting this to work, so I thought others may benefit from it. Here is the scenario... we have a bunch of control templates and styles we want to define for our application... with the possibility at some point we may want to modify aspects of what the application looks like. The modification would be handled by replacing the dll that held the style of the application. I had read in the documentation that it was possible do embed a ResourceDictionary in another assembly and then reference the assembly using merged dictionaries and an URI.
The syntax to identify the merged dictionary was simple enough... and I added it to my app.xaml.
1: <Application x:Class="WPFThemeSeperation.App"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: StartupUri="Window1.xaml"
5: >
6: <Application.Resources>
7:
8: <ResourceDictionary>
9: <ResourceDictionary.MergedDictionaries>
10: <ResourceDictionary Source="pack://application:,,,/OtherTheme;component/skin.xaml"></ResourceDictionary>
11: </ResourceDictionary.MergedDictionaries>
12: </ResourceDictionary>
13: </Application.Resources>
14: </Application>
The above configuration indicates that I want to use an assembly called OtherTheme... which is the name of the a dll with out the .dll extension... and in that assembly called OtherTheme I have skin.xaml in the root of the project.
I then created a class library project which was added to my solution. Clicked on my newly added project and selected Add -> New Item... at that point I selected a ResourceDictionary (WPF) from the dialog and named it skin.xaml... which caused it to add some references to the project. I modified the skin.xaml file to create a new style for my buttons (shown below):
1: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3: >
4: <Style TargetType="{x:Type Button}">
5: <Setter Property="Foreground" Value="white"/>
6: <Setter Property="Margin" Value="1"/>
7: <Setter Property="Template">
8: <Setter.Value>
9: <ControlTemplate TargetType="{x:Type Button}">
10: <Grid>
11: <Rectangle x:Name="GelBackground" Opacity="1" RadiusX="9" RadiusY="9"
12: Fill="Red" StrokeThickness="0.35">
13: <Rectangle.Stroke>
14: <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
15: <GradientStop Color="White" Offset="0"></GradientStop>
16: <GradientStop Color="#666666" Offset="1">
17: </GradientStop>
18:
19: </LinearGradientBrush>
20: </Rectangle.Stroke>
21: </Rectangle>
22: <Rectangle x:Name="GelShine" Margin="2,2,2,0" VerticalAlignment="Top"
23: RadiusX="6" RadiusY="6" Opacity="1" Stroke="Transparent" Height="15px">
24: <Rectangle.Fill>
25: <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
26: <GradientStop Color="#ccffffff" Offset="0"/>
27: <GradientStop Color="Transparent" Offset="1"/>
28: </LinearGradientBrush>
29: </Rectangle.Fill>
30: </Rectangle>
31: <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
32: </Grid>
33: <ControlTemplate.Triggers>
34: <Trigger Property="IsMouseOver" Value="True">
35: <Setter Property="Fill" TargetName="GelBackground">
36: <Setter.Value>
37: <RadialGradientBrush>
38: <GradientStop Color="Lime" Offset="0"/>
39: <GradientStop Color="DarkGreen" Offset="1"/>
40:
41: </RadialGradientBrush>
42: </Setter.Value>
43: </Setter>
44: </Trigger>
45: <Trigger Property="IsPressed" Value="true">
46: <Setter Property="Fill" TargetName="GelBackground">
47: <Setter.Value>
48: <RadialGradientBrush>
49: <GradientStop Color="#ffcc00" Offset="0"/>
50: <GradientStop Color="#cc9900" Offset="1"/>
51: </RadialGradientBrush>
52: </Setter.Value>
53: </Setter>
54: </Trigger>
55: </ControlTemplate.Triggers>
56: </ControlTemplate>
57: </Setter.Value>
58: </Setter>
59: <Style.Triggers>
60: <Trigger Property="IsMouseOver" Value="True">
61: <Setter Property="Foreground" Value="Black"/>
62: </Trigger>
63: <Trigger Property="IsPressed" Value="True">
64: <Setter Property="Foreground" Value="Black"/>
65: </Trigger>
66: </Style.Triggers>
67: </Style>
68: </ResourceDictionary>
When I ran the application... I got the following error:
System.Windows.Markup.XamlParseException was unhandledMessage="'pack://application:,,,/OtherTheme;component/skin.xaml' value cannot be assigned to property 'Source' of object 'System.Windows.ResourceDictionary'. Cannot locate resource 'skin.xaml'. Error at object 'System.Windows.ResourceDictionary' in markup file 'WPFThemeSeperation;component/app.xaml' Line 10 Position 25."
Hmmm... I knew it was in there... but it wasn't finding it. I thought it might be a path issue so I popped open Reflector and looked in the binary to see if it was compiled to a resource... nothing was there.
One thing I noticed in the documentation was a reference to marking the resource with a Build Action "Resource"... the problem was that the Resource option wasn't available when looking in the Build Action... it defaulted to page when it was added. I assumed that when the ResourceDictionary file was added through the New Item menu it had handled the modification of the project that would expose "Resource" as an option... after all it brought in a couple of references automatically. That assumption was not correct... and this was the missing piece to the puzzle.
Modifying the Project File
You need to make the following modifications to the class library project that hosts your styling. Add line 2 as shown below which will import the .Net 3.0 build targets:
1: <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
2: <Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />
Also modify the build action for your skin.xaml file to "Resource"... as shown below:
1: <ItemGroup>
2: <Resource Include="skin.xaml" />
3: </ItemGroup>
Now when you build you should get a resource compiled into the
Note: If you change the name of the resource file at a later time, make sure you Clean the Solution and then Rebuild the Solution... both options are found on the context menu of the Solution in the Solution Explorer, otherwise you will get an error indicating that your xaml file can not be found.
Another Note... this isn't the most flexiable approach. We took a very simple stance.... we would not allow our users to change the look of the application and we didn't want to have to recompile the entire application if we decided to change the style of the application. This allows us to compile a seperate "skin" dll and drop it in the application directory at anytime to change the way the application looks.
Posted in WPF |Comments [1]
Sysknowlogy