wpf DataGrid 通过滚动抛出 InvalidOperationException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13374957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 06:11:44  来源:igfitidea点击:

DataGrid throws InvalidOperationException by Scrolling

wpfexceptiondatagridscrollinvalidoperationexception

提问by David

I want to show in a DataGrid the contents of a file. (The file contains more than 200,000 lines)

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

To show the Grid with the Data is fast.

用数据显示网格很快。

But when I use the Scrollbar (for down scrolling) I've got the following exception:

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

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

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.

The Exception tells that it: "has received sequence of CollectionChanged events that do not agree with the current state of the Items collection."

异常告诉它:“已收到与 Items 集合的当前状态不一致的 CollectionChanged 事件序列。”

Thats the code:

这就是代码:

MainWindow.xaml

主窗口.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

主窗口.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

查看器.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

查看器.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

入口.cs

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

    public string Time { get; set; }

    public string Content { get; set; }
}

What's the problem?

有什么问题?

回答by Peter Hansen

I'm not really sure why this happens, but I can tell you how to make it work.

我不确定为什么会发生这种情况,但我可以告诉你如何让它发挥作用。

It looks like you are never telling the DataGrid that the items in EntryCollection have changed, by raising an appropriate event.

看起来您从未通过引发适当的事件告诉 DataGrid EntryCollection 中的项目已更改。

The OnPropertyChanged("EntryCollection")call you have in the Testmethod has no effect, since you have Mode=OneTimeon the DataGrid binding, and since the EntryCollectionobject is a Listand not a ObservableCollection, the adding of items to it is not raising any events to notify the DataGrid.

OnPropertyChanged("EntryCollection")您在该Test方法中的调用无效,因为您Mode=OneTime对 DataGrid 绑定有影响,并且由于EntryCollection对象是 aList而不是 a ObservableCollection,向其中添加项目不会引发任何事件来通知 DataGrid。

The way I see it, you can do two things to fix this.

在我看来,你可以做两件事来解决这个问题。

  1. Make EntryCollectionan ObservableCollectionso the DataGrid is notified as items are added/removed. Then you can remove the OnPropertyChangedcall, and still have 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);
        }
    }
    
  2. Instead of adding items to EntryCollection, set it to a new instance and raise the PropertyChangedevent. Doing it that way, you need to remove the Mode=OneTimesetting in the 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");
    }
    
  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);
        }
    }
    
  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");
    }