最近ハマったので書き残しておきます。
結論は表題の通り、
###DataGrid のItemsSource にバインドするコレクションには、非ジェネリックの IList を実装する必要があります。###
横着して以下の様な自作コレクションを作って DataGrid にバインドした時のことです。
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 を使って原因を探っていくと、
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()
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 を実装しないオブジェクトを返すようです。
namespace System.Windows.Data
{
public sealed class BindingListCollectionView
: CollectionView, IComparer, IEditableCollectionView, ICollectionViewLiveShaping, IItemProperties
{
}
}
namespace System.Windows.Data
{
public class ListCollectionView
: CollectionView, IComparer, IEditableCollectionViewAddNewItem,
IEditableCollectionView, ICollectionViewLiveShaping, IItemProperties
{
}
}
namespace MS.Internal.Data
{
internal class EnumerableCollectionView : CollectionView, IItemProperties
{
}
}
DataGrid を使うときは気をつけましょう。