我正在寻找有关如何解决我在使用WPF和MVVM(当前使用MVVM Light框架)实现类似于“ Wireshark”跟踪列表视图的功能时遇到的问题的建议。
目前,我正在将ListView添加到View中,将“ ItemsSource”绑定到ViewModel的ObservableCollection上,并通过在ListView的“ CollectionChanged”事件上添加事件处理程序来实现“自动滚动”项目集合。
这是在视图上使用的ListView的XAML:
<ListView x:Name="listView" ItemsSource="{Binding TraceItems}">
<ListView.View>
<GridView>
<GridViewColumn Header="Col1" Width="60" DisplayMemberBinding="{Binding Col1Data}" />
<GridViewColumn Header="Col2" Width="60" DisplayMemberBinding="{Binding Col2Data}" />
<GridViewColumn Header="Col3" Width="150" DisplayMemberBinding="{Binding Col3Data}" />
</GridView>
</ListView.View>
</ListView>
这是“视图”背后的代码:
public partial class TraceView : UserControl
{
public TraceView()
{
InitializeComponent();
((INotifyCollectionChanged)listView.Items).CollectionChanged += ListView_CollectionChanged;
}
private void ListView_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
listView.ScrollIntoView(listView.Items[listView.Items.Count - 1]);
}
}
}
此解决方案遇到的问题是性能确实很差。每次将对象添加到可观察集合中时,都会调用事件处理程序,因此ListView的“ ScrollIntoView”方法被调用。 连续添加元素几分钟后,UI停止响应,并且内存使用率非常高(大约每100毫秒将一个元素添加到集合中)。
我该怎么做才能解决问题?谢谢。
更新1
添加一个示例来重现该问题。 在此示例中,在我的计算机上,该程序在大约10分钟后卡住了。
注意:对于错别字,我在上面写道,每100毫秒就会有一个项目添加到列表中,而我的意思是10毫秒。
ListView用于可视化网络通信跟踪,因此可以快速添加项目。也许我可以处理一种“队列”系统,其中跟踪项不是立即插入列表视图中,而是以延迟的方式插入? 该解决方案的问题在于,实际轨迹和可视轨迹之间的时间间隔将始终增大。
MainWindow.xaml
<Window x:Class="WpfListViewIssue.MainWindow"
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"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="1200">
<Grid>
<ListView x:Name="listView" ItemsSource="{Binding TraceItems}" >
<ListView.View>
<GridView>
<GridViewColumn Header="Col1" Width="80" DisplayMemberBinding="{Binding Col1Data}" />
<GridViewColumn Header="Col2" Width="80" DisplayMemberBinding="{Binding Col2Data}" />
<GridViewColumn Header="Col3" Width="80" DisplayMemberBinding="{Binding Col3Data}" />
<GridViewColumn Header="Col4" Width="1000" DisplayMemberBinding="{Binding Col4Data}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Collections.Specialized;
using System.Windows;
using GalaSoft.MvvmLight.Threading;
namespace WpfListViewIssue
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
DispatcherHelper.Initialize();
InitializeComponent();
((INotifyCollectionChanged)listView.Items).CollectionChanged += ListView_CollectionChanged;
DataContext = new MainViewModel();
}
private void ListView_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
listView.ScrollIntoView(listView.Items[listView.Items.Count - 1]);
}
}
}
}
MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Threading;
namespace WpfListViewIssue
{
public class TraceItem
{
public string Col1Data { get; set; }
public string Col2Data { get; set; }
public string Col3Data { get; set; }
public string Col4Data { get; set; }
}
public class MainViewModel : ViewModelBase
{
private ObservableCollection<TraceItem> _traceItems;
public ObservableCollection<TraceItem> TraceItems
{
get { return _traceItems; }
set
{
if (value != _traceItems)
{
_traceItems = value;
RaisePropertyChanged();
}
}
}
public MainViewModel()
{
TraceItems = new ObservableCollection<TraceItem>();
AddItems();
}
private async void AddItems()
{
await Task.Factory.StartNew(() =>
{
var randBuffer = new byte[50];
var random = new Random();
for (int i = 0; i < Int32.MaxValue; ++i)
{
random.NextBytes(randBuffer);
TraceItem item = new TraceItem()
{
Col1Data = "Item1 N." + i,
Col2Data = "Item2 N." + i,
Col3Data = "Item3 N." + i,
Col4Data = "RandomData: " + BitConverter.ToString(randBuffer).Replace("-", "")
};
DispatcherHelper.CheckBeginInvokeOnUI(() => TraceItems.Add(item));
Thread.Sleep(10);
}
});
}
}
}