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>
我目前在使用 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 来显示第四列。
这是一个示例,说明我希望它如何显示 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>