Warning: this is not a question, but a recap of WPF Style working. The question is if this summary is right.
I read that in a style definition you can get rid of the TargetType if you include the control's class name in the Property name. That is this:
<Style x:Key="SomeKey" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red"/>
</Style>
becomes this:
<Style x:Key="SomeKey">
<Setter Property="Button.Foreground" Value="Red"/>
</Style>
Oh great, this means that, given three controls:
<StackPanel Height="40" Orientation="Horizontal">
<Button Style="{StaticResource MyStyle}" Content="First"/>
<TextBox Style="{StaticResource MyStyle}" Text="Second"/>
<Label Style="{StaticResource MyStyle}" Content="Third"/>
</StackPanel>
I can do something like this:
<Window.Resources>
<Style x:Key="MyStyle">
<Setter Property="Button.Foreground" Value="Red"/>
<Setter Property="TextBox.BorderBrush" Value="DarkBlue"/>
<Setter Property="Label.Background" Value="LightPink"/>
<Setter Property="Control.Margin" Value="4,0,0,0"/>
</Style>
</Window.Resources>
That is: first button should be standard but with red text; second should be standard but with DarkBlue Border. Third should be with LightPink foreground.
This is what I get instead:
That is: except for the third that is a label and its BorderThickness defaults to 0, every style goes to every control.
After a bit of digging it seems to me that all the mechanics of applying a style boils down to what follows.
Since it seems to be quite rudimentary, I'm wondering if this description is right or if I'm missing something important.
Phase 1: defining a style
1.a A style gets a key explicitly if x:Key is set (<Style x:Key="MyStyle">)
1.b If x:Key is not set, TargetType has to be set. (<Style TargetType="{x:Type Button}">). Internally, it assigns the style a key that is the name of the type specified in TargetType
1.c If both TargetType is set and one or more Setter are defined with the syntax <Setter Property="Class.Property" Value=.../>, there is no check of consistence between the TargetType and the Classes values in the Setters.
That is, this is legal:
<Style x:Key="MyStyle" TargetType="{x:Type Button}">
<Setter Property="Control.Margin" Value="4,0,0,0"/>
</Style>
even if it has little sense, while the following may be useful in (at least) one case.
<Style x:Key="MyStyle" TargetType="{x:Type Control}">
<Setter Property="TextBox.Text" Value="just an example"/>
</Style>
That is almost everything regarding style definition.
Pahse 2: associating the style with controls
2.a Does the control have a style defined (<Button Style="{StaticResource MyStyle}">)? Lookup in the resources if there is such a style. If there is, check if it has also a TargetType; if the control is not that class of a subclass, raise the exception:
XamlParseException. Exception: Cannot find resource named 'MyStyle'.
Resource names are case sensitive
So, in this case TargetType is not meant for filtering. It just has to match.
2.b If the control doesn't have a style defined, lookup the resources for a style with the key equals to the control's class name. This comes from a style defined with only TargetType. If found, go on to apply phase.
Notice that styles that are defined with a TargetType of a superclass of the control wont be applied. They have to match exactly. This is another 'limitation' of a style system that is far from the complexity of CSS; WPF styles seem to support little more than a dictionary lockups.
Phase 3: Applying the style
At this point, a control has a style to apply. If the style defined a TargetType, it matches with the control (the control is of that type or a subtype of the type defined in the style applied).
The style is a set of Setters that may or may not have their own specification of target control(with: <Setter Property="Control.Foreground" Value="Red"/>; without: <Setter Property="Foreground" Value="Red"/>).
It seems (and my previous example seems to proof it) that by design the class specification at the setter level is not meant for further refining or filtering. What is meant for? Good questions, I'll go back to it later on.
How does it work then? It simply ignores the class information (if present) and it goes on trying to apply every setter to the control. That is why even if I
defined a Setter like this:
<Setter Property="Label.Background" Value="LightPink"/>
all of the three controls get the LightPink background, not only the Label.
And that's the reason of this post. I couldn't find a complete explanation of how styles really work. All the information that I could find was limited to showcasing some basic usage; that is they don't show you how to approach a complex solution that requires a much more detailed knowledge.
Lastly, why should I ever specify classes names in Setters if the famework doesn't use them for filtering; it's just a useless repetition. Why that feature at all?
<Style x:Key="MyStyle">
<Setter Property="Control.Foreground" Value="Red"/>
<Setter Property="Control.BorderBrush" Value="DarkBlue"/>
<Setter Property="Control.Background" Value="LightPink"/>
<Setter Property="Control.Margin" Value="4,0,0,0"/>
</Style>
The only case that I could think of is this; i cant set a "Text" property without specifying TextBox, since Control doesn't have a Text property.
Again, it doesn't mean filtering and I suppose that if another control, not a subclass of Text had a different Text property, it would get set also.
<Style x:Key="MyStyle" TargetType="{x:Type Control}">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="BorderBrush" Value="DarkBlue"/>
<Setter Property="Background" Value="LightPink"/>
<Setter Property="Margin" Value="4,0,0,0"/>
<Setter Property="TextBox.Text" Value="just a test"/>
</Style>
I read that in a style definition you can get rid of the TargetType if you include the control's class name in the Property name.
Yes that is true according the reference of Style.
[...] except for the third that is a label and its BorderThickness defaults to 0, every style goes to every control.
A style has a specific target type, which it can be applied to. If you define a style without a target type, it will default to IFrameworkInputElement. Both FrameworkElement and FrameworkContentElement implement this interface, meaning it applies to almost any element, including Button, TextBox and Label.
Let us have a look at the properties that you have defined in this style.
Foreground is defined on TextElement, but button exposes it by adding Control as its owner.
BorderBrush is defined on Border, but TextBox exposes it by adding Control as its owner.
Background is defined on Panel, but Control exposes it by adding Control as its owner and Label is a derivative of Control, so it inherits it.
Margin is defined on FrameworkElement and Control inherits it.
What is mean by adding a control as owner is that the corresponding controls do not define the dependency properties themselves, but "borrow" them from others using the AddOwner method.
This leads to what you see in your example, the properties are effectively defined for all Controls because the default TargetType does not limit the types apart from framework and framework content elements and due to the way that the affected dependency properties are implemented.
Phase 1: Defining a style
1.a A style gets a key explicitly if x:Key is set (<Style x:Key="MyStyle">)
Yes. That is called an explicit style, because you have to apply it to each target control yourself.
1.b If x:Key is not set, TargetType has to be set. (<Style TargetType="{x:Type Button}">). Internally, it assigns the style a key that is the name of the type specified in TargetType
Not in general. If you define a style in a resource dictionary, yes. The key will be of type object, so not the name of the type, but the Type itself will be its key. If you define a style directly within the Style property of a control, you neither need a key, nor a target type.
1.c If both TargetType is set and one or more Setter are defined with the syntax , there is no check of consistence between the TargetType and the Classes values in the Setters.
Yes. Your first example is not illegal, since a button inherits its Margin property from FrameworkElement, it is just redundant. The follwing setters are essentially the same in this case.
<Setter Property="FrameworkElement.Margin" Value="4,0,0,0"/>
<Setter Property="Control.Margin" Value="4,0,0,0"/>
<Setter Property="Margin" Value="4,0,0,0"/>
I do not think that your second example makes much sense. When you define a key, you have to apply the style explicitly to a control and that makes only sense for TextBox, so you could define it as target type.
Phase 2: Associating the style with controls
2.a Does the control have a style defined ()? Lookup in the resources if there is such a style. If there is, check if it has also a TargetType; if the control is not that class of a subclass, raise the exception [...]
Basically yes, the target type has to match the type or a derivative. The resource lookup differs between StaticResource and DynamicResource. Please refer to the linked source to get a better understanding.
It would throw an InvalidOperationException wrapped in a XamlParseException, if the types do not match. The exception that you show is different and indicates that the style is not found at all.
2.b If the control doesn't have a style defined, lookup the resources for a style with the key equals to the control's class name. This comes from a style defined with only TargetType.
Yes, the type has to match the type of the control exactly with implicit styles.
Notice that styles that are defined with a TargetType of a superclass of the control wont be applied. They have to match exactly. This is another 'limitation' of a style system that is far from the complexity of CSS;
Yes this is a corollary for implicit styles. The key is the type of the target control and if that is a base type of the control both types are not equal.
WPF styles seem to support little more than a dictionary lockups.
If you look closely, a ResourceDictionary is exactly that, a dictionary of resources. Applying resources is looking up a key defined as x:Key in said dictionary.
Phase 3: Applying the style
How does it work then? It simply ignores the class information (if present) and it goes on trying to apply every setter to the control. [...] all of the three controls get the LightPink background, not only the Label.
The class information is not disregarded, as explained in the beginning.
Lastly, why should I ever specify classes names in Setters if the famework doesn't use them for filtering; it's just a useless repetition. Why that feature at all?
Look at it the other way around. Normally, you would have to specify the target type, e.g. Button in each and every setter explicitly. Defining a TargetType that lets you omit the full qualification is a convenience feature. Moreover, as you examples have shown you can use this to apply style to multiple controls.
<StackPanel Height="40" Orientation="Horizontal">
<Button Style="{StaticResource MyStyle}" Content="First"/>
<TextBox Style="{StaticResource MyStyle}" Text="Second"/>
<Label Style="{StaticResource MyStyle}" Content="Third"/>
</StackPanel>
<Style x:Key="MyStyle">
<Setter Property="Button.Foreground" Value="Red"/>
<Setter Property="TextBox.BorderBrush" Value="DarkBlue"/>
<Setter Property="Label.Background" Value="LightPink"/>
<Setter Property="Control.Margin" Value="4,0,0,0"/>
</Style>
Qualifying the base property via a subclass/derived type is always possible. The XAML parser will automatically infer the base type of the DependencyProperty by accessing the DependencyProperty.OwnerType property. For example, since Foreground is defined on the type Control, the parser will translate Button.Foreground to Control.Foreground. Since all three elements extend Control, the properties can be resolved on each.
This also adds explanation to your Phase 3 section:
"Lastly, why should I ever specify classes names in Setters if the
famework doesn't use them for filtering; it's just a useless
repetition. Why that feature at all?"
As said before, this is the class qualifier to reference the member of a class. It is always required to reference the exact memory location in order to distinguish or allow duplicate member names (of different calsses): Button.Foreground. The complete reference (FQN Fully Qualified Name) also includes the namespace: System.Windows.Controls.Button.Foreground. The qualification of the namespace can be avoided when importing the namespace either C# using System.Windows.Controls; or XAML xmlns:system="clr-namespace:System;assembly=mscorlib (note that the System.Windows.Controls namespace is imported by default in XAML scope).
In XAML you can shorten the qualification further by specifying the TargetType on e.g., a Style or ControlTemplate. This allows you to omit the class qualifier since the the XAML parser knows that all referenced properties in the current scope refer to the type specified by the TargetType property.
"If there is, check if it has also a TargetType; if the control is not
that class of a subclass, raise the exception:
XamlParseException. Exception: Cannot find resource named 'MyStyle'. Resource names are case sensitive
So, in this case TargetType is not meant for filtering. It just has to
match."
Wrong. The exception you were showing is thrown when the resource key was not found. In case the Style.TargetType doesn't match with the referencing element type, a type mismatch exception is thrown.
"styles that are defined with a TargetType of a superclass of the
control wont be applied. They have to match exactly."
Wrong. The following example applies a Style that targets a superclass to a subclass. If this Style would be implicit (without x:Key defined) then it would automatically apply to all types of ButtonBase and all subtypes too within the styles scope:
<Style x:Key="ButtonBaseStyle" TargetType="ButtonBase">
<Setter Property="BorderBrush" Value="Red"/>
</Style>
<Button Style="{StaticResource ButtonBaseStyle}" />
<ToggleButton Style="{StaticResource ButtonBaseStyle}" />
My intent is to override phone theme. By default page markup has following code:
<phone:PhoneApplicationPage
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
I replace it with a style reference:
<phone:PhoneApplicationPage
Style="{StaticResource stylePage}"
Style is defined in app resources like this:
<Application.Resources>
<Style x:Key="stylePage" TargetType="phone:PhoneApplicationPage">
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="DeepSkyBlue" />
<Setter Property="FontFamily" Value="Arial" />
</Style>
</Application.Resources>
But background is still black, and font is white. Debugger shows that the values were applied.
If I set VisualRoot Grid's background, it's applied, but I want to set fontfamily and foreground values to be used by all my controls by default. Is it possible?
According to this msdn article - only the following properties are inherited in the visual tree in Silverlight: FontFamily, FontSize, FontStretch, FontStyle, FontWeight, and Foreground. Normally - dependency properties are not inherited in Silverlight (they often are in WPF). This is likely why these are not mentioned in the Silverlight MSDN article on dependency property value precedence, but the WPF version of the article mentions inherited value as lower priority than the local value or style setters. Your solution in fact works to some degree, but by default - most controls have font properties set by their style - Buttons by their default style, TextBlocks in page templates use PhoneTextNormalStyle or PhoneTextTitle1Style. If you remove the XML attributes setting the style of your TextBlocks - your application-wide font properties will apply to the TextBlocks.
Background dependency property unfortunately is not inherited, so your background will remain as is.
Your application-wide properties will not work on controls more complex than TextBlocks - like Buttons, since the default style of Buttons and other controls defines the style of TextBlocks explicitly using appropriate system styles.
I would say you should create your own resource dictionaries with styles of your controls and apply these to your controls manually, otherwise - you would need to write some code to automatically replace these properties at run time.
I have an application resource of the following
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{DynamicResource windowTextBackColor}"/>
<Setter Property="Foreground" Value="{DynamicResource windowsTextForeColor}"/>
</Style>
So all the text blocks in my application should assume those colours.
However the Menu and its containing MenuItems on my Main Window does not take these colours?
I have to do the XAML
for it to assume those colours, Is there a reason why setting a style that targets Text blocks does not work?
Thanks
I think you have to style the menu and menuitems separately. A MenuItem is a HeaderedContentControl, and its Header property is not a TextBlock, but an object, so it wouldn't be affected by a style for TextBlock.
You might also try changing that style to target Control instead of TextBlock. (Control is where Foreground and Background are defined.) I can't say for sure that it'll work, but if it does, it'll make every Control (TextBlocks, MenuItems, Buttons...) have those background and foreground colors.
Also, you might consider using BasedOn so that you can "inherit" the styles. If you don't, then styles defined farther up the hierarchy won't affect controls that have a style defined lower in the hierarchy. Basically, the lower ones mask the higher ones, unless you used BasedOn. Use it in this fashion:
BasedOn="{StaticResource {x:Type <your type here>}}"
Im currently creating a custom control (based on the WPF DataGrid). What i would like to do is to set default styling on the datagrid. Currently im setting the Style property which works. But my problem arrises when i create a style to change fx. the background color in the main applications app.xaml. Then all my "default" styling is lost and the DataGrid looks all standard only with the background property set.
I have tried using OverrideMetadata on each of the properties on the grid that i want to apply a default value to but with no luck. I also tried setting each property in the constructor but because of property precedence the styles from the main application then never is applied.
Did you set this in the static constructor?
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomType), new FrameworkPropertyMetadata(typeof(MyCustomType)));
Also, is the Key of your resource Style equal to your custom control's Type?
It mustn't have some other Key set, even with TargetType set to your control.
The Assembly should also be marked with the following attribute:
[assembly: ThemeInfo(
//where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.None,
//where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
ResourceDictionaryLocation.SourceAssembly
)]
If you create a style without a dictionary key, it will style all objects of the type you specify within the scope that you import your style dictionary (if you specify it in a Window.Resources, it will have scope for that Window... if you specify it in App.xaml... you get the picture).
<Style TargetType="{x:Type Button}">
<Setter Property="FontFamily" Value="Times New Roman" />
<Setter Property="FontSize" Value="30" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="#FFCA5132" />
</Style>
This will give them all the same style.
This is a very powerful feature. It allows you to style any object, not just UI elements. You can style, say, one of your data entities like a "Person" object and when those elements are used visually, like databinding a list of type Person to a ListView, all of them will be styled how you specified, even though they are not natively UI elements. This is how WPF can be "lookless" about its controls.
The WPF TextBox natively makes use of the System Highlight color for painting the background of selected text. I would like to override this and make it consistent since it varies by OS/user theme.
For ListBoxItems, there is a neat trick (see below) where you can override the resource key for the HighlightBrushKey to customize the System Highlight color in a focused setting:
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="LightGreen"/>
</Style.Resources>
</Style>
The same trick does not work for the TextBox unfortunately. Does anyone have any other ideas, besides "override the ControlTemplate"?
NOTE: This behavior appears to be added to WPF 4.
Since .NET 4, TextBoxBase.SelectionBrush
You can specify the brush that highlights selected text by setting the SelectionBrush and SelectionOpacity properties. The SelectionOpacity property specifies the opacity of the SelectionBrush.
eg.
<TextBox SelectionBrush="Red" SelectionOpacity="0.5"
Foreground="Blue" CaretBrush="Blue">
As Steve mentioned : NOTE: This behavior appears to be added to WPF 4.
I bumped into the same problem.
As Dr.WPF says
"It is entirely impossible in the
current .NET releases (3.0 & 3.5
beta). The control is hardcoded to
use the system setting... it doesn't
look at the control template at all."
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/bbffa6e3-2745-4e72-80d0-9cdedeb69f7f/
This is a Windows 8.1 .Net 4.6.1 tested solution to customize the SelectionBrush of each TextBox in the app:
/// Constructor in App.xaml.cs
public App() : base()
{
// Register an additional SelectionChanged handler for appwide each TextBox
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.SelectionChangedEvent, RoutedEventHandler(_textBox_selectionChanged));
}
private void _textBox_selectionChanged(object sender, RoutedEventArgs e)
{
// Customize background color of selected text
(sender as TextBox).SelectionBrush = Brushes.MediumOrchid;
// Customize opacity of background color
(sender as TextBox).SelectionOpacity = 0.5;
}
If you want to include RichTextBox replace type name TextBox 4 times by TextBoxBase.
You can create a Style for the TextBox and write a Setter for the background. The TextBox style should be a default one so that any TextBox which comes under the visual tree will get the changed TextBox
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
Try this:
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="OrangeRed"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>