WPF Replacing AND extending styles - wpf

How do I change what WPF's idea of the default style for a control is? And why is this happening in the first place? In the below XAML, I declare a Style for Button, and then further declare a new Style that overrides one of the setters called "HugeBut". I would expect that HugeBut is implicitly BasedOn my new un-named style, but apparently it is not;
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Red">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- badness -->
<Style TargetType="{x:Type Button}" x:Key="HugeBut">
<Setter Property="Foreground" Value="Yellow"/>
</Style>
<!-- works, but I do not want to explicitly set the based-on. -->
<Style TargetType="{x:Type Button}" x:Key="HugeBut" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Foreground" Value="Yellow"/>
</Style>
</Window.Resources>
<Button Content="Regular" />
<Button Content="huge!" Style="{StaticResource HugeBut}"/>
You would expect two red buttons, one with black text and one with yellow, but Style HugeBut inherits all of the values that I did not specify in my unnamed style from the system default theme for Button (Aero in my case).
What can I do to change this behavior?

It appears that the answer is here:
http://wpfthemereplacer.codeplex.com/
From the site description:
This library allows users to provide their own resource dictionaries
to replace the default theme dictionaries loaded by WPF. This makes it
so you don't have to decorate custom styles with
BasedOn="{StaticResource {x:Type ...}}" when your own custom theme is
being used in your application. It also makes it so if you have custom
controls that just provide enhanced capability and don't need to
replace the the style, you don't need to define a new style or
override the DefaultStyleKey when you create the custom control.
This is exactly what I'm looking for. This will allow me to use Styles as they are meant to be used across an app that has been extensively "re-themed", rather than theme-ing by setting global styles (and then deal with tracking down bits of code that are missing BasedOn, or cannot deal with it at all due to WPF bugs and other constraints)

works, but I do not want to explicitly set the based-on.
Well, the framework does not really care if you don't want to, for all i know, you have to.

Related

WPF how to apply a style to a custom control item without overriding the derived style

I'm building a custom control search box in wpf based on the common windows search bar (my version). In the windows implementation the buttons would change background and foreground depending on whether they were pressed or not. To do this I'm using a style and triggers to change the button colors.
As I'm using MaterialDesign my custom control contains a button which uses the derived/default material design button. When a style is applied to this button the material design style is overridden in favor for the default wpf button. How can I apply a style while keeping the base material design style?
As my issue is solely with applying a style to a button I have omitted the search box style and instead created an example of my problem containing just a button.
Generic.xaml -CustomButton
<Style TargetType="{x:Type local:CustomButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<!-- Normally the below button would be inside a grid with additional buttons, labels and content presenter. I have removed these for simplicity-->
<Button Name="MaterialButton"
Content="CustomButton">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
App.xaml - (Material Design is in resource dictionary.)
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
What I've tried
I've tried using the BasedOn attribute as suggested here and here however this has not resolved my issue.
Deriving from a material style based on the "Material Design theme is lost"
FAQ, results in this error:
Exception: Cannot find resource named 'MaterialDesignFlatButton'. Resource names are case sensitive.
Generic.Xaml Attempted changes to CustomButton
<!--Same custon control as before--->
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatButton}">
<!--same trigger-->
It looks like you haven't installed the correct dependencies. Right-click on the project which uses the style and choose the NuGet package manager. Search for a MaterialDesignThemes package and install it. If this is already installed, then reinstall it again. This should fix it.
The rest of your approach is correct.
Alternatively copy the original style (snapshot) and all references from the GitHub repository and paste it to your App.xaml.
If all dependencies are installed (or the original style copied), then use the following Style to apply the MaterialDesign default Button style to your CustomButton:
<Style TargetType="local:CustomButton"
BasedOn="{StaticResource MaterialDesignFlatButton}" />
Update
Now that you have edited your question to provide the necessary details, I can tell what went wrong.
You have not simply extended Button, but also overridden the default Style of the extended Button. Doing this requires a default Style to be defined inside the Generic.xaml file. This file doesn't allow merged dictionaries due to the way this default resources are resolved. Only references to resources in the same ResourceDictionary of Generic.xaml are allowed.
To solve this problem, you need to override the default Style explicitly. The override Style must be in the scope of the MaterialDesignFlatButton resource, otherwise the reference won't resolve and cause the error message you had posted before. The following example defines the override Stylein App.xaml:
<Style TargetType="CustomButton"
BasedOn="{StaticResource MaterialDesignFlatButton}">
<Setter Property="OverridesDefaultStyle" Value="True" />
</Style>
</ResourceDictionary>
Remarks
Since you are obviously not interested in a default Style, you should merge details of this default Style e.g. the ControlTemplate into the override Style. You can then delete the default Style from Generic.xaml. You then also need to remove the following lines from the static constructor of the custom control e.g. CustomButton:
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton),
new FrameworkPropertyMetadata(typeof(CustomButton)));
After merging the now obsolete default Style of CustomButton into the override Style, the new override Style that could look like this:
App.xaml
<Style TargetType="local:CustomButton"
BasedOn="{StaticResource MaterialDesignFlatButton}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

DataPicker Button influenced by a global styling on all Buttons

