xamlinheritancescopeavaloniaui

Avalonia UI style scope propagation


I'm having some issues in defining styles of my items. Or actually in the scope of the styles I set. My question is how do I make sure, that a style I'm setting is not propagated for the elements of the same type, that are enclosed in the top-level type?

Let me give you a short example of my issue.

Assume we have a following class, containing a name of some person and a list of cars they own:

public class Person
{
    public string Name { get; set; } = string.Empty;

    public ObservableCollection<string> Cars { get; set; } = [];
}

In my view model I create an observable collection, filled in in place (for the purpose of this example):

public ObservableCollection<Person> People { get; set; } = 
    [
        new Person() { Name = "Kristoforo Truitt", Cars = [ "Lexus", "GMC", "Dodge", "Hyundai", ] },
        new Person() { Name = "Phil Gladwell", Cars = [ "Lexus", "Chevrolet", "Lexus", "Subaru", ] },
        new Person() { Name = "Giacobo Commusso", Cars = [ "Isuzu", "Suzuki", "Mitsubishi", "Subaru", ] },
        new Person() { Name = "Christina Ramsbottom", Cars = [ "Ford", "GMC", "Volkswagen", "Chevrolet", ] },
        new Person() { Name = "Karl Cochern", Cars = [ "Mercury", "Toyota", "Ford", "Mitsubishi", ] },
    ];

Now, in my AXAML I'm putting the following ListBox displaying all persons and all their cars next to their name:

<ListBox ItemsSource="{Binding People}" Background="Transparent" Classes="PeopleList">
    <ListBox.Styles>
        <Style Selector="ListBox.PeopleList ListBoxItem:nth-child(2n)">
            <Setter Property="Background" Value="#EFEFEF" />
        </Style>
    </ListBox.Styles>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <ListBox ItemsSource="{Binding Cars}" Background="Transparent" Margin="10,0" Classes="CarsList">
                    <ListBox.Styles>
                        <Style Selector="ListBox.CarsList ListBoxItem">
                            <Setter Property="Margin" Value="0" />
                            <Setter Property="Padding" Value="0" />
                        </Style>
                    </ListBox.Styles>
                </ListBox>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

To give some separation between the items (person) I'm defining a style, where every second item has a background defined: <Style Selector="ListBox.PeopleList ListBoxItem:nth-child(2n)">.... Even though I've tried to have this style applied to the "outer" ListBox (classes, etc.), this it what I'm getting:

ListBoxes inherit their style, not wanted

As you can see, also the cars listed have their background set (every second one).

What I would like to achieve is this:

ListBox style is applied only to the outer one, no inheritance

So my question is - how do I limit the scope of the style of the outer ListBox only to itself, and the inner ListBox does not inherit it?


Solution

  • Here is the explanation why ListBox.PeopleList > ListBoxItem:nth-child(2n) works!

    According to the avalonia ui documentation style selector syntax:

    > is Child Operator, in your case, ListBoxItem is the direct children of ListBox in the logical tree, ListBox.PeopleList > ListBoxItem:nth-child(2n) means "only ListBoxItem that is the direct children of ListBox with PeopleList class" is selected, so only outer one matches.

    A child selector is defined by separating two selectors with a > character. This selector matches only direct children in the logical controls tree.

    On the other hand, a space is Any Descendant Operator, both outer and inner ListBoxItem in your case match.

    When two selectors are separated by a space, then the selector will match any descendants in the logical tree. The parent is on the left, and the descendant is on the right.