在 ListView 中动态调整图像大小

Posted

技术标签:

【中文标题】在 ListView 中动态调整图像大小【英文标题】:Dynamically Re-Sizing Images in a ListView 【发布时间】:2021-10-12 09:38:57 【问题描述】:

我目前在使用 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 列,两边都有黑色空间,因为没有足够的空间来显示第四列。 我想尝试调整逻辑,以便当我有 3 列时,它会向上调整图像大小,以便 3 列将水平填充可用空间。这个窗口永远不会像我全屏一样调整大小。

这是一个示例,我希望它显示为 4 列。

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

所以基本上我希望所有图像始终填满屏幕上的最大空间,我希望滑块在有足够空间容纳下一列图像时捕捉。

它的运行示例,当滑块一直向右(以最大图像大小)时,它将完全适合 4 列,当我将滑块向左拖动时,它将捕捉到完全适合 5 列的位置视图等等。 这可能吗?

我想展示的视觉效果:

最少列数:4

最大列数:11

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

4 列:

5 列:

6 列:

7 列:

8 列:

9 列:

10 列:

11 列:

【问题讨论】:

编辑:有什么方法可以在后面的代码中动态更改这些值,以便我可以滑动滑块来更改图像的大小和 UniformGrid 中的列数? 【参考方案1】:

其中一个解决方案是创建一个转换器。

例子:

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>

【讨论】:

这看起来很简单,我今晚回家后试试这个!如果它有效,你就是一个救生员! 我用应用代码的一些细微差别补充了我的答案。阅读本补充材料。 我刚刚应用了代码,效果很好!!!当滑块设置为某些值时,我仍然会弹出水平滚动条。我将使用这段代码,看看我是否能让它显示得更干净一些,这对我帮助很大!谢谢!我会告诉你我的想法! 从宽度中减去常数。我在答案中的补充代码中显示了如何执行此操作。该示例使用常量 30 - 这对我来说已经足够了。也许你需要将它增加到 35 ... 40。你也可以通过转换器参数传递它,以免更改每个值的转换器代码。 我将这种转换器的代码添加到我的答案中。

以上是关于在 ListView 中动态调整图像大小的主要内容,如果未能解决你的问题,请参考以下文章

如何在css中动态调整图像大小?

PHP图像动态调整大小和圆角图像

PHP图像动态调整大小与存储调整大小的图像

将图像动态加载到覆盖的画布中并调整它们的大小

在 CodeIgniter 中动态调整图像大小而不使用 CI 图像处理函数

磁盘上的图像文件大小(KB)随着Java中动态图像大小的调整(减小大小)而增加