[英]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>
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
.我首先在ListBox
的Resources
中定义两个不同的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
来更改基于DataTrigger
的ContentTemplate
。 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.