WPF ListBox如何绑定显示项索引并在拖拽增删时自动更新
方案1:监听集合变动自动刷新索引(适用于需要Model中保留Index属性的场景)
你不需要在每个操作场景手动调用刷新方法,只需利用ObservableCollection自带的CollectionChanged事件,集合发生新增、删除、重排序操作时会自动触发索引更新:
- 修改ViewModel代码,订阅集合变更事件
using System.Collections.Specialized; public class MainWindowViewModel : BindablePropertyBase, IDropTarget { private ObservableCollection<Model> test_list; public ObservableCollection<Model> TestList { get { return test_list; } set { // 解绑旧集合的事件避免内存泄漏 if (test_list != null) test_list.CollectionChanged -= TestList_CollectionChanged; test_list = value; // 绑定新集合的事件 if (test_list != null) test_list.CollectionChanged += TestList_CollectionChanged; RaisePropertyChanged(); } } // 构造函数 public MainWindowViewModel() { TestList = new ObservableCollection<Model>() { new Model() { Index = 1, Name = "FirstModel" }, new Model() { Index = 2, Name = "SecondModel" }, new Model() { Index = 3, Name = "ThirdModel" } }; } private void TestList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // 仅在集合项增减、重排序、重置时刷新索引 if (e.Action is NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Move or NotifyCollectionChangedAction.Reset) { RefreshAllIndexes(); } } public void DragOver(IDropInfo dropInfo) { Model sourceItem = dropInfo.Data as Model; Model targetItem = dropInfo.TargetItem as Model; if (sourceItem != null && targetItem != null) { dropInfo.DropTargetAdorner = DropTargetAdorners.Insert; dropInfo.Effects = DragDropEffects.Move; } } public void Drop(IDropInfo dropInfo) { // 直接用GongDragDrop提供的原生索引属性,避免取值错误 if (dropInfo.Data is Model sourceItem) { TestList.Move(dropInfo.SourceIndex, dropInfo.InsertIndex); // 不需要手动调用刷新,CollectionChanged事件会自动触发 } } private void RefreshAllIndexes() { for (int i = 0; i < TestList.Count; i++) { TestList[i].Index = i + 1; } } }
这种方式不管是拖拽重排序,还是业务代码中新增、删除TestList的元素,都会自动更新所有项的Index属性,不需要额外写调用逻辑。
方案2:纯XAML实现序号显示(适用于仅需要显示序号、不需要在Model中存储Index的场景)
如果你不需要在Model里保留Index属性,只是前端显示序号,可以直接用WPF的AlternationCount特性实现,不需要写任何后台刷新逻辑:
- 删除Model中的Index属性,删除ViewModel中所有和Index刷新相关的代码
- 先定义一个简单的数值+1转换器:
using System.Globalization; using System.Windows.Data; public class AddOneConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is int index) return index + 1; return 1; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
- 修改ListBox的XAML代码如下:
<!-- 先在Window的资源中注册转换器 --> <Window.Resources> <local:AddOneConverter x:Key="AddOneConverter"/> <sys:Int32 x:Key="MaxInt">2147483647</sys:Int32> </Window.Resources> <ListBox ItemsSource="{Binding TestList}" dd:DragDrop.DropHandler="{Binding}" dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDragSource="True" AlternationCount="{StaticResource MaxInt}"> <ListBox.ItemTemplate> <DataTemplate> <Grid Margin="10" Background="Aqua"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <!-- 直接绑定当前项的AlternationIndex,加1实现从1开始计数 --> <TextBlock Text="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource AddOneConverter}}" Margin="10,0"/> <TextBlock Text="{Binding Name}" Grid.Column="1"/> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
这个方案性能更高,完全不需要后台处理索引相关逻辑,完全满足从1开始计数、支持拖拽重排序的需求。
内容的提问来源于stack exchange,提问作者Davide Pavani
相关产品推荐
相关产品推荐

