Устаревшие вопросы заслуживают свежих ответов:)
Получение класса из ListCollectionView
- это путь, который я выбрал для управления объектами, добавляемыми AddNew
, но после просмотра источника ListCollectionView
, чтобы выяснить, что он делает внутри, я обнаружил, что Самый безопасный способ переопределить AddNew
(технически это не переопределение) - это использовать ListCollectionView.AddNewItem
после создания моего нового объекта, чтобы ваш код выглядел так:
public class CustomView : ListCollectionView, IEditableCollectionView
{
public CustomView(System.Collections.IList list)
: base(list)
{
}
object IEditableCollectionView.AddNew()
{
DerivedB obj = new DerivedB();
return base.AddNewItem(obj);
}
}
Это хорошо работает, потому что, помимо почти одинаковых реализаций, ListCollectionView.AddNew()
и ListCollectionView.AddNewItem(object item)
оба вызывают AddNewCommon(object newItem)
:
public object AddNew()
{
VerifyRefreshNotDeferred();
if (IsEditingItem)
CommitEdit(); // implicitly close a previous EditItem
CommitNew(); // implicitly close a previous AddNew
if (!CanAddNew)
throw new InvalidOperationException(SR.Get(SRID.MemberNotAllowedForView, "AddNew"));
return AddNewCommon(_itemConstructor.Invoke(null));
}
public object AddNewItem(object newItem)
{
VerifyRefreshNotDeferred();
if (IsEditingItem)
CommitEdit(); // implicitly close a previous EditItem
CommitNew(); // implicitly close a previous AddNew
if (!CanAddNewItem)
throw new InvalidOperationException(SR.Get(SRID.MemberNotAllowedForView, "AddNewItem"));
return AddNewCommon(newItem);
}
AddNewCommon
, где происходит вся настоящая магия; события запуска, вызовы BeginInit
и BeginEdit
для нового элемента, если они поддерживаются, и, в конечном итоге, через обратные вызовы в сетке данных, устанавливающие привязки ячеек:
object AddNewCommon(object newItem)
{
_newItemIndex = -2; // this is a signal that the next Add event comes from AddNew
int index = SourceList.Add(newItem);
// if the source doesn't raise collection change events, fake one
if (!(SourceList is INotifyCollectionChanged))
{
// the index returned by IList.Add isn't always reliable
if (!Object.Equals(newItem, SourceList[index]))
index = SourceList.IndexOf(newItem);
BeginAddNew(newItem, index);
}
Debug.Assert(_newItemIndex != -2 && Object.Equals(newItem, _newItem), "AddNew did not raise expected events");
MoveCurrentTo(newItem);
ISupportInitialize isi = newItem as ISupportInitialize;
if (isi != null)
isi.BeginInit();
IEditableObject ieo = newItem as IEditableObject;
if (ieo != null)
ieo.BeginEdit();
return newItem;
}
Здесь я включил исходный код в TypedListCollectionView
, который я использую для управления поведением AddNew
, когда я не знаю, какой тип потребуется во время разработки:
public class TypedListCollectionView : ListCollectionView, IEditableCollectionView
{
Type AddNewType { get; set; }
public TypedListCollectionView(System.Collections.IList source, Type addNewType)
: base(source)
{
AddNewType = addNewType;
}
object IEditableCollectionView.AddNew()
{
object newItem = Activator.CreateInstance(AddNewType);
return base.AddNewItem(newItem);
}
}
Мне нравится этот подход, поскольку он обеспечивает максимальную гибкость для случаев, когда тип AddNew
может потребоваться изменить во время выполнения от одного к другому. Это также позволяет AddNew
работать для добавления первого элемента в коллекцию, что удобно, когда источник списка изначально пуст, но можно определить его базовый тип.
В этой ссылке обсуждается альтернативный способ принудительного использования типа, используемого AddNew()
. Он использует отражение, чтобы установить личное свойство _itemConstructor
, используемое AddNew
, в конструктор без параметров указанного типа. Это было бы особенно полезно, когда ваш ListCollectionView
исходит от компонента, который находится вне вашего влияния, или вам нужно добавить функциональность в существующий код, и вы беспокоитесь о том, чтобы что-то сломать (чего я никогда не делал, потому что я кавалерский кодер кто черствый карусель с коллекциями).