WPF:如何正确使用控件大小的多个列表视图?

WPF: How can I use multiple list views in a control size correctly?

这是我现在的XAML:

<Window x:Class="GridDemo.SubWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:GridDemo"
    d:DataContext="{d:DesignInstance local:ViewModel, IsDesignTimeCreatable=True}"
    mc:Ignorable="d"
    Title="Window" Width="300">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView
        ItemsSource="{Binding Animals}"
        SelectedItem="{Binding SelectedAnimal}"
        Grid.Row="0"/>
    <Button
        Command="{Binding AddAnimalCommand}"
        Content="Add Animal"
        HorizontalAlignment="Right"
        Grid.Row="1"/>
    <ListView
        ItemsSource="{Binding Vegetables}"
        SelectedItem="{Binding SelectedVegetable}"
        Grid.Row="2"/>
    <Button
        Command="{Binding AddVegetableCommand}"
        Content="Add Vegetable"
        HorizontalAlignment="Right"
        Grid.Row="3"/>
</Grid>

当我运行时,它显示:

马上就有问题,它使用了太多的垂直 space。

我喜欢并想保留的一件事是,如果我将它缩小到太小而无法完整显示两个列表视图,列表框会缩小并自动添加滚动条,但无论如何按钮都会保持可见.

但主要问题发生在列表视图大小不同时。他们总是会使用一半可用的 space 给他们。假设我有 10 只动物和 5 种蔬菜,我的 window 的高度足以容纳 15 件物品。我希望蔬菜列表视图仅根据需要请求 space,让动物列表视图请求所有剩余的 space。相反,两个列表视图都要求剩余的 50%space,导致动物列表视图太小而蔬菜列表视图太大。

我认为我想要的是列表视图行在有足够空间时表现得像 Height="Auto",而在没有足够空间时表现得像 Height="*"。

为了获得您想要的效果,我会这样做:

<Window x:Class="GridDemo.SubWindow" x:Name="win"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GridDemo"
        d:DataContext="{d:DesignInstance local:ViewModel, IsDesignTimeCreatable=True}"
        mc:Ignorable="d"
        Title="Window" Width="300">
    <Grid x:Name="grd">
        <Grid.RowDefinitions>
            <RowDefinition>
                <RowDefinition.Height>
                    <MultiBinding Converter="{StaticResource gpc}">
                        <MultiBinding.Bindings>
                            <Binding Path="Animals" />
                            <Binding ElementName="win" Path="ActualHeight" />
                        </MultiBinding.Bindings>
                    </MultiBinding>
                </RowDefinition.Height>
            </RowDefinition>
            <RowDefinition Height="Auto"/>
            <RowDefinition>
                <RowDefinition.Height>
                    <MultiBinding Converter="{StaticResource gpc}">
                        <MultiBinding.Bindings>
                            <Binding Path="Vegetables" />
                            <Binding ElementName="win" Path="ActualHeight" />
                        </MultiBinding.Bindings>
                    </MultiBinding>
                </RowDefinition.Height>
            </RowDefinition>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListView
            ItemsSource="{Binding Animals}"
            SelectedItem="{Binding SelectedAnimal}"
            Grid.Row="0"/>
        <Button
            Command="{Binding AddAnimalCommand}"
            Content="Add Animal"
            HorizontalAlignment="Right"
            Grid.Row="1"/>
        <ListView
            ItemsSource="{Binding Vegetables}"
            SelectedItem="{Binding SelectedVegetable}"
            Grid.Row="2"/>
        <Button
            Command="{Binding AddVegetableCommand}"
            Content="Add Vegetable"
            HorizontalAlignment="Right"
            Grid.Row="3"/>
    </Grid>
</Window>

我使用了 IMultiValueConverter 的实现来生成行高比例。它在一个资源字典中,键为"gpc",实现如下:

public class GridProportionConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Count() == 2 && values[0] is ICollection && values[1] is double && ((ICollection)values[0]).Count > 0)
        {
            ICollection collection = (ICollection)values[0];
            double windowHeight = (double)values[1];

            if (windowHeight < 350)
            {
                return new GridLength(1, GridUnitType.Star);
            }

            return new GridLength(collection.Count, GridUnitType.Star);
        }

        return new GridLength(1, GridUnitType.Star);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我已经使用 ICollection 假设你有某种 ObservableCollection<T> 用于你的 collections。

我发现这很好用,但显然它取决于一件事,即每个 ListViewItem 在每个 ListView 中的高度大致相同,否则您可能需要适应高度差异。

我也放了一点fail-safe,如果window的高度低于某个值,比例就变成1:1.

如果比例失控,例如 300:1,您始终可以同时传入 collections 并据此计算更合适的比例,例如,您可以决定 4:1 是您可以容忍的最大差异,如果它变得更大,您会默认使用它。