如何覆盖ListView的ContentTemplate以添加垂直分隔符?

4
我想修改一个WPF ListView,使项目水平呈现,并在第一项和所有后续项之间添加分隔符。类似于这样:Horizontal list with first item separated。我已经实现了水平排列的部分,但是卡在了分隔符上。我尝试使用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条目(更改WPF ListViewItem的选择颜色),以恢复突出显示行为,但没有成功。

我如何获得我正在寻找的结果,控件模板是否是进行此更改的正确位置?

编辑:理想情况下,我希望使用仅限XAML的解决方案。


1
那个分隔符更像是容器中的一部分,而不是 ListViewItem 本身。你现在正在使用水平的 StackPanel,尝试编写自己的 HorizontalPanelWithSeparator - Sinatr
我会尝试一下,谢谢。 - Jamey
3个回答

2
这是一个快速尝试自定义面板与分隔符。
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>

目前面板不能安排/测量子元素以为分隔符分配空间,您需要确保它们之间有间距。


谢谢您的帮助,稍微调整了一下边距后,我成功实现了想要的效果。理想情况下,我正在寻找一个仅限于XAML的解决方案,所以我将保持问题的开放并继续尝试,但如果没有其他更好的方法,我会接受这个答案。 - Jamey

0

我成功地找出了如何重新添加触发器(使用我最喜欢的 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的填充,否则边框将无法显示。


0

使用ItemTemplateSelector来实现这个功能。

如果您可以展示您的视图模型,我可以更具体地帮助您。但是您的第一个项目的数据模板将与其余项目不同,并包含您想要的分隔符。其余项目可以使用普通模板。

作为替代方案,将可见性属性的数据绑定到视图模型中的布尔字段,该字段仅对第一个项目为真。


嗨,Tanveer,感谢您的回答,不幸的是可见性不是我的问题。我正在努力使分隔符在我将鼠标悬停在第一项上时不包含在突出显示框中(如我第二张图片所示)。至于ViewModels,ItemViewModel只有一个boolean IsFirst属性,而ItemsViewModel只包含一个List<ItemViewModel>属性,在构造函数中使用3个项目进行初始化(将true传递给第一个和false传递给后两个)。 - Jamey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接