WPF和MVVM-ListView和自动滚动性能

时间:2018-06-26 06:47:31

标签: c# wpf performance mvvm

我正在寻找有关如何解决我在使用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);
                                            }
                                        });
        }
    }
}

0 个答案:

没有答案