Тип объекта, созданного ListCollectionView.AddNew

Как 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;
    }
}

person Tomi Junnila    schedule 08.05.2009    source источник


Ответы (3)


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

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

person Erikest    schedule 30.01.2012
comment
Я изменил архитектуру в своем приложении, поэтому для меня это больше не проблема, но ваш ответ кажется правдоподобным, если просто посмотреть на него. - person Tomi Junnila; 30.01.2012
comment
По какой-то причине я, кажется, не принял этот ответ в прошлом году. - person Tomi Junnila; 26.02.2013

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

person Julien Lebosquain    schedule 13.05.2012
comment
Действительно есть, и этот ответ так же хорош, как и ответ от Эрикеста, который я принял (в силу того, что этот ответ старше, а также упоминает ListCollectionView.AddNewItem(object)). - person Tomi Junnila; 26.02.2013

ТомиДж,

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

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

person Diego Magalhães    schedule 08.05.2009
comment
Статья (оригинал находится по адресу ‹blogs.msdn.com/vinsibal/archive/2008/05/20/) немного помог, так как заставил меня взглянуть на ListCollectionView. - person Tomi Junnila; 10.05.2009
comment
Я вижу, вам удалось получить правильный ответ, не забудьте обновить здесь. Очень интересный вопрос :D - person Diego Magalhães; 12.05.2009
comment
Я еще не совсем в этом, так как могу добавить только один новый элемент, но, по крайней мере, вызывается правильный метод AddNew. Мне нужно выяснить, что еще мне нужно реализовать, чтобы получить правильную функциональность. - person Tomi Junnila; 12.05.2009
comment
почему бы вам не реализовать шаблон стратегии, вы знакомы с концепцией? - person Diego Magalhães; 12.05.2009
comment
Где вы имеете в виду, что я должен применить шаблон стратегии? В основном добавьте новый класс Context, который будет содержать экземпляры Base/DerivedA/DerivedB, а затем коллекция будет содержать их? Это немного неловко. - person Tomi Junnila; 14.05.2009