Change Style Values of ResourceDictionary at RunTime - wpf

I have defined a Font style common for all controls in a ResourceDictionary.
Now, the requirement is if user sets any other different Font from some application level configuration then all Controls should get that new Font properties/style.
I know ResourceDictionary can't have DataContext. But there can be hundreds of Fonts(so different Themes not possible), I am thinking if is there any way I can bind Font properties of ResourceDictionary to a Property which will take that new Font at runtime.
So Is there any other way to satisfy this in a Generic way.

You can set new values for app.resources via code like this
app.Resources[name] = new FontFamily(value);
where app is your App (obviously) and can be set with this when your changing the Resource directly in the App.xaml.cs
name is your resource key as string, which could be e.g. "fontFamily"
<FontFamily x:Key="fontFamily">TheSansOsF</FontFamily>
You have to set the bindings for your font to dynamic resource, otherwise changing the font won´t work "on the fly"
<Setter Property="Label.FontFamily" Value="{DynamicResource fontFamily}"></Setter>
EDIT: Furthermore when you try to use e.g. double as a resource you can use resources like this
<sys:Double x:Key="ctrlWidth">50</sys:Double>
and adding the sys to the application tag
<Application x:Class="Presenter.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

Related

Place style inside Custom Control

I'm creating the custom control. And suddenly faced a question: why to put control's style separately from the control?
I mean that using the standard way you must: derive from base control (for example, from TextBox) and add a style for it in general.xaml.
But why can't we do just like this:
<TextBox x:Class="CustomTest.CoolTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox.Style>
<Style>
<Setter Property="TextBox.FontSize" Value="20" />
</Style>
</TextBox.Style>
</TextBox>
And code-behind:
public partial class CoolTextBox : TextBox
{
public CoolTextBox()
{
InitializeComponent();
}
}
Update #1
I'm not writing my own library but creating a custom control inside my main executable. Application support themes but they differ only by colors. So each theme is a set of brush resources and my style will refer them using DynamicResource.
What I want to know is the drawbacks of that solution. I mean performance, simplicity of usage and etc.
WPF allows changing themes at runtime, means the style shall be stored separatly from the controls. Futhermore the control shall be lookless when designining in order to other programmers to have their custom styles though somewhere there should be a default style which must be stored separatly in a Generic.xaml file. If your app doesn't support changing themes then you can define the style wherever you wish.
If you are writing a library of custom controls I suggest you to stick to standards.
Here is a link how shall a custom controls be created:
http://wpftutorial.net/HowToCreateACustomControl.html
In addition to dev hedgehog's answer about performance, I found the following drawbacks:
Style object is own for each instance of control. So you end up with number of clones of the same Style object.
You cannot override style using BasedOn property. Only completely replace is possible.

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.

Headaches with configuring themed resources from c# (Windows Phone)

I have Windows Phone App and I want to have a different themed resources based on whether the user has a dark or light theme and which accent color they have chosen.
In my app initialize code I detect the users theme and accent color then load the appropriate Resource Dictionary, Dark, Light etc. I add the Resource Dictionary to the App.Current.Resources.MergedDictionaries collection.
The problem comes when I want to reference a value in the (dynamically loaded) from my App.Xaml. In the below example the key is "DefaultBackgroundImageOpacity". For some reason the values in the ResourceDictionary that I load into the MergedDictionaries collection never get found when the App.Xaml parsing happens. I've played with loading the resources before I call InitializeComponent() and after. Neither seem to resolve the issue.
Any ideas?
// Simplified version of adding some xaml...
// Note I'm loading the key "DefaultBackgroundImageOpacity"
this.Resources.MergedDictionaries.Clear();
var myTestXaml = "<ResourceDictionary xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:System='clr-namespace:System;assembly=mscorlib'> <System:Double x:Key='DefaultBackgroundImageOpacity'>0.2</System:Double></ResourceDictionary>";
this.Resources.MergedDictionaries.Add((ResourceDictionary)XamlReader.Load(myTestXaml));
// This always fails saying that the key "DefaultBackgroundImageOpacity"
// can not be foudnd - even though it was loaded just above
InitializeComponent();
// Phone-specific initialization
InitializePhoneApplication();
I suspect your App.Xaml contains something like this:-
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/somedictionary.xaml" />
...
</ResourceDictionary.MergedDictionaries>
...
</ResourceDictionary>
</Application.Resources>
If that is so then InitializeComponent will replace the default ResourceDictionary which you have manipulated in your code hence your changes to MergeDictionaries will not be present since that instance of a ResourceDictionary is no longer referenced.
If you are not setting up any MergedDictionaries in the App.Xaml then make sure you add resources directly to Applicaiton.Resources and are not creating an new instance of ResourceDictionary in the xaml.

WPF custom View without Generic.xaml

I am trying to implement custom views that are specific to an application without any luck. Here is my problem:
I need a custom view because I would like for the user to be able to switch views dynamically at runtime. I need a custom view (as opposed to only datatemplates) because the listview layout has to change as well as the Control template and the data template.
All of the turorials say to implement Custom classes that derive from viewbase and override the DefaultStyleKey and ItemContainerDefaultStyleKey to return a ComponentResourceKey defined in generic.xaml. However, the problem is that I am trying to create several views that are very specific to that application. certain brushes and fonts will be consistant accross the application and the custom views will use these. i.e. I have application level Forebrush, Shadowbrush, Deepshadowbrush, TextDecorator, etc. and I want the views to use these. If the view will be defined in an external generic.xaml it will be very convoluted markup to bind to these. And besides, it would make them application specific anyway (if they bind to these brushes).
Anyone have an idea how to define styles for views internaly in the application that will be able to be changed at runtime?
I'm slightly confused on your details, however you can set the Style of a ListView at runtime as such...where CustomStyle is a predefined style that you want to apply to the ListView.
ListView view = new ListView();
view.Style = CustomStyle;
The DefaultStyleKey is applicable to a custom Control (this is different then a UserControl). So say you want a new Control called a Widget. You would need ot define the DefaultStyleKey for that Widget since it does not have a default style defined. A UserControl is a collection of Controls, therefore it does not have a pre-defined style as such.
In addition you can create a ResourceDictionary to break apart your styles. You can then merge them via the App.xaml as such...
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml"/>
<ResourceDictionary Source="Themes/ListViewStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

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