Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用数据绑定时ListBox的填充速度非常慢_C#_Wpf_Xaml_Data Binding_Listbox - Fatal编程技术网

C# 使用数据绑定时ListBox的填充速度非常慢

C# 使用数据绑定时ListBox的填充速度非常慢,c#,wpf,xaml,data-binding,listbox,C#,Wpf,Xaml,Data Binding,Listbox,我以前使用代码隐藏将项目手动添加到我的列表框中,但速度非常慢。我听说,从性能方面来说,通过XAML进行数据绑定是一种可行的方法 因此,我设法让数据绑定工作起来(对绑定来说是新的),但令我沮丧的是,性能并不比我以前的非数据绑定方法好多少 我的想法是,我的列表框包含一个下面有名称的图像。我做了一些基准测试,54个项目花了整整8秒钟才显示出来。对于用户来说,等待的时间自然太长了 源映像的最大值为:2100x1535px,每个文件的范围为400kb>4mb 复制此问题所需的图像可以在此处找到:链接已删除

我以前使用代码隐藏将项目手动添加到我的列表框中,但速度非常慢。我听说,从性能方面来说,通过XAML进行数据绑定是一种可行的方法

因此,我设法让数据绑定工作起来(对绑定来说是新的),但令我沮丧的是,性能并不比我以前的非数据绑定方法好多少

我的想法是,我的列表框包含一个下面有名称的图像。我做了一些基准测试,54个项目花了整整8秒钟才显示出来。对于用户来说,等待的时间自然太长了

源映像的最大值为:2100x1535px,每个文件的范围为400kb>4mb

复制此问题所需的图像可以在此处找到:链接已删除,因为问题已得到回答,并且我的服务器没有太多带宽余量。其他图片来源:

我已经为下面的问题做了一个可复制的例子。我做错了什么使事情变得如此缓慢

多谢各位

XAML:


背后的代码:

使用系统;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用系统组件模型;
使用System.Linq;
使用System.Windows;
使用System.Windows.Threading;
命名空间WpfApplication1
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
内部类项:INotifyPropertyChanged
{
公共项(字符串名称=null)
{
this.Name=Name;
}
公共字符串名称{get;set;}
公共字符串ImagePath{get;set;}
公共事件属性更改事件处理程序属性更改;
私有void NotifyPropertyChanged(字符串propertyName)
{
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
}
}
可观察收集项目收集;
列出数据;
公共主窗口()
{
初始化组件();
this.data=新列表();
this.ItemsCollection=新的ObservableCollection();
this.listBoxItems.ItemsSource=this.ItemsCollection;
对于(int i=0;i<49;i++)
{
Item newItem=新项目
{
ImagePath=String.Format(@“Images/{0}.jpg”,i+1),
Name=“项目:”+i
};
此.data.Add(newItem);
}
foreach(this.data.Select((value,i)=>new{i,value})中的var项)
{
Dispatcher.Invoke(新操作(()=>
{
this.ItemsCollection.Add(item.value);
}),DispatcherPriority.Background);
}
}
}
}
  • 移动行
    this.listBoxItems.ItemsSource=this.ItemsCollection应该会有一点帮助
    
  • 这里发生的事情是,每次执行
    this.data.Add(newItem)
    时,列表都试图更新其内容,这涉及大量I/O(读取磁盘文件并解码相当大的图像)。运行探查器应该可以确认这一点
  • 更好的方法是从a加载(这将需要更少的I/O),如果这对您的需求是可行的
  • 启用将有助于降低内存需求
这是一个关于这个话题的讨论,我想你可能会觉得有趣。

  • 当两者都保留相同的对象时,您不需要
    可观察集合
    列表
    。删除
    数据
    字段

  • 您没有正确使用
    虚拟化StackPanel
    。默认情况下,ListBox将显示其项。我无法理解为什么要使用WrapPanel作为ItemsPanel,因为您将HorizontalScrollBar设置为禁用。从最小的变化开始。我的意思是,首先移除
    virtualzingstackpanel
    ItemsPanel
    ,看看性能如何变化。您可以稍后更改ItemsPanel等

  • 我无法理解您为什么要使用
    Dispatcher.Invoke
    来填充
    ObservableCollection
    。您已经在当前线程中创建了它。没必要那样。虚拟化将负责加载映像


如果有问题,请告诉我。

现在我可以看到您使用的图像,我可以确认这里的主要问题只是加载大型图像的基本成本。使用这些图像文件根本无法在这段时间内进行改进

您可以做的是异步加载图像,以便在用户等待加载所有图像时,程序的其他部分至少能够响应,或者减小图像的大小,以便加载速度更快。如果可能的话,我强烈推荐后者

