Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 868
Jader Dias Avatar asked Nov 25 '10 15:11

Jader Dias


People also ask

Can CSS classes inherit?

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".

What is descendant selectors in CSS?

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.

How does inheritance work in a cascading style sheet?

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.

What is a descendant in HTML?

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>.


1 Answers

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>
like image 172
Robert Rossney Avatar answered Oct 05 '22 10:10

Robert Rossney