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 是您可以容忍的最大差异,如果它变得更大,您会默认使用它。
这是我现在的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 是您可以容忍的最大差异,如果它变得更大,您会默认使用它。