When overriding DefaultStyleKey is necessary? - wpf

I have just spent like 2 hours trying to figure out why after moving a control from solution A to solution B (along with its style), the control stops showing up (control template was not applied). Turned out I forgot to override default style key. Here is the question: why did it work for solution A?
In DesignerView.cs:
public class DesignerView : Control {
// No DefaultStyleKeyProperty.OverrideMetadata here
}
In Generic.xaml:
<Style TargetType="{x:Type controls:DesignerView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:DesignerView}">
<TextBlock Text="Hello" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Of course, my style is a little bit more complicated than that, but anyhow: exactly the same control (class+style, no proper DefaultStyleKeyProperty set) did show up in solution A, but didn't in solution B.

I guess you are talking about this:
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl),
new FrameworkPropertyMetadata(typeof(MyControl)));
}
Every control needs theme style which define its default template. (Most often for custom controls its defined under Themes\Generic.xaml).
DefaultStyleKeyProperty defines the key used to find the theme style of the control. If you comment out this line, it will pick default template of base class which generally is of type Control. (But since Control does not have any default template defined for it, your control is not shown when you comment out this line)
So, defaultStyleKeyProperty metadata needs to be overriden in static constructor to indicate that its default style is declared under Themes\Generic.xaml.
If you change base class to say Button and you comment out this line, you will see it pick default template of Button class.
So, for your question if your custom control is deriving from
Control, you need to override it to provide default template of control. In case deriving from control
whose default template is already defined then you can avoid
overriding it. It will pick base control style.
That being said for your question
why did it work for solution A?
I suspect you have defined an explicit style somewhere in your
solution A which is missing from Solution B. And Solution B doesn't
have theme style set as well because of no override of metadata.

Related

ContentPresenter Resources not applied when added as LogicalChild

My custom control is derived from ContentControl and has an additional dependency property 'AdditionalContent' of type FrameworkElement.
This property is bound to a ContentPresenter in style that has custom style resources:
<ContentPresenter ContentSource="AdditionalContent">
<ContentPresenter.Resources>
<Style TargetType="{x:Type Button}">
... some setters ...
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
As I learned from other questions here, I have to add this object as logical child of my control by calling AddLogicalChild(AdditionalContent) and overriding LogicalChildren property.
Now, if I use my control like this
<MyControl>
<MyControl.AdditionalContent>
<Button .../>
</MyControl.AdditionalContent>
</MyControl>
The style for Button is not applied. And that's the correct behaviour, because of style inheritance (see this answer). So I have to apply the style in the place where I define the AdditionalContent. So far so good.
But strange behaviour: when I leave out adding the object as logical child, the styles are applied.
Why does this happen? And is there a proper way to provide styles for all contents inside AdditionalContent similar to define Toolbar styles?
It's hard to tell since you have left out much of the button definition, but try setting the style of the button to a dynamic resource with the button type as the resource key.
<Button Style="{DynamicResource {x:Type Button}}"/>
When adding a default style with no resource key like you have done, the implicit key is the data type.
By setting the style to a dynamic resource you are indicating that the resource could change during runtime, which is the case when you are inserting it into the tree at runtime like you are doing.

Place style inside Custom Control

I'm creating the custom control. And suddenly faced a question: why to put control's style separately from the control?
I mean that using the standard way you must: derive from base control (for example, from TextBox) and add a style for it in general.xaml.
But why can't we do just like this:
<TextBox x:Class="CustomTest.CoolTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox.Style>
<Style>
<Setter Property="TextBox.FontSize" Value="20" />
</Style>
</TextBox.Style>
</TextBox>
And code-behind:
public partial class CoolTextBox : TextBox
{
public CoolTextBox()
{
InitializeComponent();
}
}
Update #1
I'm not writing my own library but creating a custom control inside my main executable. Application support themes but they differ only by colors. So each theme is a set of brush resources and my style will refer them using DynamicResource.
What I want to know is the drawbacks of that solution. I mean performance, simplicity of usage and etc.
WPF allows changing themes at runtime, means the style shall be stored separatly from the controls. Futhermore the control shall be lookless when designining in order to other programmers to have their custom styles though somewhere there should be a default style which must be stored separatly in a Generic.xaml file. If your app doesn't support changing themes then you can define the style wherever you wish.
If you are writing a library of custom controls I suggest you to stick to standards.
Here is a link how shall a custom controls be created:
http://wpftutorial.net/HowToCreateACustomControl.html
In addition to dev hedgehog's answer about performance, I found the following drawbacks:
Style object is own for each instance of control. So you end up with number of clones of the same Style object.
You cannot override style using BasedOn property. Only completely replace is possible.

How can I base a style on a default control's style?

