Design templates in a class library at top scope - wpf

I use a class library (WPF user control library) to host some user controls which other (C#-) applications in the project solution consume. I want these controls to use XAML ControlTemplates residing at the top scope of the class library. The ControlTemplates do not have to be consumed outside the class library.
Here a template declaration:
<ControlTemplate TargetType="{x:Type Button}" x:Key="TemplateImageButtonSmall">
<Grid>
<Image Name="img" Source="/PSCommonUI;component/Images/Buttons/ButtonMinus_normal.png"/>
</Grid>
</ControlTemplate>
Then I have a user control in the class library, containing:
<Button Height="57" Margin="10,0,6,5" Name="button3" Template="{StaticResource TemplateImageButtonSmall}" Width="82">
In an application, I can use the App.xaml file for defining the templates. However, in a class library I don't have this option.
I have searched the web and found some answers including the use of a generic.xaml file, ComponentResourceKey, merging resource files and other stuff I find exaggeratedly complicated.
Also I read that theme definitions (resources in general) shouldn't reside in a class library.
But if I need some themes ONLY in this class library for the there hosted controls, how is best practice then?
Thanks in advance,
Julian

I am not sure what you meant, however, if you want child UIElements from a specific UIElement and below to use control templates, then you can define the templates in a resource dictionary and merge the dictionary into the top control that you want the dictionary to be visible to.
Edit:
The assembly just contains the classes and resources within it. It has no events of its own (e.g. OnApplicationLoaded).
A control's XAML can contain resources of its own (e.g. control templates) for consumption by itself and child controls and thus define default styling.
Your application can merge the resource dictionaries into any level of the tree (application, window, control, ...) and thus override defaults.
If you want the styling to be dynamic (overrable by importing resource dictionaries) then using the DynamicResource keyword your XAML. If your resource is defined in the same XAML and can not be overridden then use the StaticResource keyword.

Add a resource dictionary to your class library and define your resources (templates) there. It doesn't have to be generic.xaml.
Then in each user control or other .xaml file reference the resource dictionaries you require using Xaml similar to:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="... path to dictionary 1"/>
<ResourceDictionary Source="... path to dictionary 2"/>
<ResourceDictionary Source="... etc"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
You can then use resource keys from the merged dictionaries.

Related

Override Control Style

I have created a CustomControlLibrary.dll, that contains a Control (MyControl), with its style in the Generic.xaml of the Themes folder, as per the default automatic setup.
If I want to include that dll in 2 different projects, each of which providing a custom style for "MyControl" to give it a different look, where do I put those custom styles?
I thought I just had to have a Themes\Generic.xaml for each application, that defines the custom style, but having done this, it still ends up using the style defined in CustomControlLibrary.dll
You have CustomControlLibrary.dll which is the owner of MyControl. Inside that dll you have your themes defined. Any other project that may contain your dll doesnt haven to have themes defined.
Other projects may define their custom style for MyControl in their window resource for example.
Styles may be defined at any level. :)
Check this link out:
http://msdn.microsoft.com/en-us/library/ms745683.aspx
Solution will be to define specific style for each project and apply it correspondingly.
But keep in mind that you can create proper style "inheritance" by using BasedOn property. After all you should get a set of styles that you'll manage in MergedDictionaries:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/CustomControlLibrary;component/styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

Style WPF normal Control depending on Windows Theme

