我怎么知道ListBoxItem是否是Wpf的ListBox中的最后一项?

我怎么知道ListBoxItem是Wpf的ListBox集合的最后一项(在ItemContainerStyle还是在ItemContainer的模板中)?

那个问题是因为我需要知道一个项目是否是以其他方式显示它的最后一个项目。 例如:假设我想显示以分号分隔的项目,但最后一项: a; b; c

使用ccs选择器在html和ccs中很容易做到这一点。 但是,我怎么能在Wpf中做到这一点?

由于向ListBoxItem 实现 “索引”附加属性以执行正确的工作似乎相当困难 ,我相信更容易实现的方法是在MVVM中。 您可以将必要的逻辑(“IsLast”属性等)添加到列表的实体类型中,让ViewModel处理此更新,在修改或替换集合时更新它。

编辑

经过一些尝试,我设法使用附加属性的混合并inheritanceListBox来实现ListBoxItems的索引(并因此检查最后)。 看看这个:

 public class IndexedListBox : System.Windows.Controls.ListBox { public static int GetIndex(DependencyObject obj) { return (int)obj.GetValue(IndexProperty); } public static void SetIndex(DependencyObject obj, int value) { obj.SetValue(IndexProperty, value); } ///  /// Keeps track of the index of a ListBoxItem ///  public static readonly DependencyProperty IndexProperty = DependencyProperty.RegisterAttached("Index", typeof(int), typeof(IndexedListBox), new UIPropertyMetadata(0)); public static bool GetIsLast(DependencyObject obj) { return (bool)obj.GetValue(IsLastProperty); } public static void SetIsLast(DependencyObject obj, bool value) { obj.SetValue(IsLastProperty, value); } ///  /// Informs if a ListBoxItem is the last in the collection. ///  public static readonly DependencyProperty IsLastProperty = DependencyProperty.RegisterAttached("IsLast", typeof(bool), typeof(IndexedListBox), new UIPropertyMetadata(false)); protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue) { // We capture the ItemsSourceChanged to check if the new one is modifiable, so we can react to its changes. var oldSource = oldValue as INotifyCollectionChanged; if(oldSource != null) oldSource.CollectionChanged -= ItemsSource_CollectionChanged; var newSource = newValue as INotifyCollectionChanged; if (newSource != null) newSource.CollectionChanged += ItemsSource_CollectionChanged; base.OnItemsSourceChanged(oldValue, newValue); } void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { this.ReindexItems(); } protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item) { // We set the index and other related properties when generating a ItemContainer var index = this.Items.IndexOf(item); SetIsLast(element, index == this.Items.Count - 1); SetIndex(element, index); base.PrepareContainerForItemOverride(element, item); } private void ReindexItems() { // If the collection is modified, it may be necessary to reindex all ListBoxItems. foreach (var item in this.Items) { var itemContainer = this.ItemContainerGenerator.ContainerFromItem(item); if (itemContainer == null) continue; int index = this.Items.IndexOf(item); SetIsLast(itemContainer, index == this.Items.Count - 1); SetIndex(itemContainer, index); } } } 

为了测试它,我们设置了一个简单的ViewModel和一个Item类:

 public class ViewModel : INotifyPropertyChanged { #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion private ObservableCollection items; public ObservableCollection Items { get { return this.items; } set { if (this.items != value) { this.items = value; this.OnPropertyChanged("Items"); } } } public ViewModel() { this.InitItems(20); } public void InitItems(int count) { this.Items = new ObservableCollection(); for (int i = 0; i < count; i++) this.Items.Add(new Item() { MyProperty = "Element" + i }); } } public class Item { public string MyProperty { get; set; } public override string ToString() { return this.MyProperty; } } 

风景:

                                

在视图后面的代码中,我提出了一些逻辑来测试更新集合时解决方案的行为:

 public partial class MainWindow : Window { public ViewModel ViewModel { get { return this.DataContext as ViewModel; } } public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { this.ViewModel.Items.Insert( 5, new Item() { MyProperty= "NewElement" }); } private void Button_Click_1(object sender, RoutedEventArgs e) { this.ViewModel.Items.RemoveAt(5); } private void Button_Click_2(object sender, RoutedEventArgs e) { this.ViewModel.InitItems(new Random().Next(10,30)); } } 

此解决方案可以处理静态列表以及ObservableCollections并添加,删除,插入项目。 希望你觉得它有用。

编辑

使用CollectionViews测试它,它工作得很好。

在第一个测试中,我更改了ListBox.Items中的Sort / GroupDescriptions。 当其中一个被更改时,ListBox重新创建容器,然后PrepareContainerForItemOverride命中。 当它在ListBox.Items本身中查找正确的索引时,订单会正确更新。

在第二个中,我在ViewModel中使Items属性成为ListCollectionView。 在这种情况下,当描述发生更改时,会引发CollectionChanged并且ListBox会按预期进行响应。