Я закончил тем, что изменил свое событие OnItemsChanged
, чтобы запускать код удаления с более низким приоритетом диспетчера, чем код добавления, поэтому он дает операции Add возможность отменить удаление и повторно использовать ContentPresenter объекта TabItem вместо рендеринга новогоone.
Исходный код, с которого я начал, был получен из здесь
Он в основном хранит TabItem ContentPresenters, поэтому при переключении вкладок он использует сохраненный ContentPresenter вместо перерисовки нового.,Вот изменения, которые я сделал в OnItemsChanged, чтобы Drag / Drop мог повторно использовать старый элемент вместо перерисовки нового
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
// Search for recently deleted items caused by a Drag/Drop operation
if (e.NewItems != null && _deletedObject != null)
{
foreach (var item in e.NewItems)
{
if (_deletedObject == item)
{
// If the new item is the same as the recently deleted one (i.e. a drag/drop event)
// then cancel the deletion and reuse the ContentPresenter so it doesn't have to be
// redrawn. We do need to link the presenter to the new item though (using the Tag)
ContentPresenter cp = FindChildContentPresenter(_deletedObject);
if (cp != null)
{
int index = _itemsHolder.Children.IndexOf(cp);
(_itemsHolder.Children[index] as ContentPresenter).Tag =
(item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
}
_deletedObject = null;
}
}
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
_deletedObject = item;
// We want to run this at a slightly later priority in case this
// is a drag/drop operation so that we can reuse the template
// Render is good since a normal Removal of an item will run prior to adding a new one
this.Dispatcher.BeginInvoke(DispatcherPriority.Render,
new Action(delegate()
{
if (_deletedObject != null)
{
ContentPresenter cp = FindChildContentPresenter(_deletedObject);
if (cp != null)
{
this._itemsHolder.Children.Remove(cp);
}
}
}
));
}
}