I have read lots of thing about WPF Theme, Skin, Style etc... But there is still something I cannot achieve.
I have custom controls, which are styled depending on the OS theme, by having a different style in each of the theme file (Aero.NormalColor.xaml, Luna.NormalColor.xaml or Aero2.NormalColor.xaml), this work like a charm.
I don't load/force any theme in my App.xaml, each controls (like buttons) keep there style depending on the OS theme.
So I see XP buttons on XP, Win7 buttons on windows 7 and Win8 buttons on Windows 8.
I also have ResourceDictionaries which are loaded in the App.xaml that contains "named" (explicit x:Key) styles for different normal wpf controls. They look like this:
<Style x:Key="BlackComboBox" TargetType="{x:Type ComboBox}"></Style>
and I use them like this
<ComboBox Style="{StaticResource BlackComboBox}"></ComboBox>
So for now, my BlackComboBox is the same on every Windows (XP/7/8).
What I try to achieve is to have a different Style for these normal Controls depending on the OS theme, without having to subclass the Control (I think it will be overkill to have a subclass for each control that will need an OS specific them), so BlackComboBox could be different on each OS.
I have already tried to put a style with the same key in a theme file, but this doesn't seem to work.
I have thought about loading at runtime a different ResourceDictionary containing the style for the desired OS version:
But it looks like an ugly solution.
I don't like having to check for System.Environment.OSVersion.
And it will not be theme dependant, but OS dependent.
For me the best way seems to be able to have "named" style in a Theme file that kind of overrides the one in the ResourceDictionaries.
Thanks for the help!
I believe the only way to do this would be to create resource dictionaries for each theme the same you would if you created a custom control and wanted to have a different look for each theme. Then you would create a Style in each for the ComboBox and provide a ResourceKey derived class (e.g. ComponentResourceKey) as the x:Key for the Style using the same value for the x:Key in each theme's resource dictionary. Then when you reference the Style you would use a DynamicResource to that ResourceKey.
So a simplified example would be to create a new WpfApplication (e.g. I named its WpfResourceKeys). In this case I'm going to put the theme resource dictionaries in the main assembly so I go into the AssemblyInfo.cs and set the ThemeInfo's 1st parameter (i.e. the themeDictionaryLocation) to SourceAssembly.
Then create a folder named "themes" and in it create a resource dictionary for each theme you want to support.E.g. aero.normalcolor.xaml, aero2.normalcolor.xaml, luna.normalcolor.xaml, classic.xaml, etc..
In each ResourceDictionary define a Style for ComboBox or whatever control you want and give it an x:Key of the same ResourceKey. The easiest thing to use is ComponentResourceKey. In my case I'll use a TextBox since I'll just set the Background and that will be honored regardless of the template defined for each theme. E.g.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfResourceKeys"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
x:Key="{ComponentResourceKey
ResourceId=Foo,
TypeInTargetAssembly={x:Type local:MainWindow}}">
<Setter Property="Background" Value="Purple" />
</Style>
</ResourceDictionary>
In my case I just put this into each theme xaml file but with a different value for the Background setter to test it out. So in my aero2.normalcolor.xaml the setter value was Purple and in the classic.xaml the setter value was Orange. When I run my test in Windows 8 with the default theme the TextBox is purple but if I switch to one of the high contrast themes the TextBox is Orange.
Then in the place you are going to reference it you would use a DynamicResource instead of a StaticResource since you won't be defining the Style within the resources of the window or app.xaml (because you want the framework to locate it considering the OS theme).
<Window x:Class="WpfResourceKeys.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfResourceKeys"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Style="{DynamicResource ResourceKey={ComponentResourceKey
ResourceId=Foo,
TypeInTargetAssembly={x:Type local:MainWindow}}}" Text="ABC" />
</Grid>
You just need to make sure you use an equivalent resource key to how you define it in the theme dictionaries. In the case of ComponentResourceKey that means the ResourceId and TypeInTargetAssembly are equivalent.

How can data templates in generic.xaml get applied automatically (redux)?

