How to apply a style to a class and its descendants?
I want to apply the following style to all controls that derive from ButtonBase
<Style
TargetType="{x:Type ButtonBase}">
<Setter
Property="Cursor"
Value="Hand" />
</Style>
But it only works for a given class, not for its descendants. How to achieve what I intend 开发者_运维知识库to?
This doesn't work because when an element doesn't have a style explicitly assigned, WPF finds its style by calling FindResource
, using the element's type as the key. The fact that you've created a style whose key is ButtonBase
doesn't matter: WPF finds the style with the key of Button
or ToggleButton
and uses it.
An inheritance-based lookup method would look up the style using the element's type, and then use the base type if no style for the element's type was found (and keep on going until it either found a style or hit FrameworkElement
). The problem is that only works if no match is found - i.e. if there's no default style for Button
, which of course there is.
There are two things you can do. One is to do what Jens suggests, using the style's BasedOn
property to implement your own hierarchy of styles. That's kind of annoying, though, because you have to define a style for every single type; if you don't, the default WPF style for that type will be used.
Another way is to use a StyleSelector
that implements this lookup behavior. Like this:
public class InheritanceStyleSelector : StyleSelector
{
public InheritanceStyleSelector()
{
Styles = new Dictionary<object, Style>();
}
public override Style SelectStyle(object item, DependencyObject container)
{
Type t = item.GetType();
while(true)
{
if (Styles.ContainsKey(t))
{
return Styles[t];
}
if (t == typeof(FrameworkElement) || t == typeof(object))
{
return null;
}
t = t.BaseType;
}
}
public Dictionary<object, Style> Styles { get; set; }
}
You can create an instance of this, give it a set of styles, and then attach it to any ItemsControl
:
<Window x:Class="StyleSelectorDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:StyleSelectorDemo="clr-namespace:StyleSelectorDemo" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<StyleSelectorDemo:InheritanceStyleSelector x:Key="Selector">
<StyleSelectorDemo:InheritanceStyleSelector.Styles>
<Style x:Key="{x:Type ButtonBase}">
<Setter Property="ButtonBase.Background"
Value="Red" />
</Style>
<Style x:Key="{x:Type ToggleButton}">
<Setter Property="ToggleButton.Background"
Value="Yellow" />
</Style>
</StyleSelectorDemo:InheritanceStyleSelector.Styles>
</StyleSelectorDemo:InheritanceStyleSelector>
</Window.Resources>
<Grid>
<ItemsControl ItemContainerStyleSelector="{StaticResource Selector}">
<Button>This is a regular Button</Button>
<ToggleButton>This is a ToggleButton.</ToggleButton>
<TextBox>This uses WPF's default style.</TextBox>
</ItemsControl>
</Grid>
</Window>
This indeed seems to be a limitation of the styling system.
Faced with this problem, I declared some base style, and "sub-styled" this for every descendant I cared about.
<Style x:Key="ButtonBaseStyle" TargetType="{x:Type ButtonBase}">
<!-- Style stuff -->
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource ButtonBaseStyle}">
<!-- Additional style stuff for button only -->
</Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource ButtonBaseStyle}">
<!-- Additional style stuff for toggle button only -->
</Style>
<!-- more ButtonBase descendants here -->
精彩评论