Override Overridden WPF Theme - wpf

I am writing a WPF application on WinXP and I have overridden the default theme with the vista theme like this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var themerd = new ResourceDictionary();
themerd.Source = new Uri(#"PresentationFramework.Aero;V3.0.0.0;31bf3856ad364e35;component\themes/aero.normalcolor.xaml", UriKind.Relative);
Resources.MergedDictionaries.Add(themerd);
}
And it works fine mostly. When I use control such as a button:
<Button />
The style looks fine, but if I use a Button with a different style like this:
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Width" Value="80" />
</Style>
</Button.Style>
</Button>
The style will override the specified theme style with the standard WinXP style instead of building on top of it. This is extremely limiting for me. Is there a way to avoid this issue?

Why this is happening
The default BasedOn= for a style is generated using only the current theme's resource dictionary. The technique you show for overriding a theme doesn't actually change the theme dictionary in use: It simply adds the resources from the theme's resource dictionary to the application's resource dictionary. Since the current theme is unchanged, the default BasedOn is also unchanged.
How to solve it
Option 1: Locally override the theme by intercepting calls to uxtheme.dll!GetCurrentThemeName at the Win32 level. This is quite complex but works for all your styles with no changes to your XAML.
Option 2: Set BasedOn using a custom MarkupExtension. It would look like this:
<Style TargetType="Button" BasedOn="{DefaultAeroStyle Button}"> ...
Your custom MarkupExtension would load the Aero theme dictionary on first use and store it in a static field. Its constructor would take a Type, and its ProvideValue would look up the type in the dictionary to find the style.
Option 3: Set BasedOn to an intermediate named style. It would look like this:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="... theme path ..." />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="ThemeButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
<Style x:Key="ThemeListBoxStyle" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}" />
...
</ResourceDictionary>
</Application.Resources>
</Application>
Now in your lower-level dictionary you can say:
<Style TargetType="Button" BasedOn="{StaticResource ThemeButtonStyle}" />
Option 4: Set BasedOn using a static property and the x:Static markup extension

Related

Using BasedOn Style with static ResourceDictionary in different Assembly

Background
I have multiple projects in my solution and in one project I've made a static class containing a merged ResourceDictionary, which I use in all my other projects:
namespace GUI.Shared.Resources
{
public static class ResourceFactory
{
public static ResourceDictionary _resources = null;
public static ResourceDictionary Resources
{
get
{
if (_resources == null)
{
_resources = new ResourceDictionary();
_resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("pack://application:,,,/GUI.Shared;component/Resources/VectorIcons.xaml") });
_resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("pack://application:,,,/GUI.Shared;component/Resources/ButtonStyles.xaml") });
}
return _resources;
}
}
}
}
With my styles defined in the xaml files ButtonStyles.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="SpecialButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red"/>
</Style>
</ResourceDictionary>
Normally I use the style like this:
<UserControl x:Class="GUI.Modules.TextControl.Views.TextControlView"
x:Name="TextControl"
xmlns:resources="clr-namespace:GUI.Shared.Resources;assembly=GUI.Shared">
<Grid>
<Button Style="{Binding Path=[SpecialButtonStyle], Source={x:Static resources:ResourceFactory.Resources}}">
</Grid>
</UserControl>
Problem
Now I want to extend my style locally in a View. I found out that you do this by using the BasedOn property.
<Button>
<Button.Style>
<Style TargetType="Button" BasedOn="{Binding Path=[SpecialButtonStyle], Source={x:Static resources:ResourceFactory.Resources}}">
</Style>
</Button.Style>
</Button>
Only my binding method is not allowed, giving the error:
A Binding cannot be set on the BasedOn property of type Style. A Binding can only be set on a DependencyProperty of a DependencyObject.
How can I use this style from my ResourceFactory.Resources as a BasedOn style?
The BasedOn property is a regular CLR property, not a dependency property, which means you cannot bind it. Furthermore the indexer syntax among other binding path constructs are only available in the Binding markup extension, not in x:Static, StaticResource or DynamicResource.
You can create your own markup extension based on the x:Static implementation, but that is very complex and the wrong thing to do. Instead, you should think about following a different built-in approach to your issue.
If you want to share resources, why create a static ResourceFactory at all? WPF already supports resource provision with the App.xaml resource dictionary and scoped resources within each FrameworkElement through the Resources property. Create a resource dictionary for your shared resources e.g. like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/GUI.Shared;component/Resources/VectorIcons.xaml"/>
<ResourceDictionary Source="pack://application:,,,/GUI.Shared;component/Resources/ButtonStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
As you can see, this is the same as your ResourceFactory, but easier to use. Include this dictionary into the application resources or any other resources section of any project using a pack URI e.g.:
<UserControl x:Class="GUI.Modules.TextControl.Views.TextControlView"
x:Name="TextControl"
xmlns:resources="clr-namespace:GUI.Shared.Resources;assembly=GUI.Shared">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/GUI.Shared;component/Resources/SharedResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource SpecialButtonStyle}">
<!-- ...your style definition. -->
</Style>
</Button.Style>
</Button>
</Grid>
</UserControl>
There is no need for Binding here and it is much easier to access resources this way. You can access the resources in child controls, too, because access to resources is scoped to where they are defined.

