13

我在开发照片查看器应用程序时遇到了问题。我使用 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>

我还将 ListBox 中的 Image'height 与滑块绑定。(滑块的值也绑定到 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~~>

但是如果我使用上面的样式,我必须在 ListBox 之外使用 ScrollViewer,否则我不知道如何获得平滑滚动的滚动条,并且包装面板似乎没有默认滚动条。有人帮忙吗?据说带有滚动查看器的列表框性能很差。

4

6 回答 6

6

问题是您的新布局面板是 WrapPanel,它不支持虚拟化!可以创建自己的虚拟化 WrapPanel...在这里阅读更多

还可以在此处阅读有关其他问题的更多信息,例如实现 IScrollInfo

我还强烈建议您不要仅仅为了替换布局面板而创建新的控件模板...而是执行以下操作:

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

这样做的好处是您不需要将列表框包装在滚动查看器中!

[更新] 另请阅读Josh Smith 的这篇文章!要使 WrapPanel 换行...您还必须记住禁用水平滚动...

<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
于 2008-10-09T06:46:34.337 回答
3
  1. 我不熟悉这个组件,但一般来说,列表框一次可以显示的项目数量会受到限制。

  2. 解决此类问题的一种方法是将控件中加载的图像数量保持在控件可以在可接受的性能水平上显示的数量之内。执行此操作的两种技术是分页或动态加载。

在分页中,您添加控件以在离散的图片块之间切换,例如,一次 100 个,带有前进和后退箭头,类似于导航数据库记录。

使用动态加载,您可以在后台实现分页,当用户滚动到最后时,应用程序会自动加载下一批图片,甚至可能删除一批旧图片以保持合理的响应性。发生这种情况时可能会有一个小的停顿,并且可能需要进行一些工作以将控件保持在正确的滚动点,但这可能是一个可以接受的折衷方案。

于 2008-10-08T08:56:59.647 回答
2

部分问题是它正在加载每个中的完整图像。您必须通过在 上设置或属性,使用IValueConverter以缩略图大小打开每个图像。这是我在我的一个项目中使用的一个例子......DecodePixelWidthDecodePixelHeightBitmapImage

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

}
于 2008-10-08T13:45:06.833 回答
2

我建议您不要绑定每个单独图像的 Width/Height 属性,而是在 ListBox 的 ItemsPanel 上绑定一个LayoutTransform。就像是:

<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>
于 2008-10-08T13:56:32.147 回答
1

尝试使用 VirtualizingStackPanel.IsVirtualizing="True" 附加属性来虚拟化您的 stackpael。这应该会提高性能。

在滚动查看器中使用包含许多项目的列表框是 wpf 中另一个已知的性能问题。如果可以,请尝试摆脱滚动查看器。

如果您的项目模板有点复杂,您应该考虑使用回收虚拟化模式。这告诉您的列表框重用现有对象,而不是一直创建新对象。

于 2008-10-08T10:10:40.993 回答
0

您的 PhotoListBoxStyle 样式是什么样的?如果它正在更改 ListBox 的 ItemsPanelTemplate,那么您的 ListBox 很可能没有使用 VirtualizingStackPanel 作为其底层列表面板。许多项目的非虚拟化列表框要慢得多。

于 2008-10-08T10:06:49.317 回答