【问题标题】:How to sort TabItems in the TabControl如何对 TabControl 中的 TabItems 进行排序
【发布时间】:2011-09-28 23:52:09
【问题描述】:

我有一个带有 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 属性?
  • 是否有任何类型的集合可以侦听其子级以根据子级的属性自动排序??

任何其他想法都会被理解。

【问题讨论】:

    标签: wpf binding tabcontrol


    【解决方案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】:

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

      【讨论】:

      • 谢谢,我尝试使用 CollectionViewSource,但它不起作用。它对列表进行初始排序,但不响应影响排序的更改。
      【解决方案3】:

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

      我只做了一点改动,让它跟随底层集合的变化,所以它会在底层集合或底层集合中的任何元素发生任何变化后自动排序。

      【讨论】:

        猜你喜欢
        • 2012-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-09
        • 2021-10-27
        • 2013-11-23
        • 2012-01-29
        相关资源
        最近更新 更多