Custom control not inheriting parent's styles - wpf

I'm trying to maintain a uniform look and feel across elements in my WPF application, and at the same time I want to create a modified TextBox. However, when I do this, styles that I define at the application level for TextBox aren't being applied to the class I created, even though the style created for my custom control is using the BasedOn property.
Is there something I'm missing that's causing this to behave differently than I expect?
I reproduced the issue in a brand-new WPF project in VS2010 with this setup:
C# Code:
public class CustomTextBox : TextBox
{
static CustomTextBox() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(typeof(CustomTextBox)));
}
}
XAML in Themes\Generic.xaml:
<Style TargetType="{x:Type local:CustomTextBox}" BasedOn="{StaticResource {x:Type TextBox}}"/>
XAML in App.xaml:
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red"/>
</Style>
</Application.Resources>
However, in the designer and when I run the app, the CustomTextBox falls back onthe default styling for the text box instead of having a red background, even though the documentation for the BasedOn property suggests that my derived class should have this styling...
There are several ways that styles in WPF can be extended or inherited. Styles can be based on other styles through this property. When you use this property, the new style will inherit the values of the original style that are not explicitly redefined in the new style.
...
Note: If you create a style with a TargetType property and base it on another style that also defines a TargetType property, the target type of the derived style must be the same as or be derived from the type of the base style.

Short Answer: Your style is based on a StaticResource
<Style TargetType="{x:Type local:CustomTextBox}" BasedOn="{StaticResource {x:Type TextBox}}"/>
When you did this, you are not changing the StaticResource
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red"/>
</Style>
So CustomTextBox is not supposed to inherit the red background.

Related

Set default Style for FrameworkElement is not applied to derived class

I am a beginner in XAML. I create a new default style in a ResourceDictionary.
<Style TargetType="FrameworkElement" BasedOn="{StaticResource {x:Type FrameworkElement}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Then I import it to UserControl.Resources.
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PathToStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
What I expect is that it will be applied to all FrameworkElements used in that UserControl, but it is not.
What infomation am I missing here?
The problem is because TargetType is specific.
A style which you do not give a x:Key to automatically is given a key which is equivalent to it's type.
Hence your style:
<Style TargetType="FrameworkElement"
Actually has an x:Key given to it which is
<Style x:Key="{x:Type FrameworkElement}"
https://learn.microsoft.com/en-us/dotnet/api/system.windows.style.targettype?redirectedfrom=MSDN&view=netcore-3.1#System_Windows_Style_TargetType
A piece of ui which is "looking" for a style looks for a matching key to it's type using that x:Key. It does not search up it's inheritance chain as well.
EDIT:
You can prove this:
In a new wpf app, add a frameworkelement to you mainwindow and a style targets frameworkelement.
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<local:TestSubClass/>
</Grid>
and
public class TestSubClass : FrameworkElement
Spin it up and take a look at the live visual tree > Properties.
The frameworkelement fills the grid and has an actualheight matching the grid's height.
No style is set on it.
Hence this is the situation where no style is set and inheritance is not working as claimed in another post.
Change that to a regular frameworkelement:
<Window.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Height" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<FrameworkElement/>
</Grid>
Spin it up with an f5 and take a look at the live visual tree.
There is a style in the properties.
Actualheight is 10.
What I'm expecting is it will apply to all FrameworkElement used in That UserControl
By all FrameworkElements you probably mean all FrameworkElements including its derivatives. Applying the Style as implicit style works perfectly if you target a distinct control like a Button. However, it may not work as you expect, if you target a base type of a control, like FrameworkElement, because each derivative of that base type can have their own style applied explicitly or implicitly that breaks your anticipated behavior by:
Not being based on the style of the base type
Not being based on a style that is transitively based on the base type style
Overriding the properties of the base style
The essential misunderstanding here is that all styles of derivatives of a base type will base their style on the style of said base type, but this does not apply in general.
You can check this yourself by extracting the Style of any control via Visual Studio or Blend. For example, let's look at the Style of a Button. As you can see, it is not even based on FrameworkElement, so it will not apply your base style. Even if it would, there is a chance that it will override the Margin property itself.
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}"/>
<!-- ...other setters. -->
</Style>
Apart from that, you have to define your base type style before any other styles of derivatives, because you can only use BasedOn with StaticResource. Consequently, if the style of a derived control is already defined before your style, your base style will not be applied.
What infomation am I missing here?
The fact that there are default styles defined for more derived types such as for example Button and Control and that these will take precedence over and be applied instead of your custom FrameworkElement style.
Your approach of defining a single Style for all FrameworkElements won't work. You'll need to define an implicit type for each derived FrameworkElement type.

