В WPF ListBox с более чем 1000 элементов изображений, масштабирование изображений замедляется - PullRequest
12 голосов
/ 08 октября 2008

Я столкнулся с проблемой при разработке приложения для просмотра фотографий. Я использую ListBox для показа изображений, которые содержатся в ObservableCollection. Я связываю ItemsSource ListBox с коллекцией 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>

Я также связываю высоту изображения в ListBox с помощью ползунка (значение ползунка также привязывается к zoombarmanager.ZoomBarWidth.Width). Но я обнаружил, что если коллекция станет больше, например: содержит более 1000 изображений, если я использую ползунок для изменения размера iamges, он станет немного медленнее. Мой вопрос 1. Почему стало медленно? он пытается увеличить каждое изображение или просто потому, что notify ("Width") вызывается более 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, в противном случае я понятия не имею, как получить плавную полосу прокрутки прокрутки, и кажется, что у обертки нет прокрутки по умолчанию. Кто-нибудь поможет? Говорят, что список с scrollviewer имеет низкую производительность.

Ответы [ 6 ]

6 голосов
/ 09 октября 2008

Проблема в том, что ваша новая панель макетов - это WrapPanel, и она не поддерживает виртуализацию! Можно создать собственную виртуализированную WrapPanel ... Подробнее здесь

Также читайте больше о других вопросах, таких как реализация IScrollInfo здесь

Я также настоятельно рекомендую вам не создавать новый шаблон элемента управления только для замены панели макета ... Скорее выполните следующее:

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

Преимущество этого заключается в том, что вам не нужно оборачивать свой список в окне просмотра прокрутки!

[ ОБНОВЛЕНИЕ ] Также прочитайте эту статью Джоша Смита! Чтобы сделать WrapPanel Wrap ... вы также должны помнить, чтобы отключить горизонтальную прокрутку ...

<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
3 голосов
/ 08 октября 2008
  1. Я не знаком с этим компонентом, но в целом будут ограничения на количество элементов, которые список может отображать за один раз.

  2. Метод решения этой проблемы состоит в том, чтобы количество загруженных в элемент управления изображений было равно количеству, которое элемент управления может отображать, на приемлемых уровнях производительности. Два способа сделать это - пейджинговая или динамическая загрузка.

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

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

2 голосов
/ 08 октября 2008

Я бы порекомендовал вам не связывать свойство Width / Height каждого отдельного изображения, а просто связать LayoutTransform в ItemsPanel ListBox . Что-то вроде:

<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>
1 голос
/ 08 октября 2008

Часть проблемы заключается в том, что он загружает полное изображение в каждом. Вы должны использовать IValueConverter, чтобы открыть каждое изображение в уменьшенном размере, установив свойства DecodePixelWidth или DecodePixelHeight на BitmapImage. Вот пример, который я использую в одном из моих проектов ...

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();
    }

}
1 голос
/ 08 октября 2008

попытайтесь виртуализировать свой стек стека с помощью присоединенного свойства VirtualizingStackPanel.IsVirtualizing = "True". это должно повысить производительность.

использование списка со многими элементами в scrollviewer - еще одна известная проблема с производительностью в wpf. если можете, попробуйте избавиться от прокрутки.

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

0 голосов
/ 08 октября 2008

Как выглядит ваш стиль PhotoListBoxStyle? Если он изменяет ItemsPanelTemplate ListBox, то есть большая вероятность, что ListBox не использует VirtualizingStackPanel в качестве базовой панели списка. Невиртуализированные списки намного медленнее со многими элементами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...