DataGrid通过滚动抛出InvalidOperationException

Dav*_*vid 3 wpf datagrid scroll exception invalidoperationexception

我想在DataGrid中显示一个文件的内容.(该文件包含超过200,000行)

使用数据显示网格很快.

但是,当我使用滚动条(向下滚动)时,我有以下异常:

System.InvalidOperationException:
{"An ItemsControl is inconsistent with its items source.\n  See the inner exception for more information."}
Run Code Online (Sandbox Code Playgroud)

的InnerException:

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.
Run Code Online (Sandbox Code Playgroud)

Exception告诉它:"收到的CollectionChanged事件序列与Items集合的当前状态不一致."

这是代码:

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>
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

Entry.cs

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

    public string Time { get; set; }

    public string Content { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

有什么问题?

Pet*_*sen 6

我不确定为什么会这样,但我可以告诉你如何让它发挥作用.

通过引发适当的事件,您似乎永远不会告诉DataGrid EntryCollection中的项目已更改.

OnPropertyChanged("EntryCollection")您的通话有Test方法没有效果,因为你必须Mode=OneTime在DataGrid上绑定,因为EntryCollection对象是List与不是ObservableCollection,项目它的加入不引发任何事件通知DataGrid中.

我看到它的方式,你可以做两件事来解决这个问题.

  1. EntryCollection一个ObservableCollection这样的DataGrid的通知作为添加项/删除.然后你可以删除OnPropertyChanged通话,但仍然可以Mode=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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 而不是添加项目EntryCollection,将其设置为新实例并引发PropertyChanged事件.这样做,您需要删除Mode=OneTimeXAML 中的设置.

    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");
    }
    
    Run Code Online (Sandbox Code Playgroud)