16

ListViewグループ化を実装するたびに、重大なパフォーマンスの問題が発生します。StackOverflow で似たような質問を見つけましたが、役に立たないようです。


これが私の現在の状況です(ノイズが少ないようにプロジェクトを簡素化しました):

私は を子供としてContentControl持っています。ListViewは、最初は空である にListViewバインドされます。ObservableCollection時間が経過すると、オブジェクトがコレクションに追加されます (この例では、 を使用して 10 秒ごとに 500 個のアイテムが追加されますDispatcherTimer)。サイズはObservableCollectionさまざまですが、コレクションが最終的に 25,000 点を超える可能性があります。

ObservableCollectionが 2000 未満の場合 (正確な数値ではありません)、列のサイズ変更は次のようになります。

ここに画像の説明を入力

ただし、 にオブジェクトを追加するとObservableCollection、パフォーマンスが著しく低下します (これを行うには、下にスクロールする必要があります)。

これは最終的にApplicationロックアップにつながります。

を使って問題を解決できると思っていたVirtualizationので、以下を使ってみました。

<ListView x:Name="ListView1"
                  Style="{DynamicResource lvStyle}"
                  VirtualizingPanel.IsVirtualizingWhenGrouping="True"
                  VirtualizingPanel.IsVirtualizing="True"
                  VirtualizingPanel.IsContainerVirtualizable="True"
                  ScrollViewer.IsDeferredScrollingEnabled="True">

しかし、何も機能していないようです!

言うまでもなく、完全にロックアップしますVirtualizingPanel.IsVirtualizingWhenGrouping="True"ListView

Paul McClean の優れた Data Virtualizationも調べましたが、グループ化は処理されません。


質問: で項目をグループ化する場合ListView、アプリケーションのパフォーマンスに大きな影響を与えずに列のサイズを変更する方法はありますか?

理想的には、メモリのオーバーヘッドを減らしたいので、ある種の非同期ソリューションを実装することに賛成です。


コード:

XAML:

<ContentControl x:Class="ListViewDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:local="clr-namespace:ListViewDemo"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:dat="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
        xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic" 
        mc:Ignorable="d" 
        d:DesignHeight="400" d:DesignWidth="300">
    <ContentControl.Resources>
    <Style x:Key="lvStyle" TargetType="{x:Type ListView}">
        <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
        <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
        <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True"/>
        <Setter Property="ListView.ItemsSource" Value="{Binding}"/>
        <Setter Property="ListView.View">
            <Setter.Value>
                <GridView>
                    <GridViewColumn Header="Name">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Name}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Date">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Date}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Desc">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Desc}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </Setter.Value>
        </Setter>
        </Style>
    </ContentControl.Resources>

    <Grid>
        <ListView x:Name="ListView1"
                      Style="{DynamicResource lvStyle}"
                      VirtualizingPanel.IsVirtualizingWhenGrouping="True"
                      VirtualizingPanel.IsVirtualizing="True"
                      VirtualizingPanel.IsContainerVirtualizable="True"
                      ScrollViewer.IsDeferredScrollingEnabled="True">
            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="{x:Type GroupItem}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type GroupItem}">
                                        <DockPanel>
                                            <Border DockPanel.Dock="Top">
                                                <TextBlock x:Name="groupItem"
                                                            Text="{Binding ItemCount, StringFormat={}({0} Results)}"></TextBlock>
                                            </Border>
                                            <ItemsPresenter DockPanel.Dock="Bottom"></ItemsPresenter>
                                        </DockPanel>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>
    </Grid>
</ContentControl>

コード ビハインド:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
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;
using System.Windows.Threading;

namespace ListViewDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : ContentControl
    {
        private ObservableCollection<Event> eventCollection = new ObservableCollection<Event>();

        public MainWindow()
        {
            InitializeComponent();

            DispatcherTimer dispatcherTimer = new DispatcherTimer();
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            dispatcherTimer.Interval = new TimeSpan(0, 0, 10);
            dispatcherTimer.Start();

            ListView1.ItemsSource = eventCollection;
            ListView1.Items.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
            ListView1.Items.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Descending));
            ListView1.Items.GroupDescriptions.Add(new PropertyGroupDescription("Seconds"));
        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            for(var i = 0; i < 500; i++){
                eventCollection.Add(new Event
                {
                    Name = string.Format("Name_{0}", eventCollection.Count),
                    Date = DateTime.Now.ToString("MM.dd.yy HH:mm"),
                    Seconds = Convert.ToInt32(DateTime.Now.ToString("ss")),
                    Desc = "Description"
                });
            }
        }

        public class Event
        {
            public string Name { get; set; }
            public string Date { get; set; }
            public int Seconds { get; set; }
            public string Desc { get; set; }
        }

    }

}
4

3 に答える 3

2

問題は、ListView にバインドされている ObservableCollection と、Add() ごとに 500 個のアイテムを追加している場合だと思います。Add ごとに 3 つのイベントが発生します。2 NotifyPropertyChanged- プロパティCountとプロパティのイベント、およびコレクションのItem[]1 つNotifyCollectionChanged-Event。発生したイベントは最大 1500 までカウントされます。

ObservableCollection を ObservableCollection の独自の派生実装と交換しました。これにより、一連のアイテムを追加し、一度に 3 つのイベントのみを発生させることができます。

    public class SmartCollection<T> : ObservableCollection<T> {
        public SmartCollection()
            : base() {

        }

        public SmartCollection(IEnumerable<T> collection)
            : base(collection) {

        }

        public SmartCollection(List<T> list)
            : base(list) {

        }

        public void AddRange(IEnumerable<T> range) {
            foreach (var item in range) {
                Items.Add(item);
            }

            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public void Reset(IEnumerable<T> range) {
            if (range == null) {
                throw new ArgumentNullException("range", "range is null");
            }

            var rangeToAdd = range.ToList();
            this.Items.Clear();

            AddRange(rangeToAdd);
        }
    }

私のコレクションを使用して、コレクションに要素を追加する方法を変更しました

private SmartCollection<Event> eventCollection = new SmartCollection<Event>();

private void dispatcherTimer_Tick(object sender, EventArgs e) {
  List<Event> newEvents = new List<Event>(500);

  for(var i = 0; i < 500; i++){
    newEvents.Add(new Event {
      Name = string.Format("Name_{0}", eventCollection.Count + i),
      Date = DateTime.Now.ToString("MM.dd.yy HH:mm"),
      Seconds = Convert.ToInt32(DateTime.Now.ToString("ss")),
      Desc = "Description"
    });
  }

  eventCollection.AddRange(newEvents);
}
于 2013-04-29T07:51:44.720 に答える
0

この回答が適切かどうかはわかりませんが、 VirtualizingPanel.ScrollUnitプロパティを「Item」に設定した後、パフォーマンスが回復しました。問題は、行ごとにスクロールする (マウス ホイールまたはキーを上下に移動する) ときに Grouping オブジェクトが一番上の行に重なるのを見たり、レンダラーはテキストをクリアします。

VirtualizingPanel.ScrollUnit="Item"

編集: また、VirtualizationMode を Standard、VirtualizingPanel.VirtualizationMode="Standard" に戻すと、視覚的な不具合はほとんどなくなります。(50,000行でテストしています)。

于 2013-04-29T02:10:12.910 に答える