【问题标题】:WPF ListView with horizontal arrangement of items?具有水平排列项目的WPF ListView?
【发布时间】:2010-11-05 16:58:38
【问题描述】:

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

我不介意这些项目是这样布置的:

1 4 7
2 5 8
3 6 9

或者像这样:

1 2 3
4 5 6
7 8 9

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

我能找到的最接近的是这个问题:

How do I make WPF ListView items repeat horizontally, like a horizontal scrollbar?

仅水平布置项目。

【问题讨论】:

    标签: wpf listview layout


    【解决方案1】:

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

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

    您也可以使用UniformGrid,它将项目布置在一定数量的行或列中。

    我们在 ListView、ListBox 或任何形式的 ItemsControl 中使用这些其他面板来排列项目的方法是更改​​ ItemsPanel 属性。通过设置 ItemsPanel,您可以从 ItemsControls 使用的默认 StackPanel 更改它。使用 WrapPanel,我们还应该将宽度设置为 shown here

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

    【讨论】:

    • 谢谢,我之前尝试过使用 WrapPanel,正如 Dennis 所建议的那样,但是需要 Width 绑定,而我正在使用的示例没有。
    • 显然唯一重要的是宽度。其他是可选的。
    • 当窗口最大化时,WrapPanel 出现了一些问题,因此,我将 Width 更改为 MaxWidth,现在它运行顺利。
    • 有没有办法在这个示例中自动设置项目宽度,使其对应于最大 ListView 项目宽度?
    【解决方案2】:

    我最近研究了如何在 WPF 中实现这一点,并找到了一个很好的解决方案。我想要的是在 Windows 资源管理器中复制 List 模式,即从上到下,然后从左到右。

    基本上,您想要覆盖ListBox.ItemsPanel 属性以使用WrapPanel 并将其方向设置为垂直。

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

    但是,当加载大型数据集时,这很慢,因为它的包装面板没有虚拟化。这个很重要。所以这个任务现在变得有点多了,因为现在您需要通过扩展 VirtualizedPanel 并实现 IScrollInfo 来编写自己的 VirtualizedWrapPanel。

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

    这是我在进行另一项任务之前所做的研究。如果您想了解更多信息或示例,请发表评论。

    更新。 Ben Constable 的 series on how to implement IScrollInfo 很棒。

    共有 4 篇文章。真的很好读。

    我已经实现了一个虚拟化的环绕面板,即使有上述系列文章的帮助,这也不是一件容易的事。

    【讨论】:

    • 源不应该是包裹在 ListBox.ItemsPanel 中的 ItemsPanelTemplate 而不是相反吗?您的版本无法编译。
    • @Sam:你是对的。我必须将该代码直接写入 StackOverflow(即没有检查它)。谢谢你把它捡起来。
    • 啊,我真的不知道如何写这个VirtualizedWrapPanel Ben 的教程有点不清楚何时将代码放在哪里。对我来说,我应该如何实现这个VirtualizedWrapPanel 有点令人困惑。 VirtualizedPanel 到底来自哪里?
    【解决方案3】:

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

            <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 资源管理器的列表选项的不错的模拟

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        除了@Dennis 的回答,关于 WrapPanel 失去虚拟化,我找到了一个很好的类,可以正确实现这一点。虽然 Ben Constable 建议的帖子(Part 1Part 2Part 3Part 4)是一个很好的介绍,但我无法完成 Wrap Panel 的任务。

        这是一个实现: https://virtualwrappanel.codeplex.com/ 我已经用总共 3.300 个视频和照片对其进行了测试,加载列表本身当然有点长,但最终它正确地虚拟化了列表,没有任何滚动延迟。

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

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

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

        【讨论】:

          猜你喜欢
          • 2014-07-20
          • 2012-05-30
          • 2015-12-29
          • 2020-08-27
          • 2012-07-05
          • 2010-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多