如何在TabControl中对TabItems进行排序

时间:2011-09-28 23:52:09

标签: wpf binding tabcontrol

我有一个带有Order属性的类型Page的集合,我将TabControl的ItemsSource属性设置为ObservableCollection。每当我更改实体的Order属性时我需要什么 相关的TabItem位于正确的位置。

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>

C#代码:

public class QuestionPageSection : INotifyPropertyChanged
{
    public virtual int Id { get; set; }
    public virtual string Header { get; set; }
    private int _Order;
    public virtual int Order
    {
        get
        {
            return _Order;
        }
        set
        {
            _Order = value;
            this.RaisePropertyChanged(() => this.Order , PropertyChanged);
        }
    }
    public event PropertyChangedEventHandler  PropertyChanged;
}

我想强制TabControl根据Order属性对TabItems进行排序。所以现在我有了这些任务:

  • 有没有办法以声明的方式做到这一点?
  • TabControl是否具有SortColumn属性?
  • TabItem是否有TabOrder属性?
  • 是否有任何类型的集合可以监听其子节点根据子节点的属性自动排序?

任何其他想法都会被贬低。

3 个答案:

答案 0 :(得分:1)

您可以使用CollectionViewSource在UI端对ObservableCollection进行排序。以下是一个示例链接:http://msdn.microsoft.com/en-us/library/ms742542.aspx

答案 1 :(得分:1)

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

我一直很讨厌ObservableCollection没有内置Sort方法的事实,因此我通常使用自己的自定义类继承自ObservableCollection

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
}

我可以这样使用它:

ListViewModels = GetListViewModels();
ListViewModels.Sort(p => p.Order);

答案 2 :(得分:0)

感谢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;
        }
    }
}

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