I'm maintaining a big old C#/WPF application in which a style has been set globally for all buttons It can sound ugly, but I can't change this without refactoring the whole app.
Here is an extract of this style:
<Style TargetType="Button">
<Setter Property="Height" Value="32"/>
<Setter Property="MinWidth" Value="96"/>
<Setter Property="Margin" Value="10"/>
</Style>
The problem is that when I want to use a DatePicker, this global style influenced the appearance of the DataPicker:
Is there a simple way to restore the default Margin, Height and MinWidth only for the Button inside the DatePicker?
You can override the Style for the Buttons locally. The following XAML sets the Style for all Buttons inside the DatePicker back to the default Style.
<DatePicker>
<DatePicker.Resources>
<!-- Default Style -->
<Style TargetType="{x:Type Button}"/>
</DatePicker.Resources>
</DatePicker>
Edit
As requested in the comments, a Style to fix this issue globally
<Style TargetType="{x:Type DatePicker}">
<Style.Resources>
<Style TargetType="{x:Type Button}"/>
</Style.Resources>
</Style>
Note: This Style should be placed in the same hierarchy-context as the Button Style.

WPF Stackpanel spacing between custom controls

how is it possible to get a space between some custom controls inside a stackpanel? I did it right before with a Textbox, Button, and so on, but i cannot do it with a custom control.
That's the code i've got so far
<Grid>
<StackPanel x:Name="spTasks" CanVerticallyScroll="True">
<StackPanel.Resources>
<Style TargetType="local:SmartTaskOverview">
<Setter Property="Margin" Value="50,50,50,50" />
</Style>
</StackPanel.Resources>
</StackPanel>
</Grid>
Thanks for your help
FrameworkElements and their sub-classes don't just look for a resource using the controls type, they use the value of DefaultStyleKey. It's common practice for most sub-classes of Control (and some other FrameworkElements) to override the default value of this dependency property in the static constructor to be the type of the control, but sub-classes of UserControl usually don't bother.
static Foo()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Foo), new FrameworkPropertyMetadata(typeof(Foor));
}
If you didn't do this in your SmartTaskOverview then it will be looking for its default style using typeof(UserControl) as the resource key and not typeof(SmartTaskOverview).
Note: The UserControl will require a control template to show its children, this is normally provided by the default style for UserControl but by changing the key it will find your default style instead. To resolve this, just base your style on the UserControl style.
<Style TargetType="local:SmartTaskOverview" BasedOn="{StaticResource {x:Type UserControl}}">
<Setter Property="Margin" Value="50,50,50,50" />
</Style>
Alternatively you could provide a simple template yourself.
<Style TargetType="local:SmartTaskOverview">
<Setter Property="Margin" Value="50,50,50,50" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SmartTaskOverview}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resource Dictionary WPF

I have a resource dictionary in my WPF application which contains the style information for the various controls.
Can it be used like the way we use in CSS in HTML? For example
p
{
margin:20px;
font:Tahoma;
}
this applies to all "p" tags in HTML. We dont have to specifically mention that in the HTML for "p" tag.
Is the same approach applicable in WPF, or do we have to specifically
mention the style
<TextBlock Text="Test" Style="{DynamicResource SomeTextblockStyle}" />
in the XAML
You can certainly set a default style for each type. You can do this within your Generic.xaml, note that I am not providing a key.
<Style TargetType="{x:Type Button}">
<Setter Property="Height" Value="25"/>
<Setter Property="Foreground" Value="White"/>
</Style>
This will style every instance of a Button within your application as such.
If you were go to a XAML file and define an instance of a Button, overriding the Foreground value, that local instance will take precedence over the global style.
<Button Foreground="Black"/>
You can set style like using key
<Style TargetType="{x:Type TextBlock}" x:Key="myStyle">
<Setter Property="Margin" Value="20"/>
<Setter Property="FontFamily" Value="Tahoma"/>
</Style>
And in the Window.Xaml
<TextBlock Text="Hello" Style="{DynamicResource myStyle}"/>

How do I define the default background color for window instances in a shared ResourceDictionary?

I can't seem to set a default background color for all of my windows in my application. Does anyone know how to do this?
Currently I'm setting a theme in my App.xaml file like this.
<Application>
<Application.Resources>
<ResourceDictionary Source="Themes/SomeTheme.xaml" />
This basically styles my entire application.
Inside of SomeTheme.xaml I am trying to set a default color for all of my windows like this.
<SolidColorBrush Color="{DynamicResource MainColor}" x:Key="CommonBackgroundBrush" />
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="{DynamicResource CommonBackgroundBrush}" />
</Style>
This syntax is completely ignored for derivatives of type Window.
Is there some way to force the style to apply to all derivatives of Window?
The weird thing about this syntax, is that it actually shows the correct color in the VS design preview window.
Your windows are not instances of Window, they are instances of classes derived from Window. So I think you will have to list them all, but you can use BasedOn to help.
If there really is no actual inheritance, this might be as simple as you can make it:
<Style TargetType="{x:Type Window}">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Blue"/>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:MainWindow}" BasedOn="{StaticResource {x:Type Window}}"/>
<Style TargetType="{x:Type local:Dialogue}" BasedOn="{StaticResource {x:Type Window}}"/>
...
At least you don't need to copy the whole style that way
Give your style a x:Key,
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
and then reference it in every window it should apply to:
<Window x:Class="WpfApplication1.MainWindow" ... Style="{StaticResource WindowStyle}">
I'm new to all this so here's my 5 pence worth.
I changed the background for the grid...not sure if there are any problems doing it this way :)
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="#FFECE9D8"/>
</Style>

Resources