【问题标题】:Use a different item controls in WPF ListBox在 WPF ListBox 中使用不同的项目控件
【发布时间】:2020-04-27 13:38:28
【问题描述】:

我有这样的代码:

    <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 使用我的控制 AlertExpander 作为 ListBoxItem

是否可以创建一个不仅包含AlertExpander 类型元素的列表?我希望另一种类型的元素也使用 AlertExpander 所依赖的参数。

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

【问题讨论】:

  • ItemsSource 中为每个类型定义一个DataTemplate,而不是创建一个ItemContainerStyle?

标签: c# wpf xaml


【解决方案1】:

在开始编写代码之前,我想指出这是data templates 的工作,而不是控制模板。 Template 属性采用ControlTemplate 并用于定义控件的结构。这通常在您从头开始构建控件或显着更改控件时使用。 想要做的只是改变你的数据在现有控件中的显示方式。为此,有DataTemplate 属性,例如ItemTemplate

除此之外,我为您整理了一些代码,至少应该为您指明正确的方向:

<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 } }

    ...
}

我首先在ListBoxResources 内部定义两个不同的ItemTemplates。您将为需要使用的每个不同控件定义一个。请注意,我从绑定中删除了“AlertRecord.”。这是因为每个DataTemplate 实例都将AlertRecord 作为其DataContext

对于ItemTemplate 属性,我在这里借用了这个答案:https://stackoverflow.com/a/10191762/5086631。我用ContentControl 制作了DataTemplate,并使用Style 根据DataTriggers 更改ContentTemplate。这使您可以根据列表项的属性更改模板。

根据您的评论,样式设置为默认使用“OtherControlItem”(非扩展控件),当HasPositiveValue 返回true 时使用AlertExpanderItemHasPositiveValue 是您需要添加到 AlertRecord 的属性。

【讨论】:

  • 感谢您的快速回复! AlertRecord 包含几个参数,其中之一对于在列表中选择所需控件至关重要。 ViewModelsView 是包含 AlertRecord 的集合。我想使用不同的控件在列表中显示。也就是说,如果 AlertRecord.Value> = 0,则假定将使用 AlertExpander 控件(基于 Expander),在其他情况下,将使用与 AlertExpander 相同参数的另一个控件(不基于 Expander)。
  • @DmitryAbt 好的,我已经基于此更新了我的答案。如果您有任何问题,请告诉我。
  • 基思·斯坦,感谢您的回答!还有一个问题 - 早些时候我有这样一个部分(见下一条评论),其中确定了扩展器的内容。现在该放在哪里?现在,当展开扩展器时,只有一个标题。
  • @DmitryAbt 什么内容应该进入扩展器?您能否编辑您的原始问题以包含图片?
猜你喜欢
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多