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}" />
I have this code:
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="{x:Static SystemParameters.CaptionHeight}"/>
</Setter.Value>
</Setter>
The application starts correctly, but it's verbose.
I tried to inline my code, so I wrote this:
<Setter Property="WindowChrome.WindowChrome"
Value="{x:Static SystemParameters.CaptionHeight}"/>
But now if I run the application, it doesn't start. Why?
The property is of type WindowChrome, so it expects values of type WindowChrome.
In the first case, it happens well. Also, you give to the WindowChrome instance a value of the correct type for its property CaptionHeight.
In the second case, you're trying to assign to the WindowChrome property a value of a totally different type (the type of CaptionHeight).
Now, if in your application there is only one single instance of WindowChrome, you can declare it as a StaticResource:
<App.Resources>
<WindowChrome x:Key="WindowChromeResource"
CaptionHeight="{x:Static SystemParameters.CaptionHeight}"/>
</WindowChrome>
</App.Resources>
And then call it every time you need:
<Setter Property="WindowChrome.WindowChrome"
Value="{StaticResource WindowChromeResource}"/>
If instead you need a dynamic number of different instances, it's definitely not possible to do this inline.
Many developers had claimed about the WPF verbosity many times before you, but the WPF team has never improved that aspect. See this and this for wider discussion.
I want to make a style for fields of XamDataGrid to set the AllowEdit property of the fields in the converter using some conditions.
I am trying with this but not working for me.
<Style TargetType="{x:Type igWPF:Field}">
<Style.Setters>
<Setter Property="AllowEdit" Value="{Binding Path=Field, Source={RelativeSource Self}, Converter={StaticResource FieldsEditableConverter}}">
</Setter>
</Style.Setters>
</Style>
Please suggest some code.
Infragistics said : "Once WPF Styles are in use, you cannot modify them."
Since I was looking for slightly the same thing (XamDataGrid, Field, Style, Setter modification of just access setter programmatically) but I wasn't able to. I've found the following information :
This exception has nothing to do with Infragistics controls. Once WPF Styles are in use, you cannot modify them. If you want to programmatically create Styles, you should create them from scratch, and not try to modify existing Style objects.
http://www.infragistics.com/community/forums/t/13909.aspx
Despite the fact that you can not modify it dynamically, so, on my specific case, I finally used x:Name of Style used by the targeted setter when required : so just before it has to modify the list (CanExecute).
This is how my code looks like :
C# :
var cm = uc.Resources["fiContextMenu"] as ContextMenu;
foreach(MenuItem mi in cm.Items)
if(mi.Header.Equals("Remove File"))
mi.IsEnabled = false; //Disallow remove when empty list
Xaml :
<igDP:FieldSettings.EditorStyle>
<Style TargetType="{x:Type igEditors:XamComboEditor}">
<Setter Property="ContextMenu" Value="{StaticResource fiContextMenu}" />
I wrote a style for button. Unfortunately, I encountered a problem: font size of the button depends on a value, which I can compute only in the view (specifically, that's DisplayInformation stuff).
Ideally, I would do the following:
<Style x:key="MyStyle" BasedOn="{StaticResource SomeStyle}" TargetType="Button">
<Setter Property="FontSize" Value="{Binding ElementName=rootControl, Path=SomeProperty" />
</Style>
Then, I would simply provide necessary properties in view class. But that simply doesn't work (does nothing, no messages, no errors).
How can I solve this problem?
Assigning a binding to a setter value is not supported in the Windows Runtime. (It might be supported in WPF and Silverlight 5, however).
If you google around, there are workarounds, but they're kind of hacky. See this, which uses attached properties and a binding helper class.
Having such Style
<Style TargetType="TreeViewItem">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Setter Property="Header" Value="Pink"></Setter>
</Trigger>
</Style.Triggers>
</Style>
I would expect the text of the expanded TreeViewItems to be "Pink", but nothing set actually. If I change to Property="Background" instead, it works. How Header differs from Background?
I think I'll need more info to answer this more completely. However, if I have to guess, I'd say you're probably setting the Header property on the TreeViewItem explicitly like this:
<TreeView>
<TreeViewItem
Header="Blue"/>
</TreeView>
And, in this case, setting it explicitly will override anything that you put in the style.
To elaborate on ascalonx's answer:
copied from Josh Smith's blog:
There is a well-defined set of rules
which is used internally by WPF to
figure out what the real value of a DP
is. Here is a brief summary of the
rules of precedence used when
resolving the value of a DP (from
highest to lowest priority):
Property system coercion
Active animations, or animations
with a Hold behavior
Local value
TemplatedParent template
Style triggers
Template triggers
Style setters
Theme style
Inheritance
Default value from dependency
property metadata
So if you explicitly set the Header property, (or if you bind it I think), you have that problem.