简体   繁体   中英

Use a different item controls in WPF ListBox

I have a code like this:

    <ListBox Name="lstBox" 
        ItemsSource="{Binding ViewModelsView}"
        SelectedItem="{Binding SelectedAlertViewOutput, Mode=OneWayToSource}">
        <ListBox.Style>
            <Style TargetType="{x:Type ListBox}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBox}">
                            <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" 
                                    Background="{TemplateBinding Background}" Padding="1" >
                                <ScrollViewer Padding="{TemplateBinding Padding}" >
                                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </ScrollViewer>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemContainerStyle">
                    <Setter.Value>
                        <Style TargetType="{x:Type ListBoxItem}">
                            <Setter Property="IsSelected" 
                                Value="{Binding AlertRecord.IsSelected, Mode=TwoWay}"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                        <Grid HorizontalAlignment="Stretch">
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto"/>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>
                                            <controls:AlertExpander 
                                                Margin="1" 
                                                Value="{Binding AlertRecord.AlertCategory}" 
                                                IsExpanded="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                                IsActive="{Binding AlertRecord.IsActive, Mode=OneWay }"
                                                StartTime="{Binding AlertRecord.Timestamp, Mode=OneWay}"
                                                StopTime="{Binding AlertRecord.EndTimestamp, Mode=OneWay}"
                                                AlertId="{Binding AlertRecord.Id, Mode=OneWay}"
                                            <controls:AlertExpander.Content>
                                            <ContentPresenter>
                                            </ContentPresenter>
                                        </controls:AlertExpander.Content>
                                    </controls:AlertExpander>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
</ListBox.Style>
<ListBox.Resources>
    <DataTemplate DataType="{x:Type local:AlertUnknownViewModel}">
        <local:AlertUnknownView></local:AlertUnknownView>
    </DataTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
    <DataTemplate>
        <Grid Margin="4">
            <ContentPresenter Content="{Binding}"></ContentPresenter>
        </Grid>
    </DataTemplate>
</ListBox.ItemTemplate>

ListBox uses my control AlertExpander as ListBoxItem .

Is it possible to create a list consisting not only of elements of type AlertExpander ? I would like for an element of another type to also use the parameters on which AlertExpander depends.

I have several types of controls that accept the same parameters as AlertExpander , but look different, and I need to display elements of all types in the list, not just AlertExpander .

AlertExpander.xaml:

