如何覆盖 ListView 的 ContentTemplate 以添加垂直分隔符?
How can I override the ContentTemplate of a ListView to add a vertical separator?
我想要修改 WPF ListView,以便水平呈现项目,并且第一个项目和所有后续项目之间有一个分隔符。像这样:
我有水平位,但我被分隔符卡住了。我尝试使用 DataTemplate,但这会将分隔符合并到实际项目中,这意味着当我悬停时它会突出显示(注意我使用的是 Caliburn,但我认为这不会对问题产生太大影响):
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel Margin="20">
<ListView Name="Items" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl cal:View.Model="{Binding}" />
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
项目是带有此边框的非常基本的视图:
<Border Background="White" BorderBrush="Black" BorderThickness="2">
DataTemplate 的外观:
在花了一上午时间阅读模板后,我决定 ControlTemplate 是解决方案,经过一番努力,我得到了以下代码:
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel Margin="10" Orientation="Horizontal">
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<ListView Name="Items">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
这看起来几乎相同,但现在由于某种原因我无法突出显示项目。我想也许我也覆盖了触发器,并尝试根据这个答案添加 ControlTemplate.Triggers 条目:Change selection-color of WPF ListViewItem 以恢复突出显示行为,但没有成功。
我怎样才能得到我正在寻找的结果,ControlTemplate 是进行此更改的正确位置吗?
编辑:理想情况下,我正在寻找仅使用 xaml.
的解决方案
使用 ItemTemplateSelector 来实现。
如果你能展示你的视图模型,我可以更具体地帮助你。但是您的第一项数据模板将与其余项不同,并且包含您想要的分隔符。其余项目可以使用普通模板。
作为替代方案,将可见性 属性 数据绑定到视图模型中的布尔字段,这仅适用于第一项。
这是带分隔符的自定义面板的快速尝试
public class MyPanel : StackPanel
{
static MyPanel()
{
OrientationProperty.OverrideMetadata(typeof(MyPanel), new FrameworkPropertyMetadata(Orientation.Horizontal));
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (Children.Count >= 2)
{
var child = Children[0] as FrameworkElement;
if (child != null)
dc.DrawLine(new Pen(Brushes.Red, 2), new Point(child.ActualWidth + 2, 5), new Point(child.ActualWidth + 2, ActualHeight - 5));
}
}
}
这样使用
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<local:MyPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
目前面板没有 arrange/measure 子级分配 space 作为分隔符,您必须确保它们之间有一个空白。
我设法弄清楚如何重新添加触发器(我使用我最喜欢的新 WPF 工具 WPF Inspector 确认缺少触发器)。完成后,最后一个问题是分隔符还触发了第一个元素的鼠标悬停事件,我通过定位内部边框来修复该事件:
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel Orientation="Horizontal">
<Border x:Name="ItemBorder" Padding="2">
<StackPanel>
<ContentPresenter />
</StackPanel>
</Border>
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder" Property="Border.Background" Value="Blue" />
<Setter TargetName="ItemBorder" Property="Border.BorderBrush" Value="Green" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=ItemBorder, Path=IsMouseOver}" Value="True">
<Setter TargetName="ItemBorder" Property="Border.Background" Value="Yellow" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<ListView Name="Items">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
由于某些未知原因,ItemBorder 元素需要填充 2,否则无法看到边框。
我想要修改 WPF ListView,以便水平呈现项目,并且第一个项目和所有后续项目之间有一个分隔符。像这样:
我有水平位,但我被分隔符卡住了。我尝试使用 DataTemplate,但这会将分隔符合并到实际项目中,这意味着当我悬停时它会突出显示(注意我使用的是 Caliburn,但我认为这不会对问题产生太大影响):
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel Margin="20">
<ListView Name="Items" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl cal:View.Model="{Binding}" />
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
项目是带有此边框的非常基本的视图:
<Border Background="White" BorderBrush="Black" BorderThickness="2">
DataTemplate 的外观:
在花了一上午时间阅读模板后,我决定 ControlTemplate 是解决方案,经过一番努力,我得到了以下代码:
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel Margin="10" Orientation="Horizontal">
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<ListView Name="Items">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
这看起来几乎相同,但现在由于某种原因我无法突出显示项目。我想也许我也覆盖了触发器,并尝试根据这个答案添加 ControlTemplate.Triggers 条目:Change selection-color of WPF ListViewItem 以恢复突出显示行为,但没有成功。
我怎样才能得到我正在寻找的结果,ControlTemplate 是进行此更改的正确位置吗?
编辑:理想情况下,我正在寻找仅使用 xaml.
的解决方案使用 ItemTemplateSelector 来实现。
如果你能展示你的视图模型,我可以更具体地帮助你。但是您的第一项数据模板将与其余项不同,并且包含您想要的分隔符。其余项目可以使用普通模板。
作为替代方案,将可见性 属性 数据绑定到视图模型中的布尔字段,这仅适用于第一项。
这是带分隔符的自定义面板的快速尝试
public class MyPanel : StackPanel
{
static MyPanel()
{
OrientationProperty.OverrideMetadata(typeof(MyPanel), new FrameworkPropertyMetadata(Orientation.Horizontal));
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (Children.Count >= 2)
{
var child = Children[0] as FrameworkElement;
if (child != null)
dc.DrawLine(new Pen(Brushes.Red, 2), new Point(child.ActualWidth + 2, 5), new Point(child.ActualWidth + 2, ActualHeight - 5));
}
}
}
这样使用
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<local:MyPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
目前面板没有 arrange/measure 子级分配 space 作为分隔符,您必须确保它们之间有一个空白。
我设法弄清楚如何重新添加触发器(我使用我最喜欢的新 WPF 工具 WPF Inspector 确认缺少触发器)。完成后,最后一个问题是分隔符还触发了第一个元素的鼠标悬停事件,我通过定位内部边框来修复该事件:
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<StackPanel Orientation="Horizontal">
<Border x:Name="ItemBorder" Padding="2">
<StackPanel>
<ContentPresenter />
</StackPanel>
</Border>
<Border Name="Separator" Width="2" Margin="5,10" HorizontalAlignment="Center" Background="Red" Visibility="{Binding IsFirst, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder" Property="Border.Background" Value="Blue" />
<Setter TargetName="ItemBorder" Property="Border.BorderBrush" Value="Green" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=ItemBorder, Path=IsMouseOver}" Value="True">
<Setter TargetName="ItemBorder" Property="Border.Background" Value="Yellow" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<ListView Name="Items">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
由于某些未知原因,ItemBorder 元素需要填充 2,否则无法看到边框。