3
votes

A few months ago in a C# application I began using a ListBox as a container for some UserPanels. When a panel was selected it was highlighted, just like any listbox item. I found the following XAML I was able to add to give all items a transparent background (Not sure where I originally found this or I'd link it)

<Application.Resources>
    <Style TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="border" Background="Transparent">
                        <ContentPresenter />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter TargetName="border" Property="Background">
                                <Setter.Value>Transparent</Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

This worked fine but now I need to sometimes have a background color instead of a transparent background. There is only 1 listbox and the contents change so I want to programatically change the style in the codebehind.

I found examples on changing the style in code but I wasn't able to create two of this same style. I gave it a x:name="transparentListbox", copied it, and gave the other one x:name="normalListbox" with a background of Blue, but I get an XML parse exception having two style elements, possibly because they're both generically trying to modify every listbox.

How can I have two named styles that accomplish the same thing (modifying the background when an item is selected) that I can switch between in code as needed?


Edit:

In every case my listbox is used to store UserPanels. I add them to the listbox using lstPanels.Items.Add(p) where p is an instance of a class derived from UserPanel.

When I first made the app there were multiple windows so the windows that required transparency had this style, and those that required selecting items did not. Managing multiple windows became cumbersome so it was re-factored into a single window and the listbox would be cleared and loaded with different types of panels when the mode changed. Some still required transparent backgrounds, but now some did not.

Programatically assigning a named style to the Listbox as a whole, when the mode changes, would be okay. Assigning a style to every ListBoxItem would involve updates to a lot of code as that functionality is spread out.

Perhaps the solution would be to maintain a single style but have the background property be bound to a varaible, if that is at all possible?

3

3 Answers

2
votes

I have always created the style in my UserControl.Resources section.

In your case:

<Style x:Key="ListBoxStyle1" TargetType="MyDerivedListBoxItem">

...

<Style x:Key="ListBoxStyle2" TargetType="MyDerivedListBoxItem">

...

and in the code behind I have set the styles this way.

If Not MyListBox.ItemContainerStyle.Equals(CType(Resources("ListBoxStyle1"), Style)) Then
                    MyListBox.ItemContainerStyle= CType(Resources("ListBoxStyle1"), Style)
1
votes

I wouldn't mess with trying to modify styles in your code. It may be feasible and even work, but that seems like a world of pain to me. One idea that might help is that you could inherit from ListBoxItem, defining your own control. You could then put a dependency property on that inheritor like UseAlternateBackgroundColor of type bool or something.

Then, you'd modify your style:

<Style TargetType="MyDerivedListBoxItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MyDerivedListBoxItem}">
                <Border x:Name="border" Background="Transparent">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter TargetName="border" Property="Background">
                            <Setter.Value>Transparent</Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="UseAlternateBackgroundColor" Value="true">
                        <Setter TargetName="border" Property="Background">
                            <Setter.Value>Black</Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Generally speaking, I personally try to avoid dealing with layout and visual style considerations in code as much as possible.

1
votes

You need to set different x:Key directives, if the styles are resources the Name is pretty useless anyway. If you do not set a key the TargetType is used as the key instead which causes a collision. To apply one of the styles in code you can call FindResource with the respective key.