Тип объекта, созданного ListCollectionView.AddNew - PullRequest
5 голосов
/ 09 мая 2009

Как ListCollectionView.AddNew определяет тип создаваемого объекта и как на него можно повлиять?

У меня есть иерархия нескольких типов (Base, DerivedA и DerivedB), и в настоящее время мой WPF Toolkit DataGrid создает DerivedA объектов (почему, я не знаю - возможно, потому что почти все данные в сетке относятся к этому типу), но я бы хотел, чтобы вместо этого было создано DerivedB объектов.

Обновление : я пытался извлечь новый класс из ListCollectionView и реализовать для него новый метод AddNew, и теперь я почти у цели: единственная оставшаяся проблема заключается в том, что после добавления новый элемент, новый заполнитель нового элемента не добавляется, поэтому я могу добавить только один элемент. Мой нынешний подход выглядит примерно так:

public class CustomView : ListCollectionView, IEditableCollectionView
{
    public CustomView(System.Collections.IList list)
        : base(list)
    {
    }

    object IEditableCollectionView.AddNew()
    {
        DerivedB obj = new DerivedB();
        InternalList.Add(obj);
        return obj;
    }
}

Ответы [ 3 ]

4 голосов
/ 30 января 2012

Устаревшие вопросы заслуживают свежих ответов:)

Получение класса из 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 исходит от компонента, который находится вне вашего влияния, или вам нужно добавить функциональность в существующий код, и вы беспокоитесь о том, чтобы что-то сломать (чего я никогда не делал, потому что я кавалерский кодер кто черствый карусель с коллекциями).

1 голос
/ 13 мая 2012

В .NET 4 теперь появился новый интерфейс IEditableCollectionViewAddNewItem, реализованный ListCollectionView, который обладает новым методом AddNewItem(object). Вы можете использовать его вместо AddNew() для управления вновь добавленным элементом.

1 голос
/ 09 мая 2009

TomiJ,

посмотрим, поможет ли это, но разве нет ответа?

http://www.cnblogs.com/winkingzhang/archive/2008/05/22/1204581.html

...