I'm designing some themes for my C# / XAML application and would like to define my theme in one XAML file (ResourceDictionary), with variations in colour in several other XAML files (ResourceDictionaries).
So, I'm attempting:
<Style TargetType="ListBox">
<Setter Property="Foreground" Value="{StaticResource ResourceKey=ForegroundBrush}" />
<Setter Property="Background" Value="{StaticResource ResourceKey=BackgroundBrush}" />
</Style>
In a generic theme XAML file which is in a class library and is not referenced from my main app. I then have a second file:
<Color x:Key="BackgroundColour" A="#FF" R="#10" G="#10" B="#40" />
<Color x:Key="BackgroundColour2" A="#FF" R="#10" G="#10" B="#FF" />
<Color x:Key="BorderColour" A="#FF" R="#00" G="#00" B="#FF" />
<Color x:Key="ForegroundColour" A="#FF" R="#FF" G="#FF" B="#FF" />
<Color x:Key="ForegroundColour2" A="#FF" R="#80" G="#80" B="#FF" />
<LinearGradientBrush x:Key="GradientForegroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource ForegroundColour}" Offset="0"/>
<GradientStop Color="{StaticResource ForegroundColour2}" Offset="1"/>
</LinearGradientBrush>
I can obviously then have several of these files defining different colours on the same basic theme. I have attempted:
<ResourceDictionary Source="basestyle.xaml" x:Key="basestyle" />
I would then reference bluestyle.xaml, redstyle.xaml etc... from my application. This works well if I copy the entire theme across to each file, but being able to reuse the basic code seems neater.
Is there a way to do this?
If you want to separate the resources like brushes or specific styles from your theme styles and control templates, you can simply create the generic Themes\Generic.xaml resource dictionary with references to recource keys as you did in your example with e.g. ForegroundBrush.
Then you can create a specific style resource dictionary, e.g. redstyle.xaml that contains all the resources with the correspondig keys. Alternatively, you could additionally create another basestyle.xaml resource dicitonary that contains some resources shared by all conrete theme variations that you do not want to store in the theme resource dictionary either.
In order to apply a style, add the necessary resource dictionaries to the App.xaml file as merged dictionaries. Please note, that the order matters. Include resource dictionaries that are needed by other resource dictionaries first. The lookup of StaticResources and DynamicResources will find the resource with a matching key, that was added last here. For more information on that, refer to Lookup behavior for XAML resource references.
<Application x:Class="YourApplication"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Base style that includes all default resources and styles. -->
<ResourceDictionary Source="basestyle.xaml"/>
<!-- Red style that includes just specific resources and style that changed. -->
<ResourceDictionary Source="redstyle.xaml"/>
<!-- Your control theme that references the resources and styles. -->
<ResourceDictionary Source="Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
If you intend to switch the styles at runtime, you will have to replace the corresponding dictionaries in code, e.g.:
In App.xaml.cs by modifying the Resources dictionaries
By using Application.Current.Resources.
If you do not replace all dictionaries, including your generic control themes dictionary, you will have to use the DynamicResource markup extension instead of StaticResource, because the latter one does not pick up changes to references resources at runtime.
The StaticResource Markup Extension processes a key by looking up the value for that key in all available resource dictionaries. Processing happens during load, which is when the loading process needs to assign the property value. The DynamicResource Markup Extension instead processes a key by creating an expression, and that expression remains unevaluated until the app runs, at which time the expression is evaluated and provides a value.
Yes you can create different Resource.xaml files in same project, you just need to load that files in App.cs code as,
if(_theme=="theme1")//you can use switch statement if themes are more
{
ResourceDictionary myResourceDictionary1 = new ResourceDictionary();
myResourceDictionary1.Source = new Uri("theme1.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(myResourceDictionary1);
}
else
{
ResourceDictionary myResourceDictionary2 = new ResourceDictionary();
myResourceDictionary2.Source = new Uri("theme2.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(myResourceDictionary2);
}
Related
So I have got a few DataTemplates in a User Control Library. These DataTemplates reside in themes\generic.xaml resource dictionary and can be consumed in the library project without problem.
This library project is used by the main desktop application project, but at runtime Application.Current.FindResource() cannot locate any of the resources defined in the library. (I thought it would!)
So I went ahead and merged that resource dictionary into Application resource dictionary:
<Application x:Class="Application">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ControlsLibraryProj;component/themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
and voila! it can now access those DataTemplates. My question is, did I do it correctly? Do I need to merge a dictionary defined in a referenced project? Somehow I have the impression that Application.Current.FindResource() would have access to all the resources defined in all loaded modules (exe and dll). Is that correct and have I unnecessarily loaded them twice by merging?
My question is, did I do it correctly?
Yes.
Do I need to merge a dictionary defined in a referenced project?
Yes, if you intend to actually use the styles defined in the referenced Project in your app project.
The only exception is the default control styles, i.e. the default styles for any controls that are defined in the referenced project. These are the ones that you typically define in themes/generic.xaml and these will be applied to any instances of the corresponding Controls that you create in your application without you having to merge themes/generic.xaml.
If you wrote the user control library by your own, I would rather suggest you using generic.xaml for declaring resources just for your custom controls. If you do not want to or you cannot do it, IMHO you should use ComponentResourceKey.
You can find a sample here (take a look to the "Defining resources at the theme level" section).
So your resource will be declared as:
<LinearGradientBrush
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, ResourceId=ButtonBrush}"
StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="White" Offset=".8" />
</LinearGradientBrush>
And you can retrieve it
ComponentResourceKey brushKey = new ComponentResourceKey(typeof(Painter), "MyEllipseBrush");
ellipseBrush = (Brush)Application.Current.TryFindResource(brushKey);
Please pay attention because:
Implicit style application does not occur on the theme level. Suppose
you want all the labels on your controls to have a certain style. If
you define the style in at the element level, you do not have to give
the style an explicit key, the labels will use the style
automatically. This is not the case for resources at the theme level.
You must define a key and reference the style every place you want to
use it.
I hope this can help you.
EDIT: Looks like this was related to the way I declared the color - using hex value seems to bring it through fine.
EDIT: This about referencing from the App.xaml file. The color itself works fine if declared as a local resource.
I've created a colour:
<SolidColorBrush x:Key="TestBlue">
<SolidColorBrush.Color>
<Color R="0" G="86" B="45"/>
</SolidColorBrush.Color>
</SolidColorBrush>
and placed it in a ResourceDictionary. I've then referenced it in a MergedDictionary:
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colours.xaml"/>
<ResourceDictionary Source="View\Item\ItemResource.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
When I apply it to the background of a Button as a StaticResource it doesn't apply:
Background="{StaticResource TestBlue}"
When I Snoop the control it declares the Background as some other value:
It also doesn't work if I make it an entry in the itself (like the ViewModelLocator). It does work if I place it in the Window.Resources. Any ideas what's going on here?
You need to provide value for Alpha (transparency) channel, because Color is structure and it's default value for that value is zero (transparent).
<Color A="255" R="0" G="86" B="45"/>
To use it as a StaticResource, you must include your ResourceDictionnary where you want to use your Resource. Otherwise, you'll have to use it as a DynamicResource.
So if your button is in its own xaml file, you'll have to include your ResourceDictionnary in the button's xaml file, as you did in App.xaml.
I am creating a class library project that will contain WPF user controls. My Requirement is that all controls have the same style. My project looks like:
Things I have done in order to solve this problem:
Added all references needed by a WPF application System.Xaml, WindowsBase, etc.. so that I can have wpf controls in my class library project.
In AssemblyInfo.cs I have added:
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
Added ResourceDictionary1.xaml To the project adding the style.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Brush1" Color="#FF19199E"/>
</ResourceDictionary>
Now if I want to use a style on my UserControl1.xaml I do:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid >
<Rectangle Fill="{StaticResource Brush1}" />
</Grid>
I know it works great but here is the catch. Every time I create a new userControl I will have to merge the dictionary. On a regular WPF application I could just merge the dictionary once on App.xaml and I will be able to use that dictionary on my entire application. How can I avoid having to merge the dictionary every time I create a new userControl? If I plan on addying a new resource dictionary I will have to go to all userControls and merge another dictionary. Perhaps I wrote the question title incorrectly and my question should have been how can I add a App.xaml file to a class library project
You should replace the source value ResourceDictionary1.xaml like the follow:
Source="pack://application:,,,/ControlsDLL;component/ResourceDictionary1.xaml">
or just simple as following:
<UserControl.Resources>
<ResourceDictionary Source="pack://application:,,,/ControlsDLL;component/ResourceDictionary1.xaml"></ResourceDictionary>
</UserControl.Resources>
I have a project in my application where I keep all of my Resources & reusable custom control classes (including styles, brushes, custom controls, etc.).
The App.xaml file of my Application's "Main" project references the ResourceLibrary file that references all my organized ResourceDictionary files like so:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ControlResources;component/ResourceDictionaries/ResourceLibrary.xaml"/>
<ResourceDictionary Source="Simple Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Window}">
<Setter Property="FontFamily" Value="Segoe UI" />
</Style>
<Style TargetType="{x:Type Rectangle}" />
</ResourceDictionary>
</Application.Resources>
Note: I am following this article here that includes a big-fix (the Rectangle style included above).
Below is a snapshot of what my "ControlResources" project looks like.
Referencing the style and customControl resources works great when I reference them from my main project, "GlenwareMaster", but when referencing the brush resources under "Brushes" from any of the style resources, the application is clearly not finding them.
My question: Can I just add a project-self-referencing link (an inherent right-click blend feature?). How can I get the style ResourceDictionaries to locate the Brush ResourceDictionaries in the same project?
Thanks in advance!
How are you referencing the brush resource dictionaries in your style dictionaries? Make sure you're using pack URIs and that they are correct.
My standard practice is to use the absolute pack uri format in any resource dictionaries that I plan to reference within another resource dictionary- leads to less errors if you end up moving it to another project in the future. When you reference brushes in your common style resource dictionaries, Try something like:
<ResourceDictionary Source="pack://application:,,,/ControlResources;component/ResourceDictionaries/Brushes/MyBrushes.xaml"/>
You can read more about Pack URIs here: http://msdn.microsoft.com/en-us/library/aa970069.aspx
I have defined two resource dictionaries for controls which inherit from TextBox within my usercontrol like so:
<UserControl.Resources>
<ResourceDictionary Source="KeyBox.xaml" x:Key="KeyBox" ></ResourceDictionary>
<ResourceDictionary Source="kTextBox.xaml" x:Key="kTextBox" ></ResourceDictionary>
</UserControl.Resources>
I have created the styles in separate files with different target type:
<Style TargetType="b:kTextBox" >
Now when I create the controls using these resources only the style applied last actually gets applied, so if I remove the second the first works. There's something I am missing here to make use of two resources within the same usercontrol and I can't figure out what it is. Any ideas much appreciated.
Try adding your resource dictionaries like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="KeyBox.xaml" x:Key="KeyBox" ></ResourceDictionary>
<ResourceDictionary Source="kTextBox.xaml" x:Key="kTextBox" ></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Note that unless you have a class named kTextBox, your Style wont work.
It's also worth mentioning that your control will only have one style applied at a time, and the last resource added to the dictionary will be applied. Therefore if you have a style defined in both of your dictionaries with the same key, the one from kTextBox.xaml will be applied.