<Expander 
<Control.Style>
    <Style TargetType="Expander">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Expander">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>

                        <!-- Header -->
                        <ToggleButton Name="HeaderButton">
                            <ToggleButton.Style>
                                <Style TargetType="ToggleButton">
                                    <Setter Property="IsChecked" Value="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"/>
                                    <Setter Property="IsEnabled" Value="{Binding Path=Expandable, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}}"/>
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="ToggleButton">
                                                <Grid>
                                                    <Border Grid.Row="1" BorderThickness="1" BorderBrush="Black"
                                                            CornerRadius="{Binding BorderCornerRadius, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}">
                                                    </Border>

                                                    <!-- Border shade -->
                                                    <Border Name="BorderShade" Grid.Row="1"  BorderThickness="1" BorderBrush="Transparent" Visibility="Collapsed"
                                                    </Border>
                                                    <Grid>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition Height="*"/>
                                                            <RowDefinition Height="*"/>
                                                        </Grid.RowDefinitions>
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="50"/>
                                                            <ColumnDefinition Width="*"/>
                                                        </Grid.ColumnDefinitions>
                                                        <Image Grid.RowSpan="2" Margin="10">
                                                            <Image.OpacityMask>
                                                                <ImageBrush ImageSource="{Binding SelectedItem.IconSource, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}"/>
                                                            </Image.OpacityMask>
                                                            <Image.Source>
                                                                <MultiBinding Mode="OneWay" Converter="{converters:GrayscaleImageSourceConverter}">
                                                                    <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=controls:AlertExpander}"/>
                                                                    <Binding Path="IsActive" RelativeSource="{RelativeSource AncestorType=controls:AlertExpander}"/>
                                                                </MultiBinding>
                                                            </Image.Source>
                                                        </Image>
                                                        <StackPanel x:Name="topHeader" Grid.Row="0" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Bottom">
                                                            </StackPanel>
                                                        <Label x:Name="bottomHeader" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Background="Transparent" Padding="0,2,0,0" FontSize="9pt"
                                                               Content="{Binding AlertHeaderTime, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}">
                                                        </Label>
                                                    </Grid>
                                                </Grid>
                                                <ControlTemplate.Triggers>
                                                    <Trigger Property="IsMouseOver" Value="true">
                                                        <Setter TargetName="BorderShade" Property="Visibility" Value="Visible"/>
                                                    </Trigger>
                                                    <DataTrigger Binding="{Binding IsAlertHeaderTimeShown, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}" Value="false">
                                                        <Setter TargetName="topHeader" Property="VerticalAlignment" Value="Center"/>
                                                        <Setter TargetName="topHeader" Property="Grid.RowSpan" Value="2"/>
                                                        <Setter TargetName="bottomHeader" Property="Visibility" Value="Hidden"/>
                                                    </DataTrigger>
                                                </ControlTemplate.Triggers>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                    <Style.Triggers>
                                        <Trigger Property="IsChecked" Value="true">
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate TargetType="ToggleButton">
                                                        <Grid>
                                                            <Border Grid.Row="1" BorderThickness="1" BorderBrush="Black"
                                                                    CornerRadius="{Binding BorderCornerRadius, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay, Converter={converters:AlertExpanderCornerRadiusTopConverter}}">
                                                                </Border>

                                                            <!-- Border shade -->
                                                            <Border Name="BorderShade" Grid.Row="1"  BorderThickness="1" BorderBrush="Transparent" Visibility="Collapsed"
                                                                    Background="{Binding MouseOverShade, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}"
                                                                    CornerRadius="{Binding BorderCornerRadius, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay, Converter={converters:AlertExpanderCornerRadiusTopConverter}}">
                                                            </Border>
                                                            <Grid>
                                                                <Grid.RowDefinitions>
                                                                    <RowDefinition Height="*"/>
                                                                    <RowDefinition Height="*"/>
                                                                </Grid.RowDefinitions>
                                                                <Grid.ColumnDefinitions>
                                                                    <ColumnDefinition Width="50"/>
                                                                    <ColumnDefinition Width="*"/>
                                                                </Grid.ColumnDefinitions>
                                                                <Image Grid.RowSpan="2" Margin="10">
                                                                    <Image.OpacityMask>
                                                                        <ImageBrush ImageSource="{Binding SelectedItem.IconSource, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}"/>
                                                                    </Image.OpacityMask>
                                                                    <Image.Source>
                                                                        <MultiBinding Mode="OneWay" Converter="{converters:GrayscaleImageSourceConverter}">
                                                                            <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType=controls:AlertExpander}"/>
                                                                            <Binding Path="IsActive" RelativeSource="{RelativeSource AncestorType=controls:AlertExpander}"/>
                                                                        </MultiBinding>
                                                                    </Image.Source>
                                                                </Image>
                                                                <StackPanel x:Name="topHeader" Grid.Row="0" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Bottom">
                                                                    <Label Background="Transparent" Padding="0,0,0,2" FontSize="12pt"
                                                                           FontWeight="{Binding IsActive, Mode=OneWay, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Converter={converters:BoolToFontWeightConverter}}"
                                                                           Content="{Binding AlertHeader, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}">
                                                                        </Label>
                                                                    <Label Background="Transparent" Padding="4,0,0,2" FontSize="12pt"
                                                                           FontWeight="{Binding IsActive, Mode=OneWay, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Converter={converters:BoolToFontWeightConverter}}"
                                                                           Visibility="{Binding IsActive, Mode=OneWay, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Converter={converters:InactiveNoteVisibilityConverter}}"
                                                                           Content="{Binding InactiveNote, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}">
                                                                    </Label>
                                                                </StackPanel>
                                                                <Label x:Name="bottomHeader" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Background="Transparent" Padding="0,2,0,0" FontSize="9pt"
                                                                       Content="{Binding AlertHeaderTime, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}">
                                                                </Label>
                                                            </Grid>
                                                        </Grid>
                                                        <ControlTemplate.Triggers>
                                                            <Trigger Property="IsMouseOver" Value="true">
                                                                <Setter TargetName="BorderShade" Property="Visibility" Value="Visible"/>
                                                            </Trigger>
                                                            <DataTrigger Binding="{Binding IsAlertHeaderTimeShown, RelativeSource={RelativeSource AncestorType=controls:AlertExpander}, Mode=OneWay}" Value="false">
                                                                <Setter TargetName="topHeader" Property="VerticalAlignment" Value="Center"/>
                                                                <Setter TargetName="topHeader" Property="Grid.RowSpan" Value="2"/>
                                                                <Setter TargetName="bottomHeader" Property="Visibility" Value="Hidden"/>
                                                            </DataTrigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </ToggleButton.Style>
                        </ToggleButton>

                        <!-- Content -->
                        <ScrollViewer Name="ContentScrollViewer" Grid.Row="1"
                                      HorizontalScrollBarVisibility="Hidden"
                                      VerticalScrollBarVisibility="Hidden"
                                      HorizontalContentAlignment="Stretch"
                                      VerticalContentAlignment="Bottom"
                                      Visibility="Visible">
                            <Border Name="ExpanderContentBorder" BorderThickness="1,0,1,1" BorderBrush="Black">
                                <ContentPresenter ContentSource="Content"/>
                            </Border>
                        </ScrollViewer>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Control.Style>