This is a follow-up to a question posted by Thiado de Arruda. In short, he wanted to have a DataTemplate in his generic.xaml file, but the template wasn't being applied.
The answer given suggested placing the DataTemplate in the ControlTemplate.Resources for the control that hosted his custom type. This works very well, however, suppose that he needed the DataTemplate to apply in other places, not just within the host control. Would it be necessary to copy the DataTemplate to the ControlTemplates of every other host control?
Edit (restating question):
I am developing a WPF application using MVVM design principles. MainWindow.xaml contains the structure of the UI, and all of the styling is coded in Themes\generic.xaml. (The behavior is coded in a separate view model class, but that's irrelevant.) As part of the UI, I created a subclass of ListBox (MyListBoxSubClass) to display a collection of an ordinary .Net object of my own creation (MyObject). MyListBoxSubClass has a style in generic.xaml that redefines the Template property, and it gets applied as expected. I also have a DataTemplate for MyObject in generic.xaml, but this does not get applied. According to the above link, I have to place the DataTemplate in the Resources collection of the ControlTemplate for MyListBoxSubClass in order for this DataTemplate to be applied. This works wonderfully.
My question is how to get the DataTemplate to apply to MyObject everywhere in my application without having to duplicate the DataTemplate? I've tried adding a key to the DataTemplate and referencing it where I need it, but for some reason, I get a XAML parse error at runtime, and Resharper says that it can't resolve my DataTemplate key.
Add the data template in a separate resource dictionary in another XAML file.
Bring the XAML file into your generic.xaml control template resources:
<ControlTemplate ...>
<ControlTemplate.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="wherever.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ControlTemplate.Resources>
</ControlTemplate>
Then wherever else you want to use this data template, you can bring it into merged dictionary of resources of wherever you want - user control, window, another control template, etc...
<Window x:Name="someWindow">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="wherever.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>
Hope this helps.

Can I merge a resource XAML dictionary in a single page's resources?

My application has a single page with an explicit dark background (dark image), but the rest of the application uses the system colors. Is there a way to use the merged dictionary technique outlined here, but only for a single page, in order to not have to explicitly set the colors (and styles for TextBox controls, etc) on each control one by one?
Thanks.
The technique you have linked to is not limited to the App.xaml. It can be used in any definition of a ResourceDictionary. Everywhere you see a Resources property an implicit ResourceDictionary is created for you when it is accessed. However in all these places you can also explicitly define one. This will allow you to also manipulate its MergedDictionaries property.
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<ResourceDictionary>
<ResourceDicitonary.MergedDictionaries>
<ResourceDictionary Source="urlToAnotherXamlFile" />
</ResourceDicitonary.MergedDictionaries>
</ResourceDictionary>
</Grid.Resources>
.... Content ....
</Grid>

WPF: Accessing resources in a control assembly

I have a control for which i want to declare resources in a xaml file. if this was a user control i could put the resources in an <UserControl.Resources> block and reference them in the code via this.Resources["myResourceKey"] how do i achieve the same functionality in a control. at the moment the only link to xaml i have is through the controls static constructor, to reference the style (and control template)
static SlimlineSimpleFieldTextBlock() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimlineSimpleFieldTextBlock), new FrameworkPropertyMetadata(typeof(SlimlineSimpleFieldTextBlock)));
}
but even if i add a block to the xaml <Style.Resources> I dont seem able to reference them (as the Style is null at the OnApplyTemplate stage) and even if i did it would mean if someone eles overrode the style i would lose my resources.
Construct your resource key using ComponentResourceKey. Normal resource keys are searched for only up the visual tree and in your application resources. But any resource key that is a ComponentResourceKey is also searched for in the theme dictionary for the assembly containing the type. (This is also true for Type objects used as resource keys.)
In your Themes/Generic.xaml of the assembly containing a control called "Sandwich" you might have:
<SolidColorBrush x:Key="{ComponentResourceKey local:Sandwich, Lettuce}"
Color="#00FF00" />
<ControlTemplate x:Key="{ComponentResourceKey local:Sandwich, PeanutButter}" ...>
...
</ControlTemplate>
You can reference these resources in code like this:
var lettuce = (Brush)FindResource(
new ComponentResourceKey(typeof(Sandwich), "Lettuce"));
var penutButter = (ControlTemplate)FindResource(
new ComponentResourceKey(typeof(Sandwich), "PeanutButter"));
You can also refer to these resources in XAML like this:
<Border Background="{StaticResource ResourceKey={ComponentResourceKey local:Sandwich, Lettuce}}" />
Both of these forms of reference work from anywhere that FindResource can be used, which is inside the code or XAML for any object derived from FrameworkElement, FrameworkContentElement or Application.
Additional notes
The search algorithm for a ComponentResourceKey resource involves only the assembly contaning the specified type, not the type itself. Thus a control of type Soup could use a ComponentResourceKey of {ComponentResourceKey local:Sandwich,Seasonings} if the Soup and Sandwich classes were in the same assembly. As long as everything about the ComponentResourceKey matches exactly and the resource is actually in the same assembly as the given type, the resource will be found.
Also note that although it is possible to use pack URI to load a ResourceDictionary from another assembly, it is a bad idea to do so. Unlike the Themes/Generic.xaml solution you actually have to modify the application using your controls, and it also suffers from multiple-inclusion and overridability problems.
Whenever you are using Themes/Generic.xaml you must have your ThemeInfoAttribute set correctly on that assembly. You can start with this in your control library's AssemblyInfo.cs:
[assembly:ThemeInfoAttribute(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]

Resources