Wpf 如何在TabControl中对TabItems进行排序

Wpf 如何在TabControl中对TabItems进行排序,wpf,binding,tabcontrol,Wpf,Binding,Tabcontrol,我有一个带有Order属性的Page类型集合,我将TabControl的ItemsSource属性设置为ObservableCollection。每当我更改实体的Order属性时,需要执行的操作 相关选项卡项位于正确的位置 WPF XAML: <TabControl Grid.Row="1" ItemsSource="{Binding Pages.ListViewModels}" SelectedItem="{Binding Pages.Current}" > <Ta

我有一个带有Order属性的Page类型集合,我将TabControl的ItemsSource属性设置为ObservableCollection。每当我更改实体的Order属性时,需要执行的操作 相关选项卡项位于正确的位置

WPF XAML:

<TabControl Grid.Row="1" ItemsSource="{Binding Pages.ListViewModels}" SelectedItem="{Binding Pages.Current}"  >
    <TabControl.ContentTemplate>
        <DataTemplate>
            <views:EditPageView />
        </DataTemplate>
    </TabControl.ContentTemplate>
    <TabControl.ItemTemplate>
        <DataTemplate>                                    
            <TextBlock Text="{Binding Header}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>
我想强制TabControl根据Order属性对TabItems进行排序。现在我有了这些问题:

  • 有什么方法可以声明性地做吗
  • TabControl是否具有SortColumn属性
  • TabItem是否具有TabOrder属性
  • 是否有任何类型的集合会侦听其child以根据child的属性自动对其自身进行排序

任何其他想法都会被采纳。

您可以使用CollectionViewSource在UI端对您观察到的集合进行排序。这里有一个示例链接:

您只需对TabControl绑定到的集合进行排序

我一直不喜欢
observateCollection
没有内置的
Sort
方法,因此我通常使用自己的自定义类,该类继承自
observateCollection

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    public ObservableCollectionEx() : base() { }
    public ObservableCollectionEx(List<T> l) : base(l) { }
    public ObservableCollectionEx(IEnumerable<T> l) : base(l) { }

    #region IndexOf

    /// <summary>
    /// Returns the index of the first object which meets the specified function
    /// </summary>
    /// <param name="keySelector">A bool function to compare each Item by</param>
    /// <returns>The index of the first Item which matches the function</returns>
    public int IndexOf(Func<T, bool> compareFunction)
    {
        return Items.IndexOf(Items.FirstOrDefault(compareFunction));
    }

    #endregion

    #region Sorting

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    /// <summary>
    /// Moves the items of the collection so that their orders are the same as those of the items provided.
    /// </summary>
    /// <param name="sortedItems">An <see cref="IEnumerable{T}"/> to provide item orders.</param>
    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }

    #endregion
}

谢谢Rachel,您的解决方案为我提供了线索,但您的解决方案仍然是一个答案,因为每当我更改Order属性时,我都可以手动调用Sort方法,但我想让它自动执行。因此,我最终得到了您的代码的动态版本

