WPF ListView с горизонтальным расположением элементов?
Я хочу выложить элементы в ListView аналогично WinForms ListView в режиме списка. То есть, где элементы выкладываются не только вертикально, но и горизонтально в ListView.
Я не возражаю, если элементы располагаются так:
1 4 7
2 5 8
3 6 9
или такой:
1 2 3
4 5 6
7 8 9
пока они представлены как вертикально, так и горизонтально для того, чтобы максимально используйте доступное пространство.
ближе всего я мог найти этот вопрос:
Как заставить элементы WPF ListView повторяться по горизонтали, например горизонтальную полосу прокрутки?
, который содержит только элементы, только по горизонтали.
5 ответов:
похоже, что вы ищете это WrapPannel, который будет выкладывать элементы по горизонтали, пока не останется больше места, а затем перейти к следующей строке, например:
( MSDN)
alt-текст http://i.msdn.microsoft.com/Cc295081.b1c415fb-9a32-4a18-aa0b-308fca994ac9(en-us,Expression.10).pngвы также можете использовать UniformGrid, который будет выкладывать элементы в заданном количестве строк или столбцы.
способ получения элементов для arange с помощью этих других панелей в 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>
Я недавно исследовал, как достичь этого в WPF и нашел хорошее решение. То, что я хотел, было реплицировать режим списка в Проводнике Windows, т. е. сверху вниз, а затем слева направо.
в основном то, что вы хотите переопределить
ListBox.ItemsPanel
свойство использовать WrapPanel с его ориентацией по вертикали.этой будет будьте медленны при загрузке большого набора данных, так как панель обертывания не виртуализирована. Это очень важно. Так что это задача теперь становится немного больше, так как теперь вам нужно написать свой собственный VirtualizedWrapPanel путем расширения VirtualizedPanel и реализации IScrollInfo.<ListBox> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Vertical"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox>
public class VirtualizedWrapPanel : VirtualizedPanel, IScrollInfo { // ... }
Это все, что я получил в своих исследованиях, прежде чем перейти к другой задаче. Если вы хотите получить дополнительную информацию или примеры, пожалуйста, прокомментируйте.
обновление. Бен констебль имеет большой серия о том, как реализовать 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>
в дополнение к ответу @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 стиль back-end, так что это внутри 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++; } }