WPF将项添加到绑定到observablecollection异常的datagrid

Sam*_*dam 3 .net c# data-binding wpf mvvm

我有一个简单的应用程序,显示了一个书籍列表.

BookViewModel.cs

    public class BookViewModel : INotifyPropertyChanged
    {
        private readonly IBookRepository _bookRepository;
        private bool _isDirty = false;

        public ICommand UpdateCommand { get; set; }
        public bool IsDirty { get { return _isDirty; } }

        public BookViewModel(IBookRepository bookRepository)
        {
            _bookRepository = bookRepository;
            UpdateCommand = new UpdateAction(this);

            var books = _bookRepository.GetAll();

            _allBooks = new ObservableCollection<BookModel>();
            _allBooks.CollectionChanged += collectionChanged;

            foreach (var book in books)
            {
                _allBooks.Add(new BookModel()
                {
                    Title = book.Title,
                    Stock = book.Stock
                });
            }
        }

        private ObservableCollection<BookModel> _allBooks;
        private BookModel _book;

        public BookModel Book
        {
            get { return _book; }
            set
            {
                _book = value;
                OnPropertyChanged("Book");
            }
        }

        public ObservableCollection<BookModel> AllBooks
        {
            get
            {
                return _allBooks;
            }
            set 
            { 
                _allBooks = value;
            }
        }

        void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            System.Windows.MessageBox.Show(e.Action.ToString());
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            _isDirty = true;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

AllBookView.xaml

<Window x:Class="LibraryManagement.Presentation.WPF.Book.Views.AllBookView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="AllBookView" Height="311" Width="521">
    <Grid>
        <DataGrid ItemsSource="{Binding AllBooks, Mode=TwoWay}" AutoGenerateColumns="true" Height="200" HorizontalAlignment="Left" Margin="0,72,0,0" VerticalAlignment="Top" Width="499" />
        <Button Command="{Binding UpdateCommand}" Content="Update" Height="23" HorizontalAlignment="Left" Margin="412,43,0,0" VerticalAlignment="Top" Width="75" />
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

我可以查看书籍列表没关系,我也可以删除一行.

但是当我尝试在Datagrid中添加新项时,发生了一个异常:

System.InvalidOperationException was unhandled
  HResult=-2146233079
  Message=An ItemsControl is inconsistent with its items source.
  See the inner exception for more information.
  Source=PresentationFramework
  StackTrace:
       at System.Windows.Controls.ItemContainerGenerator.Verify()
       at System.Windows.Controls.VirtualizingStackPanel.MeasureChild(IItemContainerGenerator& generator, IContainItemStorage& itemStorageProvider, Object& parentItem, Boolean& hasUniformOrAverageContainerSizeBeenSet, Double& computedUniformOrAverageContainerSize, Boolean& computedAreContainersUniformlySized, IList& items, Object& item, IList& children, Int32& childIndex, Boolean& visualOrderChanged, Boolean& isHorizontal, Size& childConstraint, Rect& viewport, VirtualizationCacheLength& cacheSize, VirtualizationCacheLengthUnit& cacheUnit, Boolean& foundFirstItemInViewport, Double& firstItemInViewportOffset, Size& stackPixelSize, Size& stackPixelSizeInViewport, Size& stackPixelSizeInCacheBeforeViewport, Size& stackPixelSizeInCacheAfterViewport, Size& stackLogicalSize, Size& stackLogicalSizeInViewport, Size& stackLogicalSizeInCacheBeforeViewport, Size& stackLogicalSizeInCacheAfterViewport, Boolean& mustDisableVirtualization, Boolean isBeforeFirstItem, Boolean isAfterFirstItem, Boolean isAfterLastItem, Boolean 
...
  InnerException: 
       HResult=-2146233088
       Message=Information for developers (use Text Visualizer to read this):
This exception was thrown because the generator for control 'System.Windows.Controls.DataGrid Items.Count:3' 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 2 is different from actual count 3.  [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]
  At index 1:  Generator's item '{NewItemPlaceholder}' is different from actual item 'LibraryManagement.Presentation.WPF.Book.BookModel'.

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.ObjectModel.ObservableCollection`1[[LibraryManagement.Presentation.WPF.Book.BookModel, LibraryManagement.Presentation.WPF, 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)

添加数据网格行时是否应附加事件处理程序?

我想用双向绑定,每边的任何变化都会反映到另一边?

Fed*_*gui 6

MessageBox.Show()从CollectionChanged事件中删除调用.

实际上,完全删除CollectionChanged事件处理程序.

MessageBox.Show() 强制Dispatcher刷新,并且在引发CollectionChanged事件时这样做不是一个好主意.