How to extend xamoutlookbar theme in wpf?

ResourceDictionary includes following markup.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:flTheme1="Fluent;component/Themes/Office2010"
xmlns:Fluent="clr-namespace:Fluent;assembly=Fluent"
xmlns:igDP="http://infragistics.com/DataPresenter"
xmlns:igEditors="http://infragistics.com/Editors"
xmlns:igOB="http://infragistics.com/OutlookBar"
xmlns:igOutlookBar="http://infragistics.com/OutlookBar"
xmlns:igThemes="http://infragistics.com/Themes"
xmlns:igThemes3="clr-namespace:Infragistics.Windows.Themes;assembly=InfragisticsWPF4.OutlookBar.v14.1"
xmlns:igThemes4="clr-namespace:Infragistics.Windows.Themes;assembly=InfragisticsWPF4.DataPresenter.v14.1"
xmlns:igThemes5="clr-namespace:Infragistics.Windows.Themes;assembly=InfragisticsWPF4.DockManager.v14.1"
xmlns:igWindows="http://infragistics.com/Windows"
xmlns:local="clr-namespace:Dwm">
<Style TargetType="{x:Type igOB:OutlookBarGroup}" BasedOn="{StaticResource ResourceKey=igThemes3}" >
<style>
</ResourceDictionary>
How to extend XamOutlookBar Theme so that I can Override style of OutlookBarGroup and apply my style on it to change its background.
Why don't you just set the Background property of your groups:
<igOB:XamOutlookBar>
<igOB:OutlookBarGroup Background="{StaticResource YourBrushResource}"/>
</igOB:XamOutlookBar>
If you'd like to create an implicit style, I'd still suggest you just override the Background property:
<Style TargetType="igOB:OutlookBarGroup" BasedOn="{StaticResource {x:Type igOB:OutlookBarGroup}}">
<Setter Property="Background" Value="{StaticResource YourBrushResource}"/>
</Style>
And finally, if you want to modify the control template, you could extract it using Visual Studio as answered in How to Extract Default Control Template In Visual Studio?

Defining WPF Window style in class library

