Как отсортировать TabItems в TabControl - PullRequest
1 голос
/ 29 сентября 2011

У меня есть коллекция типа Page со свойством Order, я устанавливаю свойство ItemsSource для TabControl 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 сортировать TabItems на основе свойства Order. Так что теперь у меня есть эти квесты:

  • Есть ли способ сделать это декларативно?
  • Имеет ли TabControl свойство SortColumn?
  • Имеет ли TabItem свойство TabOrder?
  • Существует ли какой-либо тип коллекции, которая прослушивает свои дочерние элементы для автоматической сортировки на основе свойства дочерних элементов ??

Будет реализована любая другая идея.

Ответы [ 3 ]

1 голос
/ 29 сентября 2011

Вам просто нужно отсортировать коллекцию, к которой привязан ваш 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);
1 голос
/ 29 сентября 2011

Вы можете отсортировать ObservableCollection на стороне пользовательского интерфейса, используя CollectionViewSource. Вот ссылка с примерами: http://msdn.microsoft.com/en-us/library/ms742542.aspx

0 голосов
/ 30 сентября 2011

Спасибо, Рэйчел, ваше решение дало мне подсказку, но, тем не менее, ваше решение - ответ, потому что я могу вручную вызывать метод Sort всякий раз, когда меняю свойство 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;
        }
    }
}

Я только внес небольшое изменение, чтобы оно следовало за изменениями в базовой коллекции, поэтому она будет автоматически сортироваться после любых изменений, внесенных в базовую коллекцию или любой элемент в базовой коллекции.

...