We're writing a custom subclass of TextBox where we need to change only some basic parts of the style. Now we know when you subclass Control, normally you're supposed to change the metadata like this...
public class EnhancedTextBox : TextBox {
static EnhancedTextBox() {
// Commenting this line out lets this use the default TextBox style.
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedTextBox),
new FrameworkPropertyMetadata(typeof(EnhancedTextBox)));
}
}
This changes the key used for that subclass type to be the type itself, which means it no longer gets the default TextBox style. No biggie... we just comment that line out and it again works since the key is still set to the value used by TextBox directly.
However, we're wondering if we did want to change a few things in the style, but not the entire style, we could simply create a new style and set its BasedOn property... but what do we set there? We don't want to have to pull out the XAML manually and create a new style, give it a key, then use a StaticResource, but we haven't found out what we could set there to say 'This is based on the TextBox style.
I'm hoping its something simple but again, we haven't found it. Can anyone help?
And just like that... I found it. Man, kicking myself that it was this obvious and I missed it!
<Style TargetType="{x:Type local:EnhancedTextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
....
</Style>
Found it here... WPF style basedon current

inherit a style or find the style programmatically

I have a third party control that I assume gets a style from somewhere.
I have an subclass of that control, where I add an event handler. but now when I replace the old control in xaml with my overrided control, the style gets lost. I assume that its distinguishing between the superclass and subclass when it applies the style. How do I tell it that subclasses, like MyButton:ThirdPartyButton, should have the same style as ThirdPartyButton.
Or is there a programmatic way to see the source of the style like
ThirdPartyButton.GetDefaultStyleLocation();
Define a style in the resources which is implicitly applied:
<Style TargetType="{x:Type local:MySubclass}"
BasedOn="{StaticResource {x:Type thirdParty:Control}}"/>
This is necessary since styles are sadly not inherited.

How to set default WPF Window Style in app.xaml?

