WPF и MVVM - ListView и производительность автопрокрутки - PullRequest
0 голосов
/ 26 июня 2018

Я ищу совет о том, как решить проблему, с которой я столкнулся при реализации функциональности, аналогичной представлению списка трассировки "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);
                                            }
                                        });
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...