列表框项目中选定项目和普通项目的不同项目模板
我想显示一个列表框,其项目在被选择时将显示一个按钮,该按钮对所选数据项目执行特定操作。为此,我使用两个 DataTemplates,一个是 NormalTemplate(没有按钮),另一个是 SelectedTemplate(有一个按钮,其 Tag 属性绑定到数据,即在按钮单击事件处理程序中使用)。当选择列表框中的项目时,我想分配 SelectedTemplate。
为此,我使用了自定义 ControlTemplate,它具有 VisualStateManager,它根据 VisualState 选择适当的模板(即,选定和未选定)。此解决方案的问题是,每次需要使用不同的 DataTemplate 时,我都必须创建新的 ControlTemplate。我正在尝试找到一个解决方案,您可以在其中指定正常和特定的模板。选择项目并使用通用代码根据视觉状态更改数据模板。
下面是我的未选择和未选择的数据模板选定的项目:
<DataTemplate x:Key="NormalItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="8,0,0,3" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontFamily="Arial" FontSize="14"/>
<TextBlock Text="{Binding Description}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="SelectedItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="65"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="8,0,0,3">
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontFamily="Arial" FontSize="14"/>
<TextBlock Text="{Binding Description}" />
</StackPanel>
<Border Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Stretch">
<Button Content="Process" Tag={Binding} OnClick="Process_Clicked" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
</DataTemplate>
下面是定义自定义 ControlTemplate 的样式,它使用 VisualStateManager 显示或隐藏相应的 ContentPresenter,其 ContentTemplate 分配给 NormalItemTemplate & SelectedItemTemplate
<Style TargetType="ListBoxItem" x:Key="ActiveGamesItemContainerStyle">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UnSelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UnSelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter
x:Name="SelectedContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{StaticResource NormalItemTemplate}"
HorizontalAlignment="Stretch"
Margin="{TemplateBinding Padding}"
Visibility="Collapsed"/>
<ContentPresenter
x:Name="UnSelectedContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{StaticResource SelectedItemTemplate}"
HorizontalAlignment="Stretch"
Margin="{TemplateBinding Padding}"
Visibility="Visible"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
此解决方案的问题是:
对于每个需要使用不同 DataTemplate 的新 ListBoxItem,我需要使用 ControlTemplate 创建一个样式,该样式与上面相同,但只需要更改 ContentPresenter 的 ContentTemplate。因此,有很多重复的代码/XAML。
由于我使用的是 SelectedItemTemplate 中的按钮,因此我需要在定义 Click 事件处理程序的同一 UserControl 类中定义样式。如果 UserControl 使用多个 ListBox,则将为每个 ListBox 声明一个巨大的样式定义。
我尝试使用附加属性来解决这个问题,但它不起作用。因为,我无法获取 Selected ListBoxItem (而是获取绑定数据)。这个想法是获取 ListBoxItem 的 ContentPresenter 并将其 ContentTemplate 设置为 SelectedItemTemplate。
有没有更好的方法来做到这一点?
谢谢&问候, 苏尼尔
I want to display a ListBox whose item when selected will display a button which performs specific action on the selected data item. For this, I use two DataTemplates one is NormalTemplate(which has no button) and the other is SelectedTemplate (which has a button whose Tag property is bound to data, which is used in button click event handler). When an item in Listbox is selected I want to assign the SelectedTemplate.
For this I used custom ControlTemplate which has VisualStateManager which selects appropriate Template based on the VisualState (i.e., Selected & Unselected). The problem with this solution is I've to create new ControlTemplate every time I need to use different DataTemplates. I'm trying to find a solution where you specify the templates for normal & selected items and use common code for changing the datatemplate based on the visualstate.
Below is my DataTemplate for Unselected & Selected items:
<DataTemplate x:Key="NormalItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="8,0,0,3" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontFamily="Arial" FontSize="14"/>
<TextBlock Text="{Binding Description}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="SelectedItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="65"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="8,0,0,3">
<TextBlock Text="{Binding Name}" FontWeight="Bold" FontFamily="Arial" FontSize="14"/>
<TextBlock Text="{Binding Description}" />
</StackPanel>
<Border Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Stretch">
<Button Content="Process" Tag={Binding} OnClick="Process_Clicked" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
</DataTemplate>
Below is the style which defines custom ControlTemplate which uses VisualStateManager to display or hide appropriate ContentPresenters whose ContentTemplate is assigned to NormalItemTemplate & SelectedItemTemplate
<Style TargetType="ListBoxItem" x:Key="ActiveGamesItemContainerStyle">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UnSelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UnSelectedContentPresenter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter
x:Name="SelectedContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{StaticResource NormalItemTemplate}"
HorizontalAlignment="Stretch"
Margin="{TemplateBinding Padding}"
Visibility="Collapsed"/>
<ContentPresenter
x:Name="UnSelectedContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{StaticResource SelectedItemTemplate}"
HorizontalAlignment="Stretch"
Margin="{TemplateBinding Padding}"
Visibility="Visible"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problems with this solution are:
For every new ListBoxItem which needs to use different DataTemplates I need to create a style with ControlTemplate which is same as the above but just need to change the ContentPresenter's ContentTemplate. Thereby, lot of duplicate code/XAML.
Since, I'm using the button in the SelectedItemTemplate, I need to define the styles in the same UserControl class where the Click eventhandler is defined. If the UserControl uses more than one ListBox then there will be a huge Style definition declared for every ListBoxes.
I tried to solve this problem using attached properties but its not working. As, I'm not able to get hold of Selected ListBoxItem (instead I'm getting the bound data). The idea was to get hold of the ListBoxItem get its ContentPresenter and set it's ContentTemplate to the SelectedItemTemplate.
Is there any better way to do this?
Thanks & Regards,
Sunil
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
无需两个单独的模板,只需使用一个模板并使用
DataTrigger
切换可见性即可。为您的项目创建一个具有
IsSelected
属性的视图模型。添加 NotifyPropertyChanged 事件。设置
ItemContainerStyle
以将ListBoxItem.IsSelected
属性绑定到视图模型上的IsSelected
属性。然后在您的
DataTemplate
中有一个DataTrigger
,当IsSelected
为时,它会更改按钮的
。Visibility
>正确有关如何执行上述操作的示例,请查看答案这个 StackOverflow 问题。
No need for two separate templates, simply use the one and switch the visibility using a
DataTrigger
.Create a view model of your item that has an
IsSelected
property. Add NotifyPropertyChanged events.Set the
ItemContainerStyle
to bind theListBoxItem.IsSelected
property to yourIsSelected
property on your viewmodel.Then in your
DataTemplate
have aDataTrigger
that changes theVisibility
of the button whenIsSelected
isTrue
.For an example of how do the above, take a look at the answer to this StackOverflow question.