enter image description here

Before I get to the code, I want to point out that this is a job for data templates , rather than control templates. The Template property takes a ControlTemplate and is used to define the structure of a control. This is usually used when you are building a control from scratch or significantly altering one. What you want to do is just change how your data is being displayed inside an existing control. For that, there are DataTemplate properties, such as ItemTemplate .

With that out of the way, I put together some code for you that should at least point you in the right direction:

<ListBox Name="lstBox" ItemsSource="{Binding ViewModelsView}" SelectedItem="{Binding SelectedAlertViewOutput, Mode=OneWayToSource}">
    <ListBox.Resources>
        <DataTemplate DataType="local:AlertRecord" x:Key="AlertExpanderItem">
            <controls:AlertExpander Margin="1" 
                                    Value="{Binding AlertCategory}" 
                                    IsExpanded="{Binding IsSelected, Mode=TwoWay}"
                                    IsActive="{Binding IsActive, Mode=OneWay }"
                                    StartTime="{Binding Timestamp, Mode=OneWay}"
                                    StopTime="{Binding EndTimestamp, Mode=OneWay}"
                                    AlertId="{Binding Id, Mode=OneWay}"/>
        </DataTemplate>

        <DataTemplate DataType="local:AlertRecord" x:Key="OtherControlItem">
            <controls:OtherControl Margin="1" 
                                   Value="{Binding AlertCategory}" 
                                   IsExpanded="{Binding IsSelected, Mode=TwoWay}"
                                   IsActive="{Binding IsActive, Mode=OneWay }"
                                   StartTime="{Binding Timestamp, Mode=OneWay}"
                                   StopTime="{Binding EndTimestamp, Mode=OneWay}"
                                   AlertId="{Binding Id, Mode=OneWay}"/>
        </DataTemplate>
    </ListBox.Resources>

    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        </Style>
    </ListBox.ItemContainerStyle>

    <ListBox.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding}">
                <ContentControl.Style>
                    <Style TargetType="ContentControl">
                        <Setter Property="ContentTemplate" Value="{StaticResource OtherControlItem}"/>

                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Value}" Value="0">
                                <Setter Property="ContentTemplate" Value="{StaticResource AlertExpanderItem}"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

...

public class AlertRecord
{
    ...

    public bool HasPositiveValue { get { return Value >= 0 } }

    ...
}

I start by defining two different ItemTemplate s inside inside the Resources of the ListBox . You would define one for each of the different controls you need to use. Notice that I left off " AlertRecord. " from the bindings. That is because each DataTemplate instance will have the AlertRecord as its DataContext .

For the ItemTemplate property, I borrowed from this answer here: https://stackoverflow.com/a/10191762/5086631 . I made a DataTemplate with a ContentControl and used the Style to change the ContentTemplate based on DataTrigger s. This lets you change the template based on the properties of your list item.

Based on your comment, the style is set up so that " OtherControlItem " (the non-expander control) is used by default, and AlertExpanderItem is used when HasPositiveValue returns true. HasPositiveValue is a property you would need to add to AlertRecord .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM