В 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 намного медленнее со многими элементами.