如果出于某种原因,需要以原始的大尺寸格式部署和加载映像,那么您至少应该异步加载它们。有很多不同的方法来实现这一点

最简单的方法是在
图像上设置
绑定.IsAsync
。Source
绑定:

<ListBox.ItemTemplate>
  <DataTemplate>
    <StackPanel>
      <Image Width="278" Height="178" Source="{Binding ImagePath, IsAsync=True}"/>
      <TextBlock Text="{Binding Name}" FontSize="16"
                 VerticalAlignment="Bottom" HorizontalAlignment="Center" />
    </StackPanel>
  </DataTemplate>
</ListBox.ItemTemplate>
C#:

<Window x:Class="TestSO42639506PopulateListBoxImages.MainWindow"
        x:ClassModifier="internal"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:l="clr-namespace:TestSO42639506PopulateListBoxImages"
        mc:Ignorable="d"
        WindowState="Maximized"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <StackPanel>
      <TextBlock Text="{Binding TotalSeconds, StringFormat=Total seconds: {0:0}}"/>
      <TextBlock Text="{Binding LoadSeconds, StringFormat=Load seconds: {0:0.000}}"/>
    </StackPanel>

    <ListBox x:Name="listBoxItems" ItemsSource="{Binding Data}"
             Grid.Row="1"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled">

      <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
          <WrapPanel IsItemsHost="True" />
        </ItemsPanelTemplate>
      </ListBox.ItemsPanel>

      <ListBox.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <Image Width="278" Height="178" Source="{Binding Bitmap}"/>
            <TextBlock Text="{Binding Name}" FontSize="16"
                       VerticalAlignment="Bottom" HorizontalAlignment="Center" />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </Grid>
</Window>
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void _UpdatePropertyField<T>(
        ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return;
        }

        field = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

class Item : NotifyPropertyChangedBase
{
    private string _name;
    private string _imagePath;
    private BitmapSource _bitmap;

    public string Name
    {
        get { return _name; }
        set { _UpdatePropertyField(ref _name, value); }
    }

    public string ImagePath
    {
        get { return _imagePath; }
        set { _UpdatePropertyField(ref _imagePath, value); }
    }

    public BitmapSource Bitmap
    {
        get { return _bitmap; }
        set { _UpdatePropertyField(ref _bitmap, value); }
    }
}

class MainWindowModel : NotifyPropertyChangedBase
{
    public MainWindowModel()
    {
        _RunTimer();
    }

    private async void _RunTimer()
    {
        Stopwatch sw = Stopwatch.StartNew();
        while (true)
        {
            await Task.Delay(1000);
            TotalSeconds = sw.Elapsed.TotalSeconds;
        }
    }

    private ObservableCollection<Item> _data = new ObservableCollection<Item>();
    public ObservableCollection<Item> Data
    {
        get { return _data; }
    }

    private double _totalSeconds;
    public double TotalSeconds
    {
        get { return _totalSeconds; }
        set { _UpdatePropertyField(ref _totalSeconds, value); }
    }

    private double _loadSeconds;
    public double LoadSeconds
    {
        get { return _loadSeconds; }
        set { _UpdatePropertyField(ref _loadSeconds, value); }
    }
}

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
partial class MainWindow : Window
{
    private readonly MainWindowModel _model = new MainWindowModel();

    public MainWindow()
    {
        DataContext = _model;
        InitializeComponent();

        _LoadItems();
    }

    private async void _LoadItems()
    {
        foreach (Item item in _GetItems())
        {
            _model.Data.Add(item);
        }

        foreach (Item item in _model.Data)
        {
            BitmapSource itemBitmap = await Task.Run(() =>
            {
                Stopwatch sw = Stopwatch.StartNew();
                BitmapImage bitmap = new BitmapImage();

                bitmap.BeginInit();
                // forces immediate load on EndInit() call
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.UriSource = new Uri(item.ImagePath, UriKind.Relative);
                bitmap.DecodePixelWidth = 278;
                bitmap.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
                bitmap.EndInit();
                bitmap.Freeze();

                sw.Stop();
                _model.LoadSeconds += sw.Elapsed.TotalSeconds;
                return bitmap;
            });
            item.Bitmap = itemBitmap;
        }
    }

    private static IEnumerable<Item> _GetItems()
    {
        for (int i = 1; i <= 60; i++)
        {
            Item newItem = new Item
            {
                ImagePath = String.Format(@"Images/{0}.jpg", i),
                Name = "Item: " + i
            };

            yield return newItem;
        }
    }
}
类NotifyPropertyChangedBase:INotifyPropertyChanged
{
公共事件属性更改事件处理程序属性更改;
受保护的void\u UpdatePropertyField(
ref T字段,T值,[CallerMemberName]字符串propertyName=null)
{
如果(等于)