Stale questions deserve fresh answers :)
Deriving a class from ListCollectionView
is the path I took to control the objects being added by AddNew
as well, but after browsing through the source of ListCollectionView
to find out what it does internally, I found that the safest way to redefine AddNew
(it's not technically an override) is to use ListCollectionView.AddNewItem
after creating my new object, so your code would look like this:
public class CustomView : ListCollectionView, IEditableCollectionView
{
public CustomView(System.Collections.IList list)
: base(list)
{
}
object IEditableCollectionView.AddNew()
{
DerivedB obj = new DerivedB();
return base.AddNewItem(obj);
}
}
This works well because, in addition to having nearly identical implementations otherwise, ListCollectionView.AddNew()
and ListCollectionView.AddNewItem(object item)
both call 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
is where all the real magic happens; firing events, calling BeginInit
and BeginEdit
on the new item if supported, and eventually through callbacks on the datagrid, establishing the cell bindings:
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;
}
Here I've included the source code to my TypedListCollectionView
, which I use to control the AddNew
behavior when I don't know what type will be needed at design time:
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);
}
}
I like this approach since it provides maximum flexibility for cases where AddNew
's type may need to be adjusted at runtime from one to another. It also allows AddNew
to work for adding the first item in the collection, which is handy when the list source is initially empty, but its underlying type can be determined.
This link discusses an alternative way to force the type used by AddNew()
. It uses reflection to set the private _itemConstructor
property used by AddNew
to a parameterless constructor of a specified type. This would be particularly useful when your ListCollectionView
is coming from a component that's outside of your influence, or you need to add functionality into existing code and you're worried about breaking things (which I never am because I'm a cavalier coder who callously carouses with collections).