基于Rachel,我找到了这个解决方案

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    public ObservableCollectionEx() : base() { }

    public ObservableCollectionEx(List<T> l) : base(l) { }

    public ObservableCollectionEx(IEnumerable<T> l) : base(l) { }

    Func<IEnumerable<T>,IEnumerable<T>> sortFunction;
    Action reset;

    #region IndexOf

    /// <summary>
    /// Returns the index of the first object which meets the specified function
    /// </summary>
    /// <param name="keySelector">A bool function to compare each Item by</param>
    /// <returns>The index of the first Item which matches the function</returns>
    public int IndexOf(Func<T , bool> compareFunction)
    {
        return Items.IndexOf(Items.FirstOrDefault(compareFunction));
    }

    #endregion IndexOf

    #region Sorting

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SetSort<TKey>(Func<T , TKey> keySelector)
    {
        sortFunction = list => list.OrderBy(keySelector);
        InternalSort();
        reset = () => SetSort(keySelector);
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SetSortDescending<TKey>(Func<T , TKey> keySelector)
    {
        sortFunction = list => list.OrderByDescending(keySelector);
        InternalSort();
        reset = () => SetSortDescending(keySelector);
    }

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void SetSort<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
    {
        sortFunction = list => list.OrderBy(keySelector , comparer);
        InternalSort();
        reset = () => SetSort(keySelector , comparer);
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void SetSortDescending<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
    {
        sortFunction = list => list.OrderByDescending(keySelector , comparer);
        InternalSort();
        reset = () => SetSortDescending(keySelector , comparer);
    }

    /// <summary>
    /// Moves the items of the collection so that their orders are the same as those of the items provided.
    /// </summary>
    private void InternalSort()
    {
        UpdateTracking(null , Items.ToList());
    }

    private void MoveItemToItsLocation(T item)
    {
        var sortListCache = sortFunction(Items).ToList();
        Move(IndexOf(item) , sortListCache.IndexOf(item));
    }

    #endregion Sorting

    public void UpdateTracking(IEnumerable<T> oldItems , IEnumerable<T> newItems)
    {
        if (sortFunction == null) return;

        PropertyChangedEventHandler changeTracker = (o , change) => { MoveItemToItsLocation((T)o); };
        Action<T> attachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged += changeTracker);
        Action<T> detachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged -= changeTracker);

        var greeting = new[] { attachChangeTracker , MoveItemToItsLocation };
        var farwell = new[] { detachChangeTracker };

        oldItems.ForEach(detachChangeTracker);
        newItems.ForEach(attachChangeTracker , MoveItemToItsLocation);
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);

        switch (e.Action) {
            case NotifyCollectionChangedAction.Add:
            case NotifyCollectionChangedAction.Remove:
            case NotifyCollectionChangedAction.Replace:
                UpdateTracking(e.OldItems.SafeGet(x => x.Cast<T>()) , e.NewItems.SafeGet(x => x.Cast<T>()));
                break;
            case NotifyCollectionChangedAction.Reset:
                UpdateTracking(Items.ToList() , null);
                if (reset != null)
                    reset();
                break;
            default:
                break;
        }
    }
}
公共类ObservableCollectionEx:ObservableCollection
{
public observeCollectionEx():base(){}
公共observeCollectionEx(列表l):基(l){}
公共ObservableCollectionEx(IEnumerable l):基(l){}
函数排序函数;
动作复位;
#区域指数
/// 
///返回满足指定函数的第一个对象的索引
/// 
///用于比较每个项目的布尔函数
///与函数匹配的第一项的索引
public int IndexOf(Func compareFunction)
{
返回Items.IndexOf(Items.FirstOrDefault(compareFunction));
}
#端区指数
#区域排序
/// 
///根据键按升序对集合的项进行排序。
/// 
///由返回的密钥的类型。
///从项中提取键的函数。
公共无效设置排序(Func键选择器)
{
sortFunction=list=>list.OrderBy(keySelector);
内部排序();
重置=()=>SetSort(按键选择器);
}
/// 
///根据键按降序对集合的项进行排序。
/// 
///由返回的密钥的类型。
///从项中提取键的函数。
public void集合(Func keySelector)
{
sortFunction=list=>list.OrderByDescending(键选择器);
内部排序();
重置=()=>SetSortDescending(按键选择器);
}
/// 
///根据键按升序对集合的项进行排序。
/// 
///由返回的密钥的类型。
///从项中提取键的函数。
///一个比较键的方法。
公共无效设置排序(Func键选择器、IComparer比较器)
{
sortFunction=list=>list.OrderBy(键选择器、比较器);
内部排序();
重置=()=>SetSort(按键选择器、比较器);
}
/// 
///根据键按降序对集合的项进行排序。
/// 
///由返回的密钥的类型。
///从项中提取键的函数。
///一个比较键的方法。
public void setsortsending(Func键选择器、IComparer比较器)
{
sortFunction=list=>list.OrderByDescending(键选择器、比较器);
内部排序();
重置=()=>SetSortDescending(按键选择器、比较器);
}
/// 
///移动集合中的项目,使其顺序与提供的项目相同。
/// 
私有void InternalSort()
{
UpdateTracking(null,Items.ToList());
}
专用void MoveItemToItsLocation(T项)
{
var sortListCache=sortFunction(Items).ToList();
Move(IndexOf(item),sortListCache.IndexOf(item));
}
#端区排序
公共无效更新跟踪(IEnumerable oldItems、IEnumerable newItems)
{
if(sortFunction==null)返回;
PropertyChangedEventHandler changeTracker=(o,change)=>{MoveItemToItsLocation((T)o);};
操作attachChangeTracker=o=>o.ExecuteOnCast(x=>x.PropertyChanged+=changeTracker);
Action detachChangeTracker=o=>o.ExecuteOnCast(x=>x.PropertyChanged-=changeTracker);
var greeting=new[]{attachChangeTracker,MoveItemToItsLocation};
var farwell=new[]{detachChangeTracker};
oldItems.ForEach(detachChangeTracker);
newItems.ForEach(attachChangeTracker,MoveItemToItsLocation);
}
CollectionChanged上的受保护覆盖无效(NotifyCollectionChangedEventArgs e)
{
基础。变更的集合(e);
开关(电动){
案例NotifyCollectionChangedAction。添加:
案例NotifyCollectionChangedAction。删除:
案例通知收集更改操作。替换:
UpdateTracking(e.OldItems.SafeGet(x=>x.Cast()),e.NewItems.SafeGet(x=>x.Cast());
打破
案例通知CollectionChangedAction.Reset:
UpdateTracking(Items.ToList(),null);
如果(重置!=null)
重置();
打破
违约:
打破
}
}
}

我只做了一点更改,使其跟随基础集合中的更改,因此在对基础集合或基础集合中的任何元素进行任何更改后,它将自动排序。

谢谢,我尝试使用CollectionViewSource,但它不起作用。它对列表进行初始排序,但不响应
ListViewModels = GetListViewModels();
ListViewModels.Sort(p => p.Order);
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    public ObservableCollectionEx() : base() { }

    public ObservableCollectionEx(List<T> l) : base(l) { }

    public ObservableCollectionEx(IEnumerable<T> l) : base(l) { }

    Func<IEnumerable<T>,IEnumerable<T>> sortFunction;
    Action reset;

    #region IndexOf

    /// <summary>
    /// Returns the index of the first object which meets the specified function
    /// </summary>
    /// <param name="keySelector">A bool function to compare each Item by</param>
    /// <returns>The index of the first Item which matches the function</returns>
    public int IndexOf(Func<T , bool> compareFunction)
    {
        return Items.IndexOf(Items.FirstOrDefault(compareFunction));
    }

    #endregion IndexOf

    #region Sorting

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SetSort<TKey>(Func<T , TKey> keySelector)
    {
        sortFunction = list => list.OrderBy(keySelector);
        InternalSort();
        reset = () => SetSort(keySelector);
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    public void SetSortDescending<TKey>(Func<T , TKey> keySelector)
    {
        sortFunction = list => list.OrderByDescending(keySelector);
        InternalSort();
        reset = () => SetSortDescending(keySelector);
    }

    /// <summary>
    /// Sorts the items of the collection in ascending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void SetSort<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
    {
        sortFunction = list => list.OrderBy(keySelector , comparer);
        InternalSort();
        reset = () => SetSort(keySelector , comparer);
    }

    /// <summary>
    /// Sorts the items of the collection in descending order according to a key.
    /// </summary>
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
    /// <param name="keySelector">A function to extract a key from an item.</param>
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
    public void SetSortDescending<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
    {
        sortFunction = list => list.OrderByDescending(keySelector , comparer);
        InternalSort();
        reset = () => SetSortDescending(keySelector , comparer);
    }

    /// <summary>
    /// Moves the items of the collection so that their orders are the same as those of the items provided.
    /// </summary>
    private void InternalSort()
    {
        UpdateTracking(null , Items.ToList());
    }

    private void MoveItemToItsLocation(T item)
    {
        var sortListCache = sortFunction(Items).ToList();
        Move(IndexOf(item) , sortListCache.IndexOf(item));
    }

    #endregion Sorting

    public void UpdateTracking(IEnumerable<T> oldItems , IEnumerable<T> newItems)
    {
        if (sortFunction == null) return;

        PropertyChangedEventHandler changeTracker = (o , change) => { MoveItemToItsLocation((T)o); };
        Action<T> attachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged += changeTracker);
        Action<T> detachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged -= changeTracker);

        var greeting = new[] { attachChangeTracker , MoveItemToItsLocation };
        var farwell = new[] { detachChangeTracker };

        oldItems.ForEach(detachChangeTracker);
        newItems.ForEach(attachChangeTracker , MoveItemToItsLocation);
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);

        switch (e.Action) {
            case NotifyCollectionChangedAction.Add:
            case NotifyCollectionChangedAction.Remove:
            case NotifyCollectionChangedAction.Replace:
                UpdateTracking(e.OldItems.SafeGet(x => x.Cast<T>()) , e.NewItems.SafeGet(x => x.Cast<T>()));
                break;
            case NotifyCollectionChangedAction.Reset:
                UpdateTracking(Items.ToList() , null);
                if (reset != null)
                    reset();
                break;
            default:
                break;
        }
    }
}