I would like to make a change to the PART_EditableTextBox of the default wpf combobox (change the background for example).
I tried adding a style like this:
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<TextBox x:Name="PART_EditableTextBox" Background="AntiqueWhite"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But that causes the rest of the default template to be ignored.
Is there a way I can just override a specific property of the PART_EditableTextBox or do I have to copy over the entire control template and make my change in there?
Some different ways to change the look of the Control...
Copying a Controls Template, Editing it and Using it in a Style
When copying and modify the Template of a control...you have to bear in mind 1 thing...themes.
The control might have completely different Template designs depending on the theme (i.e. different chrome)...and so your problem is...which template do you choose to copy, modify and then use on your control.
No matter, which one you choose...you have a problem...when someone is running Windows in a theme that is different to the theme from which you copied the Template from...well that control will look wrong/out of place.
To see how different Templates can look like in different themes...use ShowMeTheTemplate:
http://www.sellsbrothers.com/posts/details/2091
So to do it properly, you would have to copy and modify the template for each theme (Classic, Luna, Aero, Royale, etc) and do the necessary steps to get your different themed template to get loaded when the theme changes...so that your control is "theme aware".
When overriding WPF templates do I have to override each theme's template separately?
http://windowsclient.net/blogs/nidonocu/archive/2008/02/16/wpf-themes-and-control-libraries.aspx
http://blogs.windowsclient.net/nidonocu/archive/2008/03/03/wpf-themes-and-control-libraries-part-2.aspx
Modifying the Visual tree After Template has been applied at runtime
If you were the author of the control or you create a derived version of a control...then you can wait till the template is applied and then in OnApplyTemplate...you can then hunt for the "Part" in the visual tree, and then modify the visual tree/changes attributes at runtime (i.e. you could change the Background of PART_EditableTextBox).
However, this doesn't work if you are relying on implicit styles (as you are), or don't want to or can't replace all your controls with the derived version in your XAML.
Obtaining a Copy of the ControlTemplate at runtime, and modifying the "Part"
There is another possibility....obtaining the ControlTemplate for a control at runtime...(which will be for the current theme set at the time)....modifying it, then setting it onto the Control.
The beauty of this is if there are new themes in the system for which you didn't have prior knowledge of their names (and thus didn't design a Template for it), then instead of your control Template being picked up from Generic theme (and thus being out of place)....you have a better chance for the look to fit in better with the new theme. But it is a bit of a hack.
https://siderite.dev/blog/cloning-wpf-controltemplate.html
Define a WPF ControlTemplate at runtime
Using 'BasedOn' will only overwrite the properties you specify in your new style.
However, in your case, the property you are overwriting is the Template. This is the entire template for the combobox, because that is the property you are trying to modify in your style.
To overwrite just part of it, you have to copy over the entire control template and make your change there; just like you thought.
If you don't have Expression Blend to retrieve the entire control template, you can find them on MSDN.
Related
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.
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.
I have read lots of thing about WPF Theme, Skin, Style etc... But there is still something I cannot achieve.
I have custom controls, which are styled depending on the OS theme, by having a different style in each of the theme file (Aero.NormalColor.xaml, Luna.NormalColor.xaml or Aero2.NormalColor.xaml), this work like a charm.
I don't load/force any theme in my App.xaml, each controls (like buttons) keep there style depending on the OS theme.
So I see XP buttons on XP, Win7 buttons on windows 7 and Win8 buttons on Windows 8.
I also have ResourceDictionaries which are loaded in the App.xaml that contains "named" (explicit x:Key) styles for different normal wpf controls. They look like this:
<Style x:Key="BlackComboBox" TargetType="{x:Type ComboBox}"></Style>
and I use them like this
<ComboBox Style="{StaticResource BlackComboBox}"></ComboBox>
So for now, my BlackComboBox is the same on every Windows (XP/7/8).
What I try to achieve is to have a different Style for these normal Controls depending on the OS theme, without having to subclass the Control (I think it will be overkill to have a subclass for each control that will need an OS specific them), so BlackComboBox could be different on each OS.
I have already tried to put a style with the same key in a theme file, but this doesn't seem to work.
I have thought about loading at runtime a different ResourceDictionary containing the style for the desired OS version:
But it looks like an ugly solution.
I don't like having to check for System.Environment.OSVersion.
And it will not be theme dependant, but OS dependent.
For me the best way seems to be able to have "named" style in a Theme file that kind of overrides the one in the ResourceDictionaries.
Thanks for the help!
I believe the only way to do this would be to create resource dictionaries for each theme the same you would if you created a custom control and wanted to have a different look for each theme. Then you would create a Style in each for the ComboBox and provide a ResourceKey derived class (e.g. ComponentResourceKey) as the x:Key for the Style using the same value for the x:Key in each theme's resource dictionary. Then when you reference the Style you would use a DynamicResource to that ResourceKey.
So a simplified example would be to create a new WpfApplication (e.g. I named its WpfResourceKeys). In this case I'm going to put the theme resource dictionaries in the main assembly so I go into the AssemblyInfo.cs and set the ThemeInfo's 1st parameter (i.e. the themeDictionaryLocation) to SourceAssembly.
Then create a folder named "themes" and in it create a resource dictionary for each theme you want to support.E.g. aero.normalcolor.xaml, aero2.normalcolor.xaml, luna.normalcolor.xaml, classic.xaml, etc..
In each ResourceDictionary define a Style for ComboBox or whatever control you want and give it an x:Key of the same ResourceKey. The easiest thing to use is ComponentResourceKey. In my case I'll use a TextBox since I'll just set the Background and that will be honored regardless of the template defined for each theme. E.g.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfResourceKeys"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox"
x:Key="{ComponentResourceKey
ResourceId=Foo,
TypeInTargetAssembly={x:Type local:MainWindow}}">
<Setter Property="Background" Value="Purple" />
</Style>
</ResourceDictionary>
In my case I just put this into each theme xaml file but with a different value for the Background setter to test it out. So in my aero2.normalcolor.xaml the setter value was Purple and in the classic.xaml the setter value was Orange. When I run my test in Windows 8 with the default theme the TextBox is purple but if I switch to one of the high contrast themes the TextBox is Orange.
Then in the place you are going to reference it you would use a DynamicResource instead of a StaticResource since you won't be defining the Style within the resources of the window or app.xaml (because you want the framework to locate it considering the OS theme).
<Window x:Class="WpfResourceKeys.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfResourceKeys"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Style="{DynamicResource ResourceKey={ComponentResourceKey
ResourceId=Foo,
TypeInTargetAssembly={x:Type local:MainWindow}}}" Text="ABC" />
</Grid>
You just need to make sure you use an equivalent resource key to how you define it in the theme dictionaries. In the case of ComponentResourceKey that means the ResourceId and TypeInTargetAssembly are equivalent.
I've create a style for ListItems that I want to use across all ListBoxes in my application. I can set these manually like so:
<ListBox ItemContainerStyle="">
But I'm having trouble getting the style to apply to every single ListBox in my application without touching each one and adding the above attribute.
In addition, and more importantly, I want to apply the style to list boxes used within custom templated controls. Right now I have to modify the Generic.xaml theme in the control library... not something i think I should have to do.
Fairly certain this has something to do with themes, btu having a heck of a time figuring it out.
You can do this with implicit styles in Silverlight 4.
Define your style in the regular way:
<Style x:Key="DefaultListBoxStyle" TargetType="ListBox">
....
<Style>
then create the implicit style:
<Style TargetType="ListBox"
BasedOn="{StaticResource DefaultListBoxStyle}" />
you could use implicit styles.
http://www.silverlightshow.net/items/Implicit-Styles-in-Silverlight-4.aspx
You define one global style for a type (in your case ListBoxItem) and then this style is the new default style for your app.
If you need any further information, just leave a comment.
BR,
TJ
I really like WPF because of its awesome skinning support by changing resourcedictionaries on the fly, but the catch is, the styles must be made by designers in XAML. My client needs a skinnable UI where the end users can make skins themselves. My question is -
In Photoshop, you can take any image, and add a color overlay to change all the colors to that hue. Can you do something similar in WPF? I'm just a beginner, and looking at several WPF styles, it seems like all the color values are hard-coded.
Here's a sample scenario - user selects two colors from color pickers, and all the controls have a gradient background from Color1 to Color2.
EDIT: Can the colors be saved to a XML file and loaded again too?
The key is to realize that a Style can contain a DynamicResource or a Binding, so if your style is:
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{DynamicResource UserSelectedBackground}" />
...
</Style>
anything you set as a "UserSelectedBackground" resource will be applied to all buttons.
Alternatively you might bind to a view model object:
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding ButtonBackground, Source={x:Static my:SkinModel.Instance}" />
...
</Style>
Now whenever ButtonBackground in your SkinModel instance changes, all button backgrounds will automatically update. (This assumes your SkinModel uses DependencyProperties or implements INotifyPropertyChanged.)
To allow the user to separately control the two ends of a gradient fill, create two SolidColorBrush properties in your SkinModel which are bound from two-way by the color pickers. Whenever these properties change, recompute the ButtonBackground property (either in the PropertyChangedCallback of a DependencyProperty or in the setter of a CLR property).
Saving your state to the file is trivial: Just use XamlWriter to serialize your SkinModel to XAML, then write it to the file. To load it later, just use XamlReader.Parse.
You could store the color values in XML/DataBase (sqllite might be a good fit) and put them into a class that the controls will bind to. That way you can use a colorpicker for the user to change these data.