Reset inherited WPF style?

In the App.xaml portion of my application I have a ResourceDictionary element that targets things like DataGridColumnHeader and DataGridCell and applies custom styling to them. These definitions are global (in that they don't use a x:key - they apply to all instances of the type).
Is it possible to completely reset the style of these types to the 'base' Aero theme (or whatever theme is currently in use) for use in a subsection of my application?
I've tried using
<Style TargetType="{x:Type DataGridColumnHeader}" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
</Style>
Which doesn't seem to do anything, as I'm guessing its just inheriting itself.
There are 2 ways to reset the style:
Set the style property of the element to {x:Null}. You will have to do this on every element.
<Button Height="50" Style="{x:Null}"></Button>
Define an empty style in the resources of the element containing the elements you want to reset. Don't specify BasedOn, otherwise it will inherit all properties and does not reset them to standard (as you correctly noticed).
<Style TargetType="{x:Type DataGridColumnHeader}">
</Style>

What is the most economical way to implement your own window border and title bar?

I am pretty new to WPF and am sitting here with my book trying to figure out the best approach to this application.
The title bar is not part of the client area so I am making my own title bar.
Which way would it be easiest to make this into some sort of resource to apply to all new windows I create?
<Application.Resources>
<Style x:Key="WindowTheme">
<Setter Property="Window.WindowStyle" Value="None"/>
</Style>
<!--Would I create a user control here for the title bar/border and title bar buttons? Or would it be a style?-->
</Application.Resources>
In WPF, there are two ways to use styles: Named styles and typed styles. A named style has an x:Key="..." attribute. A typed style doesn't have a name, but a TargetType="..." attribute (Rem: Named styles can and very often do have a TargetType as well, so named styles and unnamed styles would be more precise). Typed styles automatically get applied to all controls in the scope, which are of type TargetType (not a derived type).
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Blue" />
</Style>
To create your own window, you can set it's template property to a UserControl in the style:
<Style TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The professional way to implement the control template is to implement it 'from scratch', this means not using a UserControl which derives from Window. To do this, you define the visual tree of the Window, and use the WPF feature TemplateParts to define what part of your control template is responsible for what functionality of the window.
Here is a tutorial which describes pretty exactly what you want to do:
CodeProject tutorial

How to apply WPF styles based on top of another style without using BasedOn property

I am styling CellValuePresenter (From Infragistics) to give different look to Gid Lines and have defined a style (gridLineStyle) and applied to the Grid's CellValuePresenterStyle Property.
I have discovered that there are columns for which custom templates are defined by templating CellValuePrenter and the grid lines are not visible (as expected). I can make it work by applying BasedOn property as in
<Style x:Key="gridLineStyle" TargetType="ig:CellValuePresenter">
<Setter Property="BorderThickness" Value="0,0,1,1"/>
<Setter Property="BorderBrush" Value="{Binding Path=BorderBrushForAllCells,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type pwc:CarbonBlotter}}}"/>
</Style>
<Style x:Key="anotherColumnStyle" TargetType="{x:Type ig:CellValuePresenter}" BasedOn="{StaticResource gridLineStyle}">
<Setter Property="Template">
....
<pwc:BaseXamDataGrid>
<pwc:BaseXamDataGrid.FieldSettings>
<ig:FieldSettings CellValuePresenterStyle="{StaticResource gridLineStyle}"
...
But there are many styles with custom templates, and just wondering whether I can define a style without using BasedOn property and yet inheriting default style
You can find the complete CellValuePresenter style definition in your infragistics installation folder under DefaultStyles\DataPresenter\DataPresenterGeneric_Express.xaml
You can copy that style into your App.xaml under Application.Resources, modify it as you wish and that should become your new default style for CellValuePresenter.

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;

Resources