WPF ListView与项目的横向排列?

我想在列表模式下以类似于WinForms ListView的方式在ListView中布局项目。 也就是说,项目不仅在垂直方向而且在ListView中水平排列。

我不介意这些东西是这样摆放的:

1 4 7
2 5 8
3 6 9

或者像这样:

1 2 3
4 5 6
7 8 9

只要它们在垂直和水平方向呈现,以最大限度地利用可用空间。

我能find的最接近的就是这个问题:

如何使WPF ListView项目水平重复,如水平滚动条?

其中只有横向布局的项目。

这听起来像你正在寻找一个WrapPannel ,这将水平放置的项目,直到没有更多的空间,然后移动到下一行,如下所示:

( MSDN )
替代文字http://i.msdn.microsoft.com/Cc295081.b1c415fb-9a32-4a18-aa0b-308fca994ac9(en-us,Expression.10).png

你也可以使用一个UniformGrid ,它将把这些项目放置在一定数量的行或列中。

在ListView,ListBox或任何forms的ItemsControl中使用这些其他面板来获取项目的方式是通过更改ItemsPanel属性。 通过设置ItemsPanel,你可以从ItemControls使用的默认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中实现这一点,并find了一个很好的解决scheme。 我想要的是复制列表模式在Windows资源pipe理器,即从上到下,然后从左到右。

基本上你想要做的就是重写ListBox.ItemsPanel属性来使用一个WrapPanel,它的方向设置为Vertical。

 <ListBox> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Vertical"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> 

但是,由于包装面板没有被虚拟化,因此在加载大型数据集时这会很慢。 这个很重要。 所以现在这个任务变得更加复杂了,因为现在需要通过扩展VirtualizedPanel和实现IScrollInfo来编写自己的VirtualizedWrapPanel。

 public class VirtualizedWrapPanel : VirtualizedPanel, IScrollInfo { // ... } 

这就像我在进行研究之前,必须要去做另一项任务。 如果您想了解更多信息或示例,请发表评论。

更新 。 Ben Constable的关于如何实现IScrollInfo有一个很好的系列 。

共有4篇文章。 一个非常好的阅读。

我已经实现了一个虚拟化的包装面板,即使在上述系列文章的帮助下,也不是一件容易的事情。

就我而言,最好的select是使用:

  <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资源pipe理器的列表选项

从左到右,然后从上到下使用

  <ListView.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" MaxWidth="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type er:MainWindow}}}"/> </ItemsPanelTemplate> </ListView.ItemsPanel> 

除了@丹尼斯的回答,关于WrapPanel丢失虚拟化,我find了一个很好的类,正确地实现了这一点。 虽然Ben Constable所提出的build议( 第1部分 , 第2部分 , 第3部分 , 第4部分 )是一个很好的介绍,但我无法完成一个包装面板的任务。

这是一个实现: https : //virtualwrappanel.codeplex.com/我testing了总共3.300video和照片,加载列表本身当然有点长,但最终它正确虚拟化列表,没有滚动滞后。

  • 此代码存在一些问题,请参阅上面的页面上的问题标签。

将源代码添加到您的项目后,示例源代码:

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

MVVM风格的后端,所以这是在ViewModel里面:

  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++; } }