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 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.
In one of the apps of mine I have a performance problem I can’t resolve:
The app is built with input controls derived from the TextBox-class, having their own ControlTemplate in Themes\Generic.xaml.
My problem is, that these controls will not be released after they are no more used. If I look at them with SciTech MemoryProfiler, I see that they are hold by an instance of System.Windows.Documents.TextEditor and the TextEditor-instance is hold through the finalizer queue.
Memory profiler attaches a warning to the TextEditor-instance, saying “Instance indirectly rooted by finalizer queue”.
Has anyone an idea what’s going on here? Is it not allowed to derive directly from TextBox? Or have I forgotten something important to implement?
Additional information for the implementation:
The implementation of some of these derived controls is very simple. In class constructor, the DefaultStyleKeyProperty’s metadata is overridden and no event handlers are attached to elements contained in the control template. Something like:
public class MyDerivedTextBox : TextBox{
static MyDerivedTextBox(){
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyDerivedTextBox), new FrameworkPropertyMetadata(typeof(MyDerivedTextBox)));
}
}
The (simplified) style looks something like:
<Style TargetType="{x:Type myApp_controls:MyDerivedTextBox}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="UndoLimit" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type myApp_controls:MyDerivedTextBox }">
<Border Name="Border" ... >
<ScrollViewer Margin="1" x:Name="PART_ContentHost" />
</Border>
</Setter.Value>
</Setter>
</Style>
Finalizer queue
The answer for the question about the finalizer queue is, that the observed effect is not a constant one: The finalization was simply not finished at the point I have analyzed the memory. The problem here is to understand the tool (and the environment) I have used.
Memory Leak
The leak itselfs however was a real problem and it turned out, that it was the same thing I also observed at other positions (in the same app). Bindings to CLR-properties of classes not implementing INotifyPropertyChanged! http://support.microsoft.com/kb/938416/en-us
It was one of my first WPF projects and in the meantime it grew to a huge application. At the time I started, I was not aware, that WPF has problems with bindings mentioned above, and during development I have tried to do as much as possible with data binding but didn't care about the target objects. Now after the app has grown so big and the number of clients has increased dramatically, those memory problems came to light (and led to very strange effects).
After resolving the most problematic bindings, also the effect with the finalizer queue has been decreased drastically. It seems that before, the memory leaks had led to a deferred execution of the object finalization (this is only an assumption, I have not digged deeper into GC-behaviour).
Derived TextBoxes:
I have created a little sample project with such derived text-box controls, using them in some stresstests within memory profiler. As far as I can say from my observation of the test project, derive from TextBoxes as I did works perfectly well.
Fazit
I can only emphasize the importance of checking the target objects of Bindings during application creation. Otherwise, it will be a lot of work to identify the leaking spots in an application. I hope this explanation helps someone not doing the same mistakes as I did.
Not sure if it will change anything, but instead of the static constructor in your control, have you tried something like:
public MyDerivedTextBox()
{
this.DefaultStyleKey = typeof(MyDerivedTextBox);
}
That's the pattern I'm more familiar with. Perhaps the MetaDataOverride is doing something wonky.
As an aside, one thing I did notice with some Silverlight memory issues, was the presence of unexplained AutomationPeers brought on by Tablet PC Input Services (see http://www.wintellect.com/CS/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx).
This question is a follow on from this one...
I am binding to a heterogeneous collection of objects, not all objects have the same set of properties. I am doing this in a datagrid. I would like to gray out the cell if the binding fails. Is there a way to apply a trigger if a binding fails?
EDIT: The answer below was suitable for my purposes, but i followed up with this question because I would like to know how to do it (in a non hack fashion - i do love the hack however, don't get me wrong)
As far as I know, you can't do this directly. However, if you can identify a value that will never be returned from successful bindings, you can create a DataTrigger whose binding has that value as its FallbackValue, and trigger on that same value:
<!-- Hibble returns only positive values -->
<DataTrigger Binding="{Binding Hibble, FallbackValue=-1}" Value="-1">
<Setter Property="Background" Value="Red" />
</DataTrigger>
In theory it might be possible to omit the FallbackValue and trigger on {x:Static DependencyProperty.UnsetValue}, which would be much cleaner, but this doesn't appear to work in practice.
I know I can write a Setter in Silverlight like this:
<Setter Property="Background" Value="{StaticResource MyBrush}" />
However, due to other considerations, I have to write the setter in the format:
<Setter Property="Background">
<Setter.Value>
????? static resource
</Setter.Value>
</Setter>
Any examples I've seen have the value set as the full brush but I want to use an existing resource and I don't know how to write it. Its not just brushes, I'm having trouble with the concept in general trying to work out the right type of property to try and set the value for. Any tips?
Jeff
Any markup extension, denoted by {XXX} can be written as <XXX>. Here, {StaticResource MyBrush} is in fact creating an object of type StaticResourceExtension, passing "MyBrush" as the only argument of its constructor, which itself sets the ResourceKey property of the object. You can achieve the same effect by using:
<StaticResource ResourceKey="MyBrush" />
If you don't know what type a property is, simply set your caret on the property you're interested in and press F1 to open the help in Visual Studio which should provides you with the information you're seeking.