Need multiple styles dependent on Listboxitem

2019-09-05 05:55发布

问题:

i have a Listbox, which stores two different object types, based on the same baseclass. (e.g. BaseObject = baseclass and the children of it: CustomPath and CustomImage)

The Datasource:

ObservableCollection<BattlegroundBaseObject> _baseObjectCollection;
public ObservableCollection<BattlegroundBaseObject> BaseObjectCollection
{
    get { return _baseObjectCollection?? (_baseObjectCollection= new ObservableCollection<BaseObject>()); }
} 

The Listbox databinding: <ListBox ItemsSource="{Binding BaseObjectCollection}"

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem" x:Name="ListBoxPathLineStyle">
        <Setter Property="Template">

            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem" x:Name="BattlegroundObjectControlTemplate">

                    <Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}">
                    </Path>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter Property="Effect" TargetName="PathLine">
                                <Setter.Value>
                                    <DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.ItemContainerStyle>

I want to add to the ControlTemplate where the Path is, also a Image and to differ it by type or a property. doesnt matter.

anyone any ideas?

回答1:

You can add to ListBox resources DataTemplate for each type.

In my example classes Car and Motorbike derived from Vehicle class.

<ListBox x:Name="listBox">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:Car}">
            <StackPanel Background="Red">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:Motorbike}">
            <StackPanel Background="Orange">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>

EDIT:

You can add style for ListBoxItem to resources:

<ListBox x:Name="listBox">
    <ListBox.Resources>
        <Style TargetType="ListBoxItem">
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="true">
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
        <DataTemplate DataType="{x:Type local:Car}">
            <StackPanel Background="Red">                       
                <TextBlock Text="{Binding Name}" />                        
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:Motorbike}">
            <StackPanel Background="Orange">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>


回答2:

You could define some DataTemplates for your different classes. They determine how the classes are displayed. I've been using them to display derived classes differently when working with a collection of the base class.

<DataTemplate DataType="{x:Type CustomPath}">
    <TextBlock Text="This is a CustomPath"/>
</DataTemplate>

<DataTemplate DataType="{x:Type CustomImage}">
    <TextBlock Text="This is a CustomImage"/>
</DataTemplate>


回答3:

At the moment you are changing the style of the controls that WPF is using to render your bound data. A better way to do this is to provide WPF with a way of generating the correct controls. Ignore the ListBoxItem and use DataTemplates for your actual objects.

First you need to tell the Window or control how to find your types.

<Window or UserControl
...
    xmlns:model="clr-namespace:yourNamespace"
>

Then you can provide WPF with a way to show your objects e.g.

<DataTemplate TargetType="{x:Type model:CustomPath}">
  <Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" 
    Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}"/>
  <!-- maybe use a binding from the Path.Effect back to the IsSelected and ValueConverters 
    to re-apply the selection effect-->
</DataTemplate>

<DataTemplate TargetType="{x:Type model:CustomImage}">
  <Image Src="{Binding SomeProperty}" />
</DataTemplate>

Now all you need to do is to make these available to the ListBox in some way. Almost every element in WPF can have .Resources added to it, so you could choose to do these across the entire window

<Window ...>
  <Window.Resources>
     <DataTemplate .../>
     <DataTemplate .../>
  </Window.Resources>
  ...
  <ListBox .../>
</Window>

or you can apply it more locally

<Window ...>
  ...
  <ListBox>
    <ListBox.Resources>
       <DataTemplate .../>
       <DataTemplate .../>
    </ListBox.Resources>
  </ListBox>
</Window>

And this way your listbox definition can become much neater too, e.g. if you are using Window.Resources

<ListBox ItemsSource="{Binding BaseObjectCollection}"/>