Currently I am developing some WPF class library, which will have several WPF Windows and trying to create my own window ControlTemplate for these Windows to make more nice design of these windows (inspired by this article: Reusing Control Templates in Resource Dictionaries).
Problem is, then it's a WPF class library not an application assembly, where i can use app.xaml and to define my resource dictionary reference & etc...
Using code below i getting an error: StaticResource reference 'MyWindowStyle' was not found
<Window x:Class="SomeERP.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Style="{StaticResource MyWindowStyle}">
<Window.Resources>
<!-- My Window Style -->
<Style x:Key="MyWindowStyle" TargetType="Window">
<Setter Property="Background" Value="Transparent" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
<Setter Property="Opacity" Value="0.95" />
<Setter Property="Template" Value="{StaticResource MyWindowTemplate}" />
</Style>
<!-- Window Template -->
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type Window}">
<Grid>
</Grid>
</ControlTemplate>
</Window.Resources>
</Window>
I suspect i get this error because in my case it's not predeclared before Window declaration as in application's case in app.xaml which i don't have in class libary. I am pretty new in WPF and just starting to use WPF design possibilities.
If you need the style only once, the solution is pretty simple: Just define the style in-place
<Window.Style>
<!-- My Window Style -->
<Style TargetType="Window">
...
</Style>
</Window.Style>
However, if you need the style in more than one window, it is reasonable to define the style in a resource dictionary. Then you can integrate the resource dictionary in the window's resources and set the style accordingly:
<Window.Resources>
<!-- My Window Style -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
<!-- path to the resource dictionary -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resource>
<Window.Style>
<StaticResource ResourceKey="MyWindowStyle"/>
</Window.Style>
Looks like i've found solution to my problem in this post: Assembly-wide / root-level styles in WPF class library and according to this post What's the difference between StaticResource and DynamicResource in WPF?
A StaticResource will be resolved and assigned to the property during the loading of the XAML which occurs before the application is actually run. It will only be assigned once and any changes to resource dictionary ignored.
A DynamicResource assigns an Expression object to the property during loading but does not actually lookup the resource until runtime when the Expression object is asked for the value. This defers looking up the resource until it is needed at runtime. A good example would be a forward reference to a resource defined later on in the XAML. Another example is a resource that will not even exist until runtime. It will update the target if the source resource dictionary is changed.
That was what i needed, as i have class library, i don't have app.xaml and can't predeclare resources for Window. I just need to use DynamicResource rather than StaticResource to get it working.
Thanks for attention :)
Just add the external assembly:
<Application.Resources>
<ResourceDictionary>
Source="/MyAssemblyName;component/MyResources.xaml"
</ResourceDictionary>
</Application.Resources>

Derived classes and theme inheritance in WPF?

I'm building a WPF application, and I derived a bunch of controls from the standard WPF control types -- textblocks, buttons, etc. I tried adding a resource dictionary to app.xaml to set the theme, but my custom controls don't seem to be respecting it. (For example, standard Buttons take the Aero theme just fine, but a myButton derived from a Button is still lookless.) Is there a way I can set the theme for my derived controls to be the same as for the base controls?
EDIT: I should note that these custom controls are instantiated at runtime, so I can't manipulate their properties directly via XAML. I can change particular properties like background color by using a Setter in the application resource dictionary, but haven't found a way to set a theme using that technique.
If you have this style in a Resource Dictionary called Dictionary1.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MyButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="75" />
<Setter Property="Height" Value="23" />
</Style>
</ResourceDictionary>
Then you can set it on any button with this code behind
Uri resourceLocater = new Uri("/YourAssemblyName;component/Dictionary1.xaml", System.UriKind.Relative);
ResourceDictionary resourceDictionary = (ResourceDictionary)Application.LoadComponent(resourceLocater);
Style myButtonStyle = resourceDictionary["MyButtonStyle"] as Style;
Button button = new Button();
button.Style = myButtonStyle;

WPF global font size

