其中一个解决方案是创建一个转换器。
例子:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Converters
{
public class WidthToColumnsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values?.Length == 2 && (values[0] is double widthImage) && (values[1] is double widthPanel))
return (int)Math.Floor(widthPanel / widthImage);
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public static WidthToColumnsConverter Instance { get; } = new WidthToColumnsConverter();
}
public class WidthToColumnsConverterExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return WidthToColumnsConverter.Instance;
}
}
}
<Grid>
<FrameworkElement.Resources>
<DataTemplate x:Key="ItemTemplate">
<Image Width="{Binding Value, ElementName=slider}"
Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"
Stretch="Fill"
Source="{Binding}"/>
</DataTemplate>
</FrameworkElement.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="slider"
Minimum="50" Maximum="500" SmallChange="5" TickFrequency="5"
Value="300"/>
<ListView x:Name="MovieListView"
Grid.Row="1"
Grid.RowSpan="3"
ItemTemplate="{DynamicResource ItemTemplate}"
Background="Black"
SelectionChanged="MovieListView_SelectionChanged"
ItemsSource="{Binding Path = movie_posters_list}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid HorizontalAlignment="Center">
<UniformGrid.Columns>
<MultiBinding Converter="{cnvs:WidthToColumnsConverter}">
<Binding Path="Value" ElementName="slider"/>
<Binding Path="ActualWidth" ElementName="MovieListView"/>
</MultiBinding>
</UniformGrid.Columns>
</UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
P.S. 在确定元素的确切面板尺寸时存在细微差别。
在我展示的示例中,在某些情况下,宽度可能会稍大一些,并且会显示一个水平滚动条。
这是因为该示例以父列表框为基础进行计算。
但它除了元素之外,还包含Border和Vertical ScrollBar。
你可以通过这个绑定移除 Border 的影响:
<ItemsPanelTemplate>
<UniformGrid HorizontalAlignment="Center">
<UniformGrid.Columns>
<MultiBinding Converter="{cnvs:WidthToColumnsConverter}">
<Binding Path="Value" ElementName="slider"/>
<Binding Path="ActualWidth"
RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType={x:Type ScrollContentPresenter}}"/>
</MultiBinding>
</UniformGrid.Columns>
</UniformGrid>
</ItemsPanelTemplate>
但是这仍然给出了大小而不考虑垂直滚动条占用的空间。
我没有找到一种简单的方法来通过绑定为元素分配位置。
您可以从宽度中减去一些常数以考虑垂直滚动条的大小。
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values?.Length == 2 && (values[0] is double widthImage) && (values[1] is double widthPanel))
return (int)Math.Floor((widthPanel - 30) / widthImage);
return DependencyProperty.UnsetValue;
}
P.S.S. 转换器通过一个参数传递的值,面板的宽度必须减小。
public class WidthToColumnsConverter : IMultiValueConverter
{
public static readonly DoubleConverter DoubleConverter = (DoubleConverter)TypeDescriptor.GetConverter(typeof(double));
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double param = 0;
if (DoubleConverter.IsValid(parameter))
param = (double)DoubleConverter.ConvertFrom(parameter);
if (values?.Length == 2 && (values[0] is double widthImage) && (values[1] is double widthPanel))
return (int)Math.Floor((widthPanel - param) / widthImage);
return DependencyProperty.UnsetValue;
}
例子:
<ItemsPanelTemplate>
<UniformGrid HorizontalAlignment="Center">
<UniformGrid.Columns>
<!--The parameter value can be changed during the execution (Debug) of the project.
This makes the process of selecting it for a specific layout very convenient.-->
<MultiBinding Converter="{cnvs:WidthToColumnsConverter}"
ConverterParameter="35">
<Binding Path="Value" ElementName="slider"/>
<Binding Path="ActualWidth"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}"/>
</MultiBinding>
</UniformGrid.Columns>
</UniformGrid>
</ItemsPanelTemplate>
补充:
它的运行示例,当滑块一直向右(以最大图像大小)时,它将完全适合 4 列,当我将滑块向左拖动时,它将捕捉到完全适合 5 列的位置视图等等。这可能吗?
如果我对您的理解正确,那么您需要调整图像的大小,以便它们加起来始终填满面板的整个宽度。
如果是这样,这是 UniformGrid 的默认行为。
您只需从元素中删除显式尺寸并将面板的宽度绑定到为其提供的空间。
例子:
<Grid>
<FrameworkElement.Resources>
<DataTemplate x:Key="ItemTemplate">
<Image Stretch="Fill"
Source="{Binding}"/>
</DataTemplate>
<RelativeSource x:Key="ancestorScroll"
Mode="FindAncestor"
AncestorType="{x:Type ScrollContentPresenter}"/>
</FrameworkElement.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider x:Name="slider"
Minimum="50" Maximum="500" SmallChange="5" TickFrequency="5"
Value="300"/>
<ListView x:Name="MovieListView"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Grid.Row="1"
Grid.RowSpan="3"
ItemTemplate="{DynamicResource ItemTemplate}"
Background="Black"
SelectionChanged="MovieListView_SelectionChanged"
ItemsSource="{Binding Path = movie_posters_list}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!--Explicit setting of the Panel width from the size of the parent container.
If necessary, you can add a converter that corrects this value.-->
<UniformGrid HorizontalAlignment="Center"
Width="{Binding ActualWidth,
RelativeSource={StaticResource ancestorScroll}}">
<UniformGrid.Columns>
<!--The parameter value can be changed during the execution (Debug) of the project.
This makes the process of selecting it for a specific layout very convenient.-->
<MultiBinding Converter="{cnvs:WidthToColumnsConverter}"
ConverterParameter="35">
<Binding Path="Value" ElementName="slider"/>
<Binding Path="ActualWidth"
RelativeSource="{StaticResource ancestorScroll}"/>
</MultiBinding>
</UniformGrid.Columns>
</UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</Window>