Overrides control resource with setter in style file - c#

I have the code to override a CalendarDatePicker resource (CalendarDatePickerCalendarGlyphForeground) in XAML.
<CalendarDatePicker Style="{StaticResource CalendarDatePickerStyle}">
<CalendarDatePicker.Resources>
<SolidColorBrush x:Key="CalendarDatePickerCalendarGlyphForeground" Color="{StaticResource PrimaryColor}"/>
</CalendarDatePicker.Resources>
</CalendarDatePicker>
Now, I need reuse this code, creating a generic style in my styles file project, to use in the others CalendarDatePicker in my project, like this:
<Style x:Key="CalendarDatePickerStyle" TargetType="CalendarDatePicker">
<Setter ....
</Style>
How should I do that with a Setter?
And if I wanted to apply a generic style to all project calendars without having to be typing in every Calendar Style={StaticResource ...}, how should I define this style?

You can directly add the SolidColorBrush definition into your App.xaml resources and it will override the default styling. The main thing is to use the same Key as the control uses.
So for example to override CalendarDatePickerCalendarGlyphForeground with your own color in every CalendarDatePicker, make the App.xaml look like the following:
<Application
x:Class="App1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
RequestedTheme="Light">
<Application.Resources>
<SolidColorBrush x:Key="CalendarDatePickerCalendarGlyphForeground" Color="Green"/>
</Application.Resources>
Here more info about styling controls in UWP.

Related

Unable to override default style of wpf control in generic.xaml

So we are trying to retemplate some stock wpf controls by changing their default styles in the generic.xaml
When we normally do this we subclass a control and then override the default style key of the subclassed control in its static initializer. However, we are trying to just override the basic control now without subclassing it. That way anyone in the company using the stock wpf control will get our new styling by default.
I can't seem to get this to work though.
In my sandbox application which is a watered down version of our actual problem, I have the following.
MainWindow.xaml
<StackPanel>
<TextBlock>It doesn't work</TextBlock>
<local:CustomTextBlock>It works</local:CustomTextBlock>
</StackPanel>
Themes/Generic.xaml
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="100" />
</Style>
<Style TargetType="{x:Type test:CustomTextBlock}">
<Setter Property="FontSize" Value="100" />
</Style>
CustomTextBlock.cs
public class CustomTextBlock : TextBlock
{
static CustomTextBlock()
{
Type _CustomTextBlock = typeof(CustomTextBlock);
DefaultStyleKeyProperty.OverrideMetadata(
_CustomTextBlock,
new FrameworkPropertyMetadata(_CustomTextBlock));
}
}
Which results in this being displayed.
My theory is that the WPF engine is ignoring our style because the default style key is either A: not overridden or B: is finding their style in their generic.xaml first.
My question is, is there a work around for this? Are my assumptions correct?
UPDATE:
According to reference source, the default style key is overridden in the stock wpf control for TextBlock.cs in this case
Reference Source TextBlock.cs (Line 346)
To accomplish this, you can put your styles either directly into App.xaml or into a separate ResourceDictionary (named DefaultStyles.xaml).
Putting directly into App.xaml is easy enough, just put the style within the Resources element.
If you want to put the styles into a file (this is useful if you want the styles for multiple applications or within multiple assemblies) you add it to the MergedDictionaries of your App.xaml as such
<Application x:Class="MyAwesomeApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/DefaultStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
This assumes that you put the file DefaultStyles into the Themes folder. If it is in another assembly you would do the following:
<ResourceDictionary Source="/Company.Controls.UI;component/DefaultStyles.xaml"/>
Have a look at this post (What is so special about Generic.xaml).
The main issue seems to be:
WPF looks for the default style in a special resource dictionary in the Themes folder in the same assembly as the control.
'Your' control is defined in 'your' assembly, TextBlock is defined in PresentationFramework. So you better create another ResourceDictionary for re-styling standard controls and include/merge it in each of your xaml documents (I suppose, this hurts).
Hope it helps.

WPF Using images in generic.xaml template

