我希望以类似于WinForms ListView列表模式的方式在ListView中布局项目。也就是说,项目不仅垂直排列,还水平排列。
我不介意这样布局:
1 4 7
2 5 8
3 6 9
或者这样布局:
1 2 3
4 5 6
7 8 9
只要它们既垂直又水平地呈现,以最大化利用可用空间。
我找到的最接近的问题是:
如何使WPF ListView项目像水平滚动条一样水平重复?
但是它只能水平排列项目。
我希望以类似于WinForms ListView列表模式的方式在ListView中布局项目。也就是说,项目不仅垂直排列,还水平排列。
我不介意这样布局:
1 4 7
2 5 8
3 6 9
或者这样布局:
1 2 3
4 5 6
7 8 9
只要它们既垂直又水平地呈现,以最大化利用可用空间。
我找到的最接近的问题是:
如何使WPF ListView项目像水平滚动条一样水平重复?
但是它只能水平排列项目。
看起来你需要的是WrapPanel,它会将项目水平放置直到没有更多空间,然后移到下一行,就像这样:
(MSDN)
alt text http://i.msdn.microsoft.com/Cc295081.b1c415fb-9a32-4a18-aa0b-308fca994ac9(en-us,Expression.10).png
你也可以使用UniformGrid,它会按照固定的行数或列数排列项目。
我们可以通过改变ItemsPanel属性来使用这些面板在ListView、ListBox或任何形式的ItemsControl中排列项目。设置ItemsPanel后,可以将它从ItemsControls默认使用的StackPanel更改为其他面板。对于WrapPanel,我们还应该根据此处所示的方式设置宽度。
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
...
</ListView>
最近我研究了如何在WPF中实现这个功能,并找到了一个好的解决方案。我想要的是复制Windows资源管理器中的列表模式,即从上到下,然后从左到右。
基本上,你需要覆盖 ListBox.ItemsPanel
属性,使用一个将其方向设置为垂直的 WrapPanel。
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
但是,当加载大量数据时,使用WrapPanel会很慢,因为它没有虚拟化。这非常重要。所以现在这个任务变得有点复杂了,因为你需要通过扩展VirtualizedPanel并实现IScrollInfo来编写自己的VirtualizedWrapPanel。
public class VirtualizedWrapPanel : VirtualizedPanel, IScrollInfo
{
// ...
}
在我进行研究之前,这就是我所得到的。如果您需要更多信息或示例,请评论。
更新。Ben Constable有一篇关于如何实现IScrollInfo的系列文章,共4篇。非常值得一读。
我已经实现了一个虚拟化的包装面板,即使有上述系列文章的帮助,这也不是一项容易的任务。
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"
MaxHeight="{Binding (FrameworkElement.ActualHeight), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
MinHeight="{Binding ItemHeight, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
这给了我一个相当不错的类似于Windows资源管理器的列表选项
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
MaxWidth="{Binding ActualWidth, Mode=OneWay,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type er:MainWindow}}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
将源代码添加到您的项目后,示例源代码:
<!--in your <Window> or <UserControl> tag -->
<UserControl
xmlns:hw="clr-namespace:Project.Namespace.ToClassFile" >
<!--...-->
<ListView x:Name="lvImages" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="10" Height="auto"
ItemsSource="{Binding ListImages}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<hw:VirtualizingWrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="5" MaxHeight="150">
<TextBlock Text="{Binding title}" FontWeight="Bold"/>
<Image Source="{Binding path, IsAsync=True}" Height="100"/>
<TextBlock Text="{Binding createDate, StringFormat=dd-MM-yyyy}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public ObservableCollection<Media> ListImages
{
get
{
return listImages;
}
set { listImages = value; OnPropertyChanged(); }
}
//Just load the images however you do it, then assign it to above list.
//Below is the class defined that I have used.
public class Media
{
private static int nextMediaId = 1;
public int mediaId { get; }
public string title { get; set; }
public string path { get; set; }
public DateTime createDate { get; set; }
public bool isSelected { get; set; }
public Media()
{
mediaId = nextMediaId;
nextMediaId++;
}
}