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

时间:2013-10-01 15:43:06

标签: c# .net wpf data-binding 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));
            }
        }
    }

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>

我可以查看好的书籍列表,我也可以删除一行。

但是当我尝试在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.

我应该在添加数据网格行时附加事件处理程序吗?

我认为双向绑定,双方的任何变化都会反映到另一方?

1 个答案:

答案 0 :(得分:6)

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

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

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