13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DataGrid(WPF) の ItemsSource には IList が必要

Posted at

最近ハマったので書き残しておきます。

結論は表題の通り、
###DataGrid のItemsSource にバインドするコレクションには、非ジェネリックの IList を実装する必要があります。###


横着して以下の様な自作コレクションを作って DataGrid にバインドした時のことです。

MyCollection.cs
    public class MyCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable
    {
        // ~ 略 ~
    }

セルを編集しようとすると以下の様な例外が発生しました。

InvalidOperationException: 'EditItem' は、このビューに対して許可されていません。

当然のことながら、コレクションの要素となるクラスには IEditableObject を実装していました。

例外のスタックトレースは以下のとおり。

   場所 System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.EditItem(Object item)
   場所 System.Windows.Controls.DataGrid.EditRowItem(Object rowItem)
   場所 System.Windows.Controls.DataGrid.OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
   場所 System.Windows.Controls.DataGrid.OnExecutedBeginEdit(Object sender, ExecutedRoutedEventArgs e)
   場所 System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
   ~ 略 ~

ILSpy を使って原因を探っていくと、

ItemCollection.cs
namespace System.Windows.Controls
{
    public sealed class ItemCollection
        : CollectionView, IList, ICollection, IEnumerable, IEditableCollectionViewAddNewItem,
            IEditableCollectionView, ICollectionViewLiveShaping, IItemProperties, IWeakEventListener
    {
        
        // ~ 略 ~
        
        private CollectionView _collectionView;
        
        void IEditableCollectionView.EditItem(object item)
        {
            IEditableCollectionView editableCollectionView = this._collectionView as IEditableCollectionView;
            if (editableCollectionView != null)
            {
                editableCollectionView.EditItem(item);
                return;
            }
            throw new InvalidOperationException(SR.Get("MemberNotAllowedForView", new object[]
            {
                "EditItem"
            }));
        }
    }
}

となっており、_collectionView が IEditableCollectionView を実装していないと発生するようです。

以下のようにメソッドを辿って行き、運命の分かれ道を発見しました。

ItemCollection.SetCollectionView()
  ItemCollection.SetItemsSource()
    CollectionViewSource.GetDefaultCollectionView()
      CollectionViewSource.GetDefaultCollectionView()
        DataBindEngine.GetViewRecord()
          ViewManager.GetViewRecord()
ViewManager.cs
namespace MS.Internal.Data
{
    internal class ViewManager : HybridDictionary
    {
        internal ViewRecord GetViewRecord(object collection, CollectionViewSource cvs, Type collectionViewType, bool createView, Func<object, object> GetSourceItem)
        {
            
            // ~ 略 ~
            
                  IList list2 = (list != null) ? list : (collection as IList);
                  if (list2 != null)
                  {
                      IBindingList bindingList = list2 as IBindingList;
                      if (bindingList != null)
                      {
                          collectionView = new BindingListCollectionView(bindingList);
                      }
                      else
                      {
                          collectionView = new ListCollectionView(list2);
                      }
                  }
                  else
                  {
                      IEnumerable enumerable = collection as IEnumerable;
                      if (enumerable != null)
                      {
                          collectionView = new EnumerableCollectionView(enumerable);
                      }
                  }
            
            // ~ 略 ~
            
        }
    }
}

IList を実装している場合は BindingListCollectionView, ListCollectionView と言う
共に IEditableCollectionView を実装したオブジェクトを返し、

IList を実装していない場合は EnumerableCollectionView と言う
IEditableCollectionView を実装しないオブジェクトを返すようです。

BindingListCollectionView.cs
namespace System.Windows.Data
{
    public sealed class BindingListCollectionView
        : CollectionView, IComparer, IEditableCollectionView, ICollectionViewLiveShaping, IItemProperties
    {
    }
}
ListCollectionView.cs
namespace System.Windows.Data
{
    public class ListCollectionView
        : CollectionView, IComparer, IEditableCollectionViewAddNewItem,
            IEditableCollectionView, ICollectionViewLiveShaping, IItemProperties
    {
    }
}
EnumerableCollectionView.cs
namespace MS.Internal.Data
{
    internal class EnumerableCollectionView : CollectionView, IItemProperties
    {
    }
}

DataGrid を使うときは気をつけましょう。

13
13
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?