I'm creating a WPF app and I would like to know the best way to be able to change the font size for every element in the ui. Do I create a resource dictionary and set Styles to set the font size for all the controls I use?
What is the best practice?
I'd do it this way:
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="baseStyle">
<Setter Property="FontSize" Value="100" />
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource baseStyle}"></Style>
<!-- ComboBox, RadioButton, CheckBox, etc... -->
</Window.Resources>
That way, if I want to change ALL the controls, I'd just have to change the "baseStyle" style, the rest would just inherit from it. (That's what BasedOn property those, you can also extend the base style if you create other setters inside of the inherited style)
FontSizeProperty is inherited from Parent Control. So you just need to change FontSize of your main window.
If you don't need dynamic behaviour this should work:
Add a style for Window to your ResourceDictionary
<Style TargetType="{x:Type Window}">
<Setter Property="FontSize" Value="15" />
</Style>
Apply the style to your main form (will not be applied implicit because its a derived type)
Style = (Style)FindResource(typeof (Window));
<Window> has a property FontSize.
So you can set desired fontsize in element if you want to change the fontsize in all the elements within that window.
<Window FontSize="12">
</Window>
Another option is to define the FontFamily and FontSize as resources.
<FontFamily x:Key="BaseFontFamily">Calibri</FontFamily>
<sys:Double x:Key="BaseFontSize">12</sys:Double>
That way you can use them in your setters.
Application.Current.MainWindow.FontSize = _appBodyFontSize;
This way you can change the Font Size at run time also.
TextElement.FontSize is an inherit property, which means you can simply set the font size at root element, and all the children elements will use that size (as long as you don't change them manually)
For any styles in WPF, you should have a separate resource dictionary that contains the styles for your app.
If you want to have a single Font Size that's reused throughout the app then just create a style for that font size. You can either give it a unique name/key to use explicitly or you can set a targetType that will transcend throughout the app.
Explicit Key:
<Style
x:Key="MyFontSize"
TargetType="TextBlock">
<Setter
Property="FontSize"
Value="10" />
</Style>
<Control
Style="{StaticResource MyFontSize}" />
*Note this style can be used with controls that have contentPresenters
For all textblocks in the app:
<Style
TargetType="TextBlock">
<Setter
Property="FontSize"
Value="10" />
</Style>
<TextBlock
Text="This text will be size 10" />
If you need to programmatically change global FontSize, not statically (XAML), to be applied once for all your windows, you can do:
TextElement.FontSizeProperty.OverrideMetadata(
typeof(TextElement),
new FrameworkPropertyMetadata(16.0));
TextBlock.FontSizeProperty.OverrideMetadata(
typeof(TextBlock),
new FrameworkPropertyMetadata(16.0));
This values are applied to any TextBlock, Labels and almost any text in any windows, whereas it has not a explicit FontSize defined. But this does not affect for TextBox, you have to write a similar code for it or any other special controls.
To dynamically change the font size globally with ctrl-mousewheel:
XAML:
<Window Name="MainWindow" ... PreviewMouseWheel="MainWindow_PreviewMouseWheel">
code behind:
private void MainWindow_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
{
if (e.Delta > 0)
++mainCtrl.FontSize;
if (e.Delta < 0 && mainCtrl.FontSize > 1)
--mainCtrl.FontSize;
}
}
Using Resources in XAML is the way to go. Although there are many great answers to this question, I would like to add my two cents to the SCOPE of the Resource.
For Global accessibility in all of the Windows and User Controls of the Project, you can have your resource in the App.xaml file
<Application.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Application.Resources>
For accessibility at a Window level, you can have your resource in your xaml file for Window
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</Window.Resources>
You could even have it at a Control level, for example
<DockPanel.Resources>
<Style TargetType="{x:Type Control}" x:Key="GlobalFontSize">
<Setter Property="FontSize" Value="28"/>
</Style>
</DockPanel.Resources>
Let's have some BLACK MAGIC things:
Add a double resource into your Application resource
<Application.Resources>
<sys:Double xmlns:sys="clr-namespace:System;assembly=mscorlib" x:Key="GlobalFontSize">12</sys:Double>
</Application.Resources>
Add a static property in your App class
public static double GlobalFontSize
{
get => (double)Current.Resources["GlobalFontSize"];
set => Current.Resources["GlobalFontSize"] = value;
}
Use this resource any where you want by DynamicResource
FontSize="{DynamicResource GlobalFontSize}"
Access property App.GlobalFontSize in any way to change value, binding is okay!
App.GlobalFontSize = 20;
//Or
{Binding Path=(local:App.GlobalFontSize)}

Resources