WPF ListView с горизонтальным расположением элементов? - PullRequest
54 голосов
/ 25 июня 2009

Я хочу расположить элементы в ListView аналогично WinForms ListView в режиме List. То есть элементы располагаются не только вертикально, но и горизонтально в ListView.

Я не против, если элементы выложены так:

1 4 7
2 5 8
3 6 9

Или вот так:

1 2 3
4 5 6
7 8 9

Пока они представлены как вертикально, так и горизонтально, чтобы максимально использовать доступное пространство.

Самым близким, что я смог найти, был этот вопрос:

Как заставить элементы ListView WPF повторяться по горизонтали, как горизонтальная полоса прокрутки?

Который выкладывает предметы только по горизонтали.

Ответы [ 5 ]

100 голосов
/ 25 июня 2009

Звучит так, как будто вы ищете WrapPannel , который будет располагать элементы горизонтально, пока не останется свободного места, а затем перейдет к следующей строке, например:

( MSDN )
альтернативный текст http://i.msdn.microsoft.com/Cc295081.b1c415fb-9a32-4a18-aa0b-308fca994ac9(en-us,Expression.10).png

Вы также можете использовать UniformGrid , который будет размещать элементы в заданном количестве строк или столбцов.

Способ расположения элементов с помощью этих других панелей в ListView, ListBox или любой форме ItemsControl заключается в изменении свойства ItemsPanel . Установив ItemsPanel, вы можете изменить его со стандартной StackPanel, которая используется ItemsControls. С 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>
21 голосов
/ 25 июня 2009

Я недавно исследовал, как этого добиться в WPF, и нашел хорошее решение. То, что я хотел, было в режиме репликации списка в проводнике Windows, то есть сверху вниз, затем слева направо.

В основном то, что вы хотите сделать, переопределяет свойство ListBox.ItemsPanel, чтобы использовать WrapPanel с его ориентацией, установленной на Вертикаль.

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

Однако это будет медленным при загрузке большого набора данных, так как панель переноса не виртуализирована. Это важно. Так что теперь эта задача становится немного более сложной, так как теперь вам нужно написать свою собственную VirtualizedWrapPanel, расширив VirtualizedPanel и реализовав IScrollInfo.

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

Это все, что я получил в своем исследовании, прежде чем перейти к другому заданию. Если вам нужна дополнительная информация или примеры, пожалуйста, прокомментируйте.

UPDATE . У Бен Констебля есть отличная серия о том, как реализовать IScrollInfo .

Всего 4 статьи. Действительно хорошее чтение.

С тех пор я внедрил виртуализированную панель переноса, это не легкая задача даже с помощью вышеуказанной серии статей.

8 голосов
/ 04 мая 2012

В моем случае лучшим вариантом было использовать:

        <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

2 голосов
/ 09 мая 2016

В дополнение к ответу @ Dennis о потере виртуализации WrapPanel, я нашел хороший класс, который правильно реализует это. Хотя предложенный пост Бен Констебл ( Часть 1 , Часть 2 , Часть 3 , Часть 4 ) является хорошим введением, я не удалось выполнить задачу для панели обтекания.

Вот реализация: 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++;
    }
}
2 голосов
/ 23 июня 2014

слева направо, затем сверху вниз

      <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal" 
                     MaxWidth="{Binding ActualWidth, Mode=OneWay, 
                       RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type er:MainWindow}}}"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...