重新排列集合而不添加/删除项目?

时间:2010-09-02 12:35:33

标签: wpf collections drag-and-drop tabcontrol

我有一个TabControl,可以通过拖放选项卡重新排列。当前进程从列表中删除项目并将其添加到新位置。由于选项卡的复杂程度,我在切换选项卡时遇到了一些性能问题,因此找到了一个替代方法,用于存储已呈现的选项卡并在请求时重新加载它们。我唯一的问题是,当拖放标签时,它会重新渲染每个标签并导致相同的延迟。有没有办法简单地移动集合中的项目而不是添加/删除它?

或者失败了,有没有办法在拖放操作期间取消OnItemsChanged事件中的添加/删除?该过程会影响控件的可视化,因此我需要实际取消添加/删除事件,如果它是由拖放操作引起的(用户也可以正常添加/删除标签)。

2 个答案:

答案 0 :(得分:2)

您是否尝试将TabControl.ItemsSource绑定到集合视图,然后根据索引对集合视图进行排序?然后你的移动逻辑只会改变索引,标签项会相应地排序。

答案 1 :(得分:0)

我最终修改了我的OnItemsChanged事件,以较低的调度程序优先级然后添加代码运行删除代码,因此它为Add操作提供了取消删除操作并重新使用TabItem的ContentPresenter而不是渲染的机会一个新的。

我开始使用的原始代码是从here

获得的

它基本上存储TabItem ContentPresenters,因此在切换选项卡时,它使用存储的ContentPresenter而不是重新绘制新的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);
                    }
                }
            }
            ));
        }
    }