В WPF ListBox с более чем 1000 элементами изображения увеличенные изображения становятся медленными
Я столкнулся с проблемой при разработке приложения для просмотра фотографий.
Я использую ListBox для отображения изображений, который содержится в ObservableCollection.
Я связываю ListBox ItemsSource с ObservableCollection.
<DataTemplate DataType="{x:Type modeldata:ImageInfo}">
<Image
Margin="6"
Source="{Binding Thumbnail}"
Width="{Binding ZoomBarWidth.Width, Source={StaticResource zoombarmanager}}"
Height="{Binding ZoomBarWidth.Width, Source={StaticResource zoombarmanager}}"/>
</DataTemplate>
<Grid DataContext="{StaticResource imageinfolder}">
<ScrollViewer
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<ListBox Name="PhotosListBox"
IsSynchronizedWithCurrentItem="True"
Style="{StaticResource PhotoListBoxStyle}"
Margin="5"
SelectionMode="Extended"
ItemsSource="{Binding}"
/>
</ScrollViewer>
Я также привязываю Image'height в ListBox со слайдером (значение ползунка также привязывается к zoombarmanager.ZoomBarWidth.Width).
Но я обнаружил, что коллекция станет больше, например: содержит более 1000 изображений. Если я использую ползунок для изменения размера iamges, он становится немного медленным.
Мой вопрос есть.
1. Почему он становится медленным? стараясь, чтобы он увеличивал все изображения, или просто потому, что уведомление ( "Ширина" ) вызывается более 1000 раз.
2. Есть ли способ решить эту проблему и сделать ее быстрее.
PhotoListBoxStyle выглядит следующим образом:
<Style~~ TargetType="{x:Type ListBox}" x:Key="PhotoListBoxStyle">
<Setter Property="Foreground" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}" >
<WrapPanel
Margin="5"
IsItemsHost="True"
Orientation="Horizontal"
VerticalAlignment="Top"
HorizontalAlignment="Stretch" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style~~>
Но если я использую стиль выше, я должен использовать ScrollViewer вне ListBox, иначе я понятия не имею, как получить плавный прокручивающийся скроллер, а в оболочке нет никакого скроллера по умолчанию. Кто-нибудь поможет? Говорят, что listbox с scrollviewer имеет низкую производительность.
Ответы
Ответ 1
Проблема в том, что ваша новая панель макета - WrapPanel, и она не поддерживает виртуализацию! Возможно создать собственный виртуализированный WrapPanel... Подробнее здесь
Также читайте больше о других проблемах, таких как реализация IScrollInfo здесь
Я также настоятельно рекомендую вам не создавать новый шаблон управления только для замены панели макета... Скорее сделайте следующее:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Преимущество этого заключается в том, что вам не нужно обертывать свой список в scrollviewer!
[ UPDATE] Также прочитайте статью эту статью Джоша Смита! Чтобы сделать WrapPanel wrap... вы также должны помнить, чтобы отключить горизонтальную прокрутку...
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
Ответ 2
-
Я не знаком с этим компонентом, но, как правило, будут ограничены количества элементов, которые может отображать список в одно время.
-
Метод решения этой проблемы заключается в том, чтобы сохранить количество изображений, загруженных в элемент управления, в пределах числа, которое элемент управления может отображать на приемлемых уровнях производительности. Два метода для этого - подкачка или динамическая загрузка.
В пейджинге вы добавляете элементы управления для переключения между дискретными блоками изображений, например, 100 за раз, с прямыми и обратными стрелками, аналогичными навигации по записям базы данных.
При динамической загрузке вы реализуете пейджинг за кулисами таким образом, чтобы при прокрутке пользователя до конца приложение автоматически загружалось в следующую партию изображений и потенциально даже удаляло партию старых, чтобы сохранить отзывчивость разумный. Может произойти небольшая пауза, так как это может произойти, и может быть какая-то работа, чтобы держать контроль в правильной точке прокрутки, но это может быть приемлемым компромиссом.
Ответ 3
Я бы порекомендовал вам не привязывать свойство Width/Height для каждого отдельного изображения, но вместо этого вы привязываете LayoutTransform в ListBox ItemsPanel. Что-то вроде:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel>
<StackPanel.LayoutTransform>
<ScaleTransform
ScaleX="{Binding Path=Value, ElementName=ZoomSlider}"
ScaleY="{Binding Path=Value, ElementName=ZoomSlider}" />
</StackPanel.LayoutTransform>
</StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Ответ 4
попробуйте виртуализировать свой стек с виртуализирующим атрибутом VirtualizationStackPanel.IsVirtualizing = "True". это должно повысить производительность.
использование списка со многими элементами в scrollviewer - еще одна известная проблема с производительностью в wpf. если можно, попробуйте избавиться от scrollviewer.
если ваши itemtemplates являются сложными, вам следует подумать об использовании метода виртуализации Recycling. это говорит вашему списку, чтобы повторно использовать существующие объекты, а не создавать новые все время.
Ответ 5
Частью проблемы является то, что она загружает полное изображение в каждом. Вы должны использовать IValueConverter
, чтобы открыть каждое изображение в миниатюрном размере, установив в BitmapImage
свойства DecodePixelWidth
или DecodePixelHeight
. Вот пример, который я использую в одном из моих проектов...
class PathToThumbnailConverter : IValueConverter {
public int DecodeWidth {
get;
set;
}
public PathToThumbnailConverter() {
DecodeWidth = 200;
}
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
var path = value as string;
if ( !string.IsNullOrEmpty( path ) ) {
FileInfo info = new FileInfo( path );
if ( info.Exists && info.Length > 0 ) {
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.DecodePixelWidth = DecodeWidth;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri( info.FullName );
bi.EndInit();
return bi;
}
}
return null;
}
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
throw new NotImplementedException();
}
}
Ответ 6
Как выглядит ваш стиль PhotoListBoxStyle? Если он меняет ListBox ItemsPanelTemplate, тогда есть хороший шанс, что ListBox не использует VirtualizingStackPanel в качестве своей панели списка. Невиртуализированные ListBoxes намного медленнее со многими элементами.