I am trying to set the default Style for every window in my WPF Windows application in my app.xaml. So far i have this in app.xaml:
<Application.Resources>
<ResourceDictionary>
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
<Setter Property="Background" Value="Blue" />
</Style>
</ResourceDictionary>
</Application.Resources>
I can get the window to appear with this style when running the app (but not in VS designer) by specifically telling the window to use this style via:
Style="{DynamicResource WindowStyle}
This works, but is not ideal. So how do I:
Have all windows automatically use the style (so i don't have to specify it on every window)?
Have VS designer show the style?
Thanks!
To add on to what Ray says:
For the Styles, you either need to supply a Key/ID or specify a TargetType.
If a FrameworkElement does not have an
explicitly specified Style, it will
always look for a Style resource,
using its own type as the key
- Programming WPF (Sells, Griffith)
If you supply a TargetType, all instances of that type will have the style applied. However derived types will not... it seems. <Style TargetType="{x:Type Window}"> will not work for all your custom derivations/windows. <Style TargetType="{x:Type local:MyWindow}"> will apply to only MyWindow. So the options are
Use a Keyed Style that you specify as the Style property of every window you want to apply the style. The designer will show the styled window.
.
<Application.Resources>
<Style x:Key="MyWindowStyle">
<Setter Property="Control.Background" Value="PaleGreen"/>
<Setter Property="Window.Title" Value="Styled Window"/>
</Style>
</Application.Resources> ...
<Window x:Class="MyNS.MyWindow" Style="{StaticResource MyWindowStyleKey}"> ...
Or you could derive from a custom BaseWindow class (which has its own quirks), where you set the Style property during the Ctor/Initialization/Load stage once. All Derivations would then automatically have the style applied. But the designer won't take notice of your style You need to run your app to see the style being applied.. I'm guessing the designer just runs InitializeComponent (which is auto/designer generated code) so XAML is applied but not custom code-behind.
So I'd say explicitly specified styles are the least work. You can anyways change aspects of the Style centrally.
Know this is years later, but since the question is still up here...
Create a resource dictionary in your project (Right-click the project...)
I'll create a new folder under the Project called "Assets" and
put "resourceDict.XAML in it.
Add the code to resourceDict.XAML:
<Style x:Key="WindowStyle" Target Type="Window" >
<Setter Property="Background" Value="Blue" />
</Style>
In your Project XAML file add the following under Window:
<Window.Resources>
<ResourceDictionary>
<!-- Believe it or not the next line fixes a bug MS acknowledges -->
<Style TargetType="{x:Type Rectangle}" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Assets/resourceDict.XAML" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
</Window.Resources>
ref the following web site: Trouble referencing a Resource Dictionary that contains a Merged Dictionary
"There is a bug: if all your default styles are nested in merged dictionaries three levels deep (or deeper) the top dictionary does not get flagged so the search skips it. The work around is to put a default Style to something, anything, in the root Dictionary."
And it seems to fix things reliably. Go figure...
And finally, under Window, maybe after Title, but before the final Window '>' :
Style="{DynamicResource windowStyle}"
And you'll need to add the code in steps 3 & 4 to every project to which you want the style to apply.
If you wanted to use a gradient background rather than a solid color, add the following code to the resourceDict.XAML:
<LinearGradientBrush x:Key="windowGradientBackground" StartPoint="0,0"
EndPoint="0,1" >
<GradientStop Color= "AliceBlue" Offset="0" />
<GradientStop Color= "Blue" Offset=".75" />
</LinearGradientBrush>
And modify your Style Setter for the background color to read:
<Setter Property="Background" Value="{DynamicResource
windowGradientBackground}" />
Steps 3 & 4 need to be repeated in each project.XAML file as described above, but hey, you get uniform Windows across the Solution! And the same process could apply to any controls you want to have a uniform look as well, buttons, whatever.
For anyone coming into this late, hope this helps as I'm sure the original posters got this all figured out years ago.
Paul
The designer is not working because you're specifying a DynamicResource. Please change this to StaticResource and all will be well.
To apply to all windows, you should remove the x:Key from the style. Setting the TargetType implicitly sets the x:Key to whatever is in TargetType. However, in my tests, this is not working, so I am looking into it.
If I set the TargetType to x:Type TextBlock, the designer works perfectly, it just seems to be the Window that is showing different behaviour.
You can add this code to your App.xaml.cs file:
FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata
{
DefaultValue = Application.Current.FindResource(typeof(Window))
});
After this, the style applied to the Window type will also apply to all types derived from Window
I investigated this one for some days now and made it work through the Constructor of my custom Window Class:
public class KWindow : Window
{
public KWindow()
{
this.SetResourceReference(StyleProperty, typeof(KWindow));
}
static KWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(KWindow), new FrameworkPropertyMetadata(typeof(KWindow)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// gets called finally
}
}
Hope it helps someone
For those this struggling with a solution to the problem: How can I have a custom style be automtically applied to all my Window derived types? Below is the solution I came up with
NOTE: I really didn't want to derive from the Window type or have to insert XAML on each window to force a style update etc. for reasons specific to my project (consumers of my product us my generic reusable style library and create their own layout/windows etc.) so I was really motivated to figure a solution out that worked that I was willing to live with any side effects
Need to iterate through all the instantiated windows and simply force them to use the new custom style you have defined for the Window type. This works great for windows that are already up but when a window or child window is instantiated it won't know to use the new/custom type that has been declared for its base type; the vanilla Window type. So the best I could come up with was to use the LostKeyBoardFocus on the MainWindow for when it loses Focus to a ChildWindow (IOW When a child window has been created) and then invoke this FixupWindowDerivedTypes().
If someone has a better solution for "detecting" when any kind of window derived type is instantiated and thus call the FixupWindowDerivedTypes() that would be great. There may be something useful with handling the WM_WINDOWPOSCHANGING in this area as well.
So this solution is not elegant per say but gets the job done without me having to touch any code or XAML related to my windows.
public static void FixupWindowDerivedTypes()
{
foreach (Window window in Application.Current.Windows)
{
//May look strange but kindly inform each of your window derived types to actually use the default style for the window type
window.SetResourceReference(FrameworkElement.StyleProperty, DefaultStyleKeyRetriever.GetDefaultStyleKey(window));
}
}
}
}
//Great little post here from Jafa to retrieve a protected property like DefaultStyleKey without using reflection.
http://themechanicalbride.blogspot.com/2008/11/protected-dependency-properties-are-not.html
//Helper class to retrieve a protected property so we can set it
internal class DefaultStyleKeyRetriever : Control
{
/// <summary>
/// This method retrieves the default style key of a control.
/// </summary>
/// <param name="control">The control to retrieve the default style key
/// from.</param>
/// <returns>The default style key of the control.</returns>
public static object GetDefaultStyleKey(Control control)
{
return control.GetValue(Control.DefaultStyleKeyProperty);
}
}
Considering Gishu's answer I figured out one more workaround. But it could be little bit weird.
If you use MVVM pattern you could remove code-behind of your window and x:Class markup in XAML file. So you will get an instance of window or your custom window but not a some instance of 'MainWindow' class that is derived from 'Window' class and marked as partial.
I'm making VS-like window so I had to inherit window class and extend it functionality. In that case it will be possible to make new window class as partial that would allow us to make code-behind without inheritance.
you will save all the styles in one xaml file(example design.xaml)
and then call that (design.xaml) xaml file in all the pages like this way
Like:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Design.xaml"/>
</ResourceDictionary.MergedDictionaries>

Resources