我的WPF应用程序中有一个简单的数据绑定元素列表,我试图从中获取更多性能。在下面的代码中,我添加了一百万条记录,并在我的项目列表中发出更改信号。
我有以下XAML:
<Window x:Class="Log_.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Log+ Viewer" Height="400" Width="500">
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TabControl>
<TabItem Header="Everything">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding LogRecords}">
<ListView.View>
<GridView>
<GridViewColumn Header="Message" DisplayMemberBinding="{Binding Message}"/>
<GridViewColumn Header="Timestamp" DisplayMemberBinding="{Binding Timestamp}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
这是C#代码:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Log_
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableList<LogRecord> LogRecords { get; set; }
public MainWindow()
{
InitializeComponent();
LogRecords = new ObservableList<LogRecord>();
DataContext = this;
new Thread(() =>
{
LogRecord record = new LogRecord();
record.Message = "Hello, world.";
record.Timestamp = DateTime.Now;
List<LogRecord> logRecordList = new List<LogRecord>();
for (int i = 0; i < 1000000; i++)
{
logRecordList.Add(record);
}
Stopwatch timer = new Stopwatch();
timer.Start();
Dispatcher.Invoke(() =>
{
LogRecords.AddRange(logRecordList);
});
timer.Stop();
Console.WriteLine("The operation took {0} milliseconds.", timer.ElapsedMilliseconds);
}).Start();
}
public class LogRecord
{
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}
public class ObservableList<T> : IEnumerable<T>, INotifyCollectionChanged
{
public List<T> UnderlyingList = new List<T>();
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void AddRange(IEnumerable<T> list)
{
UnderlyingList.AddRange(list);
OnCollectionChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, UnderlyingList));
}
protected virtual void OnCollectionChange(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public IEnumerator<T> GetEnumerator()
{
return UnderlyingList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return UnderlyingList.GetEnumerator();
}
}
}
}
输出为:“操作耗时4834毫秒。”
这似乎是一个荒谬的时间,它将这些记录添加为一系列记录。我在这里打破UI虚拟化,因为我的项目源继承了这样的IEnumerable,还是这种正常的性能?如何让这段代码比目前运行得更快?
答案 0 :(得分:0)
如果您有一百万条记录,您可能不希望使用所有这些记录更新UI。当用户滚动列表时,请考虑在网格视图中使用分页或加载记录。
答案 1 :(得分:0)
我的问题是,确实,我认为继承可枚举是一个非常糟糕的主意,而我应该只使用ObservableCollection ...考虑性能差异:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Log_
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableList<LogRecord> LogRecords { get; set; }
public MainWindow()
{
InitializeComponent();
LogRecords = new ObservableList<LogRecord>();
DataContext = this;
new Thread(() =>
{
LogRecord record = new LogRecord();
record.Message = "Hello, world.";
record.Timestamp = DateTime.Now;
List<LogRecord> logRecordList = new List<LogRecord>();
for (int i = 0; i < 1000000; i++)
{
logRecordList.Add(record);
}
Stopwatch timer = new Stopwatch();
timer.Start();
Dispatcher.Invoke(() =>
{
LogRecords.AddRange(logRecordList);
});
timer.Stop();
Console.WriteLine("The operation took {0} milliseconds.", timer.ElapsedMilliseconds);
}).Start();
}
public class LogRecord
{
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}
public class ObservableList<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
public void AddRange(IEnumerable<T> list)
{
foreach (var item in list)
{
Items.Add(item);
}
OnCollectionChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected virtual void OnCollectionChange(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
}
}
}
操作耗时75毫秒。
答案 2 :(得分:0)
您可以尝试的一件事可能是使用局部变量进行存储,然后将其分配给LogRecords
属性。
public MainWindow()
{
InitializeComponent();
//A local variable
var logRecords = new ObservableList<LogRecord>();
DataContext = this;
new Thread(() =>
{
LogRecord record = new LogRecord();
record.Message = "Hello, world.";
record.Timestamp = DateTime.Now;
List<LogRecord> logRecordList = new List<LogRecord>();
for (int i = 0; i < 1000000; i++)
{
logRecordList.Add(record);
}
Stopwatch timer = new Stopwatch();
timer.Start();
Dispatcher.Invoke(() =>
{
// Should prevent UI to update itself
logRecords .AddRange(logRecordList);
});
timer.Stop();
// Assign to actual collection causing UI update
LogRecords = logRecords ;
Console.WriteLine("The operation took {0} milliseconds.", timer.ElapsedMilliseconds);
}).Start();
}