简体   繁体   English

在 WPF 列表框中使用不同的项目控件

[英]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 . ListBox使用我的控件AlertExpander作为ListBoxItem

Is it possible to create a list consisting not only of elements of type AlertExpander ?是否可以创建一个不仅包含AlertExpander类型元素的列表? I would like for an element of another type to also use the parameters on which AlertExpander depends.我希望另一种类型的元素也使用AlertExpander所依赖的参数。

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相同的参数,但看起来不同,我需要在列表中显示所有类型的元素,而不仅仅是AlertExpander

AlertExpander.xaml: 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. Template属性采用ControlTemplate并用于定义控件的结构。 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 .为此,有DataTemplate属性,例如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 .我首先在ListBoxResources中定义两个不同的ItemTemplate You would define one for each of the different controls you need to use.您将为需要使用的每个不同控件定义一个。 Notice that I left off " AlertRecord. " from the bindings.请注意,我从绑定中删除了“ AlertRecord. ”。 That is because each DataTemplate instance will have the AlertRecord as its DataContext .这是因为每个DataTemplate实例都将AlertRecord作为其DataContext

For the ItemTemplate property, I borrowed from this answer here: https://stackoverflow.com/a/10191762/5086631 .对于ItemTemplate属性,我在这里借用了这个答案: 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.我用ContentControl制作了一个DataTemplate并使用Style来更改基于DataTriggerContentTemplate 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.根据您的评论,样式设置为默认使用“ OtherControlItem ”(非扩展控件),并在AlertExpanderItem返回 true 时使用HasPositiveValue HasPositiveValue is a property you would need to add to AlertRecord . HasPositiveValue是您需要添加到AlertRecord的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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