I am writing a style for a custom control derived directly from Control. Visual Studio places the style for a "Custom Control (WPF)" in the Themes\generic.xaml file. My style contains an image which I can't get displayed, seems there's something special about how to set the Source for an image from within the generic.xaml file.
I managed to reproduce the issue with a simpler scenario. Create a "WPF Custom Control library" then add a style for buttons like so, in the themes\generic.xaml . Here's my complete generic.xaml:
<ResourceDictionary
...
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
After this I have create a UserControl (in the same project) containing just a button (for the sake of testing out the style) like so:
<UserControl x:Class="BlendControls.UserControl1"
...
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button/>
</UserControl>
I have added the SmallHandle.png in the root project directory, in the themes directory, I have added it also to the good old Resources page, tried changing the build action to resource, embedded resource, tried copying the image manually to the build directory, but to no effect. The image is never displayed.
This must be related to the generic.xaml file, because copying the entire style to the same file where the Button is placed works fine. That is, the following works as expected:
<UserControl x:Class="BlendControls.UserControl1"
...
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button></Button>
</UserControl>
So, how should I set the Source of images from generic.xaml? Or alternatively, where should I put the styles/templates for custom controls?
---- Solution ----
As pointed out by Sheridan, I have to use the "full" pack URI notation as:
pack://application,,,/MyAssembly;components/SmallHandle.png
This looks strange to me, as the image is in the same assembly. Not sure, looks like I am referencing from outside the dll.
There's nothing unusual about accessing an image in Generic.xaml, you're just not referencing it correctly. You can reference a resource file in the project assembly using this format:
<Image Source="/AssemblyName;component/Subfolder/SmallHandle.png" />
If your images are directly inside the project root (which is not recommended), then you can access them like this:
<Image Source="/AssemblyName;component/SmallHandle.png" />
If your images are in a folder in another project, then you can access it like this:
<Image Source="/ReferencedAssembly;component/Subfolder/SmallHandle.png" />
See the Pack URIs in WPF page on MSDN for more information.
UPDATE >>>
In .NET 4, the above Image.Source values would work. However, Microsoft made some horrible changes in .NET 4.5 that broke many different things and so in .NET 4.5, you'd need to use the full pack path like this:
<Image Source="pack://application:,,,/AssemblyName;component/Images/image_to_use.png">
If you don't feel as though your generic.xaml is being picked up, you can reference it from your App.cs.xaml like this:
<App.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MY.NAMESPACE;component/Themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</App.Resources>
Your generic.xaml file should be marked as "Resource".
Also, your image file should be marked as "Resource".
Finally, reference your ImageSource like this:
<Image Source="Themes/IMAGE.png" />
or try
<Image Source="../Themes/IMAGE.png" />
Personally, I like to put my style templates in their own .xaml file, and reference them all as MergedDictionaries.
Typed base style in Themes\Generic style is automatically applied only to Custom Control.
If you need use typed based style in your user control you need add generic.xaml to user control resources.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Also change Image Source URI to
<Image Source="pack://application:,,,/WpfCustomControlLibrary1;component/SmallHandle.png" />

What's the correct way to create a custom control

Edit (as commented: XY-Problem) - Problem:
I want to create my own control which has predefined styles and positions for special elements (Button,...), but in general everything should be able to be placed inside my custom control. The custom control in my case is just a "menubar" which should be able to be used anywhere in the "GUI code" - but there is no need it has to be there. But when it is used it should be the same style and behavior everywhere. A style is - I think - not enough, because there are also predefined elements in this menubar (e.g. Help is already in menubar)
Edit end.
I want to build a custom control (just a special stackpanel) in WPF with the following requirements:
can be used as any other control within a xaml
has defined styles for controls within the custom control
First I simply tried to create a UserControl containing a stackpanel with defined styles (in the xaml) for containing elements (e.g. Button). This UserControl contained the
<ContentPresenter />
in the xaml. With this method it is not possible to name the containing elements. E.g.:
<mynamespace:MyStackPanel>
<Button Name="w00t">This does not work!</Button>
</mynamespace:MyStackPanel>
Next try was to create a "real" custom control. This custom control is just a class without the xaml. Code is very simple. Class inherits from UserControl and just contains:
StackPanel sp = new StackPanel();
sp.Children.Add(new ContentPresenter());
this.AddChild(sp);
Woooohoooo, now it's possible to name the containing elements. But still a big problem: How to define the styles?
I could define the style for my very own custom control in a ResourceDictionary. But i have to add the ResourceDictionary to the global (App.xaml) Resources. And then I can define styles only for my custom control - not for the containing elements? - But anyway... doing it like this just feels wrong!
So the main question is: WHAT is the "correct" way of creating a custom control which can be used in xaml like any other control? If the second way is the correct way - how is it possible to set the style like I do it in a xaml (e.g. every Button in this element has a special style) and has it to be a "global" ResourceDictionary?
How is it implemented in third-party stuff?
Ok I made an example for you, which involves Custom Controls (as Opposed to UserControls)
Step 1:
Create a new class (code only, no XAML) derived from ContentControl (or whatever UI element that has a behavior similar to what you need)
public class ReusableContainer : ContentControl
{
public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register("Button", typeof(Button), typeof(ReusableContainer), new PropertyMetadata(default(Button)));
public Button Button
{
get { return (Button)GetValue(ButtonProperty); }
set { SetValue(ButtonProperty, value); }
}
}
See how I'm defining the Button property as a DependencyProperty here. You can add more DPs for whatever "content placeholders" that you need in your custom control.
Step 2:
Have your predefined Styles for the UI elements inside the container in a separate ResourceDictionary:
CustomStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Background" Value="Green"/>
</Style>
</ResourceDictionary>
Step 3: in app.xaml, define an application-wide style for the ReusableContainer, which defines it's template:
<Application x:Class="WpfApplication14.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="{x:Type local:ReusableContainer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ReusableContainer}">
<ControlTemplate.Resources>
<ResourceDictionary Source="CustomStyles.xaml"/>
</ControlTemplate.Resources>
<ContentPresenter Content="{TemplateBinding Button}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
See how I'm using the TemplateBinding expression to define that the ContentPresenter's content is going to be defined by the Button property in the ReusableContainer.
Also notice how I'm Adding the Resources in CustomStyles.xaml to the ControlTemplate.Resources collection. This makes these resources available to all UI elements inside the Template.
Step 4:
Place your ReusableContainer in a Window:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button1" Content="Hello! Button 1"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button2" Content="Hello! Button 2"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button3" Content="Hello! Button 3"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
</StackPanel>
</Window>

WPF Style for base window not applied in App.xaml, but is in Themes/Generic.xaml

I am in the process of creating a base window class for most of my windows to derive from. Obviously the best solution for this was a separate class, and a style that applies to it.
The issue is that the <Style ../> I have is not being applied when it is in App.Resources. That is, if it's defined in an external ResourceDictionary, and merged into App.xaml's resources, or a local dictionary and merged, or placed inline into App.Resources. The <Style ../> is, however, applied when it is placed into Themes/Generic.xaml.
The problem can be demonstrated without doing anything special at all in the base window, apart from overriding the DefaultStyleKeyProperty.
Below is ThemeWindow:
public class ThemeWindow : Window
{
static ThemeWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ThemeWindow), new FrameworkPropertyMetadata(typeof(ThemeWindow)));
}
}
Here is the very simple <Style ../> I am trying to apply (it makes the Window background red, nothing more):
<Style TargetType="{x:Type testing:ThemeWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type testing:ThemeWindow}">
<Grid>
<Grid.Background>
<SolidColorBrush Color="Red"/>
</Grid.Background>
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The MainWindow that uses ThemeWindow, is simply the following XAML:
<testing:ThemeWindow x:Class="Testing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testing="clr-namespace:Testing"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="125,83,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</testing:ThemeWindow>
Now, as stated, if you place that Style in its own ResourceDictionary, and include it like this:
<App.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/ThemeWindow.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</App.Resources>
.. it does not work. If you inline the style straight into App.Resources, it does not work.
The only situation I can find it working is to call the ResourceDictionary xaml Generic.xaml, and place it into the Themes/ directory of the application.
I am wondering exactly why this is happening.
My only theory is that when WPF sees a control type, it will head over to Themes, and scan all ResourceDictionarys for the type, then fall back to Generic.xaml and load it. This doesn't explain why it would not load if the <Style /> is available in a merged ResourceDictionary though. Note that it does work if the MergedDictionary is placed into Generic.xaml, for obvious reasons.
I'm perfectly fine with having to merge the ResourceDictionary into Generic.xaml if that's what I have to do. I just want to get down at the technical details as to why it needs to be like this.
Screenshots of this not working / working:
I have a simple workaround that would allow you to set your Style in you app.xaml.
Define your style in app.xaml like this :
<Style x:Key="{x:Type testing:ThemeWindow}" TargetType="{x:Type testing:ThemeWindow}">
And change your ThemWindow to this :
public class ThemeWindow : Window
{
static ThemeWindow()
{
StyleProperty.OverrideMetadata(typeof(ThemeWindow), new FrameworkPropertyMetadata(GetDefautlStyle()));
}
private static Style GetDefautlStyle()
{
if (defaultStyle == null)
{
defaultStyle = Application.Current.FindResource(typeof(ThemeWindow)) as Style;
}
return defaultStyle;
}
private static Style defaultStyle = null;
}
It does not really solve the question, but that would allow you to achieve what you need !
EDIT : Looking at DefaultStyleKey reference, it's clearly stated that it's used for theme style lookup. That explains why it won't find it in app.xaml or any other dictionary. It will only search in Theme dictionaries. So you either have to define your style in a Theme Dictionary, or to use the Style property directly as in the above example.
I come around the following solution already discussed in stackoverflow. it would required to add in the load component when load of the application.
Refer Solution

WPF not applying default styles defined in MergedDictionaries?

In a WPF application I defined default control styles in separate resource dictionaries (e.g. "ButtonStyle.xaml"), and added them as merged dictionaries to a resource dictionary named "ResDictionary.xaml".
If I refer this "ResDictionary.xaml" as merged dictionary in my App.xaml, the default styles are not applied. However, if I refer the "ButtonStyle.xaml", it works correctly.
If I recompile the same code in .NET 3.5 or 3.0, it recognizes and applies the default styles referred in "App.xaml" through "ResDictionary.xaml", but not in .NET 4.0.
At runtime if I check the Application.Current.Resources dictionary, the default styles are there, but they are not applied, only if I specify the Style property explicitly in the Button control.
Are there any solutions to refer a resource dictionary (containig default styles) this way in .NET 4.0?
App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/ResDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
ResDictionary.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Default/ButtonStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
ButtonStyle.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Background" Value="Yellow"/>
</Style>
</ResourceDictionary>
The best solution is to add a dummy default style in the resource dictionary where you merge all resources together.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Style/Button.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Control" BasedOn="{StaticResource {x:Type Control}}" />
This could be caused by a known bug when there is a single style in application.resources within app.xaml when not using a startupuri.
The fix is to add an additional style like this...
...
<Style x:Key="unused" />
</Application.Resources>
for more details check out this link.... http://bengribaudo.com/blog/2010/08/19/106/bug-single-application-resources-entry-ignored
There is a sort-of fix for this, but I’ve only been able to make it work at the window level (not the application level).
In order to include a WPF 4.0 resource from a separate project, the resource must be added as a resource in the window’s code behind. The statement belongs in the window’s constructor, prior to the InitializeComponent method call:
public ControlsWindow()
{
this.Resources = Application.LoadComponent(new Uri("[WPF 4.0 ResourceProjectName];Component/[Directory and File Name within project]", UriKind.Relative)) as ResourceDictionary;
InitializeComponent();
}
Note: Replace the '[WPF 4.0 ResourceProjectName]' text with your resource's project name. Also, the '[Directory and File Name within project]' needs to be replaced with the relative location of the resource file (like 'Themes/StandardTheme.xaml')
I go into more details about this issue here.

Categories

Resources