Я ищу совет о том, как решить проблему, с которой я столкнулся при реализации функциональности, аналогичной представлению списка трассировки "Wireshark", с использованием WPF и MVVM (в настоящее время используется инфраструктура MVVM Light).
В тот момент, что я делал, добавлял ListView в свой View, делал привязку «ItemsSource» к ObservableCollection в ViewModel и реализовывал «автопрокрутку», добавляя обработчик события в событие «CollectionChanged» в ListView. Коллекция предметов.
Это XAML ListView, используемого в представлении:
<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]);
}
}
}
Проблема, с которой я столкнулся при таком решении, заключается в том, что производительность действительно плохая. Каждый раз, когда объект добавляется в наблюдаемую коллекцию, вызывается обработчик события, и поэтому метод «ScrollIntoView» объекта ListView.
После нескольких минут непрерывного добавления элементов пользовательский интерфейс перестает отвечать, и использование памяти становится очень высоким (элемент добавляется в коллекцию примерно каждые 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);
}
});
}
}
}