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?
CSS provides five special universal property values for controlling inheritance. Every CSS property accepts these values. Sets the property value applied to a selected element to be the same as that of its parent element. Effectively, this "turns on inheritance".
The descendant selector is a way to select elements that are located somewhere underneath other elements, according to the tree structure of the webpage. This selector is actually multiple selectors combined together, but separated by a space.
Inheritance in CSS occurs when an inheritable property is not set on an element. It goes up in its parent chain to set the property value to its parent value. CSS properties such as height , width , border , margin , padding , etc. are not inherited.
A descendant refers to any element that is connected but lower down the document tree - no matter how many levels lower. In the diagram below, all elements that are connected below the <div> element are descendants of that <div>.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With