3

ファイルの内容を DataGrid に表示したい。(ファイルには 200,000 行以上が含まれています)

データを含むグリッドを表示するのは高速です。

しかし、スクロールバー (下スクロール用) を使用すると、次の例外が発生します。

System.InvalidOperationException:
{"An ItemsControl is inconsistent with its items source.\n  See the inner exception for more information."}

内部例外:

Information for developers (use Text Visualizer to read this):
This exception was thrown because the generator for control 'System.Windows.Controls.DataGrid Items.Count:0' with name '(unnamed)' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection.  The following differences were detected:
  Accumulated count 0 is different from actual count 200000.  [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]

One or more of the following sources may have raised the wrong events:
     System.Windows.Controls.ItemContainerGenerator
      System.Windows.Controls.ItemCollection
       System.Windows.Data.ListCollectionView
        System.Collections.Generic.List`1[[WpfApplication3.Entry, WpfApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
(The starred sources are considered more likely to be the cause of the problem.)

The most common causes are (a) changing the collection or its Count without raising a corresponding event, and (b) raising an event with an incorrect index or item parameter.

The exception's stack trace describes how the inconsistencies were detected, not how they occurred.  To get a more timely exception, set the attached property 'PresentationTraceSources.TraceLevel' on the generator to value 'High' and rerun the scenario.  One way to do this is to run a command similar to the following:
   System.Diagnostics.PresentationTraceSources.SetTraceLevel(myItemsControl.ItemContainerGenerator, System.Diagnostics.PresentationTraceLevel.High)
from the Immediate window.  This causes the detection logic to run after every CollectionChanged event, so it will slow down the application.

Exception は、「Items コレクションの現在の状態と一致しない一連の CollectionChanged イベントを受け取った」ことを示します。

コードは次のとおりです。

MainWindow.xaml

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication3="clr-namespace:WpfApplication3"
        Title="MainWindow" Height="350" Width="525">
    <Grid Name="Test">
        <WpfApplication3:Viewer x:Name="LogUC" />
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        Test.DataContext = this;

        LogUC.Loaded += LogUcOnLoaded;
    }

    private void LogUcOnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        LogUC.Test();
    }
}

Viewer.xaml

<UserControl x:Class="WpfApplication3.Viewer"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="300" Width="300">
    <Grid Name="Container">
        <DataGrid ItemsSource="{Binding Path=EntryCollection, Mode=OneTime}"
                  AutoGenerateColumns="False"
                  CanUserResizeColumns="True"
                  CanUserResizeRows="True"
                  CanUserAddRows="False"
                  CanUserDeleteRows="False"
                  IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding ErrorCode}" Header="" />
                <DataGridTextColumn Binding="{Binding Time}" Header="Time" />
                <DataGridTextColumn Binding="{Binding Content}" Header="Content" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

Viewer.xaml.cs

public partial class Viewer : INotifyPropertyChanged
{
    public Viewer()
    {
        EntryCollection = new List<Entry>();
        InitializeComponent();
        Container.DataContext = this;
    }

    public List<Entry> EntryCollection { get; set; }

    internal void Test()
    {
        List<Entry> test = new List<Entry>();

        for (int i = 0; i < 200000; i++)
        {
            Entry entry = new Entry(){
                ErrorCode = 0,
                Time = DateTime.Now.ToString(),
                Content = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
            };
            test.Add(entry);
        }

        EntryCollection.AddRange(test);
        OnPropertyChanged("EntryCollection");
    }

    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion Implementation of INotifyPropertyChanged
}

Entry.cs

public class Entry
{
    public int ErrorCode { get; set; }

    public string Time { get; set; }

    public string Content { get; set; }
}

どうしたの?

4

1 に答える 1

6

なぜこれが起こるのかはよくわかりませんが、それを機能させる方法を説明することはできます。

適切なイベントを発生させることにより、EntryCollection内のアイテムが変更されたことをDataGridに通知していないようです。

DataGridバインディングにあるため、メソッドでのOnPropertyChanged("EntryCollection")呼び出しは効果がありません。また、オブジェクトはaであり、ではないため、アイテムを追加しても、DataGridに通知するイベントは発生しません。TestMode=OneTimeEntryCollectionListObservableCollection

私の見方では、これを修正するために2つのことができます。

  1. アイテムが追加/削除されたときにDataGridに通知されるようにしますEntryCollection。次に、呼び出しObservableCollectionを削除しても、を保持できます。OnPropertyChangedMode=OneTime

    public Viewer()
    {
        EntryCollection = new ObservableCollection<Entry>();
        InitializeComponent();
        Container.DataContext = this;
    }
    
    public ObservableCollection<Entry> EntryCollection { get; set; }
    
    internal void Test()
    {
        for (int i = 0; i < 200000; i++)
        {
            Entry entry = new Entry()
            {
                ErrorCode = 0,
                Time = DateTime.Now.ToString(),
                Content = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
            };
    
            EntryCollection.Add(entry);
        }
    }
    
  2. にアイテムを追加する代わりにEntryCollection、新しいインスタンスに設定してPropertyChangedイベントを発生させます。Mode=OneTimeそのようにするには、XAMLの設定を削除する必要があります。

    public Viewer()
    {
        EntryCollection = new List<Entry>();    
        InitializeComponent();
        Container.DataContext = this;
    }
    
    public List<Entry> EntryCollection { get; set; }
    
    internal void Test()
    {
        List<Entry> test = new List<Entry>();
    
        for (int i = 0; i < 200000; i++)
        {
            Entry entry = new Entry()
            {
                ErrorCode = 0,
                Time = DateTime.Now.ToString(),
                Content = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
            };
            test.Add(entry);    
        }
    
        EntryCollection = test;
        OnPropertyChanged("EntryCollection");
    }
    
于 2012-11-14T11:25:37.360 に答える