MaterialDesginInXamlToolkit - 垂直对齐 ListViewItem 内容

MaterialDesginInXamlToolkit - Vertically align ListViewItem content

对所有内容使用默认的边框,我无法让心形图标在此列表视图中垂直居中。 要重现该问题,只需创建一个超级简单的全新 WPF 应用程序和 add MaterialDesginInXamlToolkit ResourceDictionarys to App.xaml,然后添加以下代码:

MainWindow.xaml

<Window
    x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp1"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    Background="{DynamicResource MaterialDesignPaper}"
    FontFamily="{DynamicResource MaterialDesignFont}"
    TextElement.FontSize="13"
    TextElement.FontWeight="Regular"
    TextElement.Foreground="{DynamicResource MaterialDesignBody}"
    TextOptions.TextFormattingMode="Ideal"
    TextOptions.TextRenderingMode="Auto"
    mc:Ignorable="d">
    <Window.Resources>
        <local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Window.Resources>
    <ListView
        x:Name="listview"
        VerticalContentAlignment="Center"
        VirtualizingPanel.IsVirtualizing="True"
        VirtualizingPanel.VirtualizationMode="Recycling">
        <ListView.View>
            <GridView>
                <GridViewColumn Width="Auto">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Path
                                Width="{Binding Height, Mode=OneWay, RelativeSource={RelativeSource Self}}"
                                Height="12"
                                VerticalAlignment="Center"
                                Fill="Red"
                                Stretch="Uniform"
                                Stroke="White"
                                StrokeThickness="1"
                                Visibility="{Binding IsLoved, ConverterParameter=True, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
                                <Path.Data>
                                    <PathGeometry Figures="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
                                </Path.Data>
                            </Path>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Width="330" Header="Title">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();

            var items = new List<Item>();
            items.Add(new Item { IsLoved = true, Title = "This is a test" });
            items.Add(new Item { IsLoved = false, Title = "This is a test" });
            items.Add(new Item { IsLoved = true, Title = "This is a test" });
            items.Add(new Item { IsLoved = false, Title = "This is a test" });
            items.Add(new Item { IsLoved = true, Title = "This is a test" });

            this.listview.ItemsSource = items;
        }
    }
    public class Item
    {
        public bool IsLoved { get; set; }
        public string Title { get; set; }
    }
    public class BooleanToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            bool isVisible;

            try
            {
                isVisible = System.Convert.ToBoolean(value);
            }
            catch
            {
                return DependencyProperty.UnsetValue;
            }

            var falseVisibility = parameter == null
                ? Visibility.Collapsed
                : Visibility.Hidden;

            return isVisible
                ? Visibility.Visible
                : falseVisibility;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return DependencyProperty.UnsetValue;
        }
    }
}

Material Design 的默认 ListViewItem 样式保留 ListViewItem.VerticalContentAlignment 未更改其默认值 Top,而不是使用父 ListView 设置的值。我们可以这样解决:

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem" BasedOn="{StaticResource MaterialDesignGridViewItem}">
        <Setter 
            Property="VerticalContentAlignment" 
            Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType=ListView}}" 
            />
    </Style>
</ListView.ItemContainerStyle>

如果您有需要垂直填充行的项目(例如边框、彩色背景),请将其设为 Stretch 而不是 Center。在这种情况下,您必须在所有模板中显式设置 VerticalAlignment="Center",就像您现在对路径所做的那样。

MaterialDesignGridViewItem是一种风格defined here

line 155 we find the VerticalContentAlignment of the GridViewRowPresenter which presents the row content. It has a TemplateBinding which uses the VerticalContentAlignment defined for the ListViewItem. That Style has no Setter for that property, so it will just use the default value of Control.VerticalContentAlignment 上,即 VerticalAlignment.Topdefault(VerticalAlignment) 恰好是 Top

    <GridViewRowPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />