ListView 中的动态 Re-Sizing 个图像

Dynamically Re-Sizing Images in a ListView

我目前在使用 DataTemplate 将图像放入 WrapPanel 然后放入 ListView 的 ListView 中调整图像大小时遇到​​问题。 这是程序的图片:

我目前使用这个作为我的数据模板:

<DataTemplate x:Key="ItemTemplate">
        <WrapPanel>
            <Image
                Width="300"
                Height="300" 
                Stretch="Fill" 
                Source="{Binding}"/>
        </WrapPanel>
    </DataTemplate>

然后我在我的 ListView 中使用这个数据模板:

<ListView Grid.Row="0" Grid.RowSpan="3"
              Name="MovieListView"
              ItemTemplate="{StaticResource ItemTemplate}"
              ItemsSource="{Binding Path = movie_posters_list}"
              Background="Black"
              SelectionChanged="MovieListView_SelectionChanged">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid  Columns="6" HorizontalAlignment="Center"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
    </ListView>

这是填充 ListView 源代码的背后代码:

public void LoadImages()
    {
        List<String> filenames = new List<String>(System.IO.Directory.EnumerateFiles(movie_poster_path, "*.jpg"));

        foreach (String filename in filenames)
        {
            this.movie_posters_list.Add(new BitmapImage(new Uri(filename)));
            Console.WriteLine("filename " + filename);
            Match regex_match = Regex.Match(filename.Trim(), regex_pattern);
        }
        MovieListView.ItemsSource = movie_posters_list;
    }

所以目前,我可以在 XAML 中编辑数据模板并更改图像的宽度和高度,它会在我加载程序时反映出来。 我还可以更改 UniformGrid 中的列数,它会在我加载程序时反映出来。 有什么方法可以在代码隐藏中动态更改这些值,以便我可以滑动滑块来更改 UniformGrid 中列的大小和数量?

问题补充: 这里我有 3 列,两边都是黑色 space,因为没有足够的 space 来显示第四列。
我想尝试调整逻辑,以便当我有 3 列时,它将调整图像大小,以便 3 列将水平填充可用的 space。这个 window 永远不会调整大小,因为我在全屏模式下拥有它。

这是一个示例,说明我希望它如何显示 4 列。

如果我将滑块向下拖动一点,它将显示 4 列,这是我不想要的。

所以基本上我希望所有图像始终填满屏幕上的最大值 space,我希望滑块在有足够 space 以适应下一个时捕捉图片列。

它的功能示例,当滑块一直向右移动时(在最大图像尺寸下)它将完全适合 4 列,因为我将滑块向左拖动它会捕捉到完全适合 5 的位置视图中的列等。 这可能吗?

我想要显示的视觉效果:

最小列数:4

最大列数:11

当我移动滑块时,我希望它只允许图像显示如下图:

4 列:

5 列:

6 列:

7 列:

8 列:

9 列:

10 列:

11 列:

解决方案之一是创建转换器。

示例:

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. 在确定元素的确切面板尺寸时存在细微差别。
在我展示的示例中,在某些情况下,width 中的元素可能会采用稍大的尺寸,并且将显示水平的 ScrollBar。
这是因为示例以Parent ListBox为计算基础。
但是它除了元素之外,还包含了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>

但是这样还是给出了尺寸,没有考虑到Vertical ScrollBar占用的space。 我没有找到一种简单的方法来通过绑定为元素分配位置。 您可以从宽度中减去一些常量以考虑 Vertical ScrollBar 的大小。

        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>

补充:

Example of it functioning, When slider is all the way to the right (at the max image size) it will perfectly fit 4 columns as I drag the slider to the left it will snap to the position that perfectly fits 5 columns in the view and so on. Is this possible?

如果我的理解正确,那么您需要调整图像的大小,使它们加起来始终填满面板的整个宽度。
如果是这样,这就是 UniformGrid 的默认行为。
您只需要从元素中删除显式尺寸并将面板的宽度绑定到为其提供的 space。

示例:

    <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>