WPF中如何将ItemsControl内ComboBox的选择绑定至非ItemSource列表
WPF MVVM 手动排序实现方案
先解决你代码里的几个关键问题,再给你修正后的完整代码:
问题根源
- 选中值共享:你用
Enumerable.Repeat(new ItemSelectionPart(items), items.Count)时,本质是把同一个ItemSelectionPart实例重复添加了N次,所有ComboBox绑定的都是同一个Selection属性,选一个自然全跟着变。 - DisplayMemberPath写错了:这个属性直接填要显示的属性名字符串(比如"Id"或"Name")就行,不需要用
{Binding}语法。 - 没加属性变更通知:你的
ItemSelectionPart和ViewModel都没实现INotifyPropertyChanged,选完值UI不会同步,ViewModel也收不到变化。 - SelectedValue用错了:你的
Selection是Item类型,直接用SelectedItem绑定就行,SelectedValue要配合SelectedValuePath使用,反而麻烦。
修正后的完整代码
1. 通用通知基类(避免重复写INotifyPropertyChanged)
using System.ComponentModel; using System.Runtime.CompilerServices; public class ObservableObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 修改Item类(可选,后续属性变更时UI能自动更新)
public class Item : ObservableObject { private int _id; private string _name; public Item(int id, string name) { Id = id; Name = name; } public int Id { get => _id; set { _id = value; OnPropertyChanged(); } } public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } }
3. 修正ItemSelectionPart类
每个实例独立,选值变化能通知UI:
public class ItemSelectionPart : ObservableObject { private Item _selection; public ItemSelectionPart(List<Item> allItems) { AllItems = allItems; } public List<Item> AllItems { get; } public Item Selection { get => _selection; set { _selection = value; OnPropertyChanged(); } } }
4. 重写ViewModel
循环创建独立的ItemSelectionPart实例,添加获取排序结果的方法:
public class ComboViewModel : ObservableObject { public string Test2 { get; set; } public List<Item> Items { get; set; } public ObservableCollection<ItemSelectionPart> ItemsSelection { get; set; } public ComboViewModel() { Test2 = "test"; Items = new List<Item> { new Item(100, "Entry #1"), new Item(101, "Entry #2"), new Item(102, "Entry #3"), new Item(103, "Entry #4") }; // 循环创建独立实例,解决选中值共享问题 ItemsSelection = new ObservableCollection<ItemSelectionPart>(); foreach (var _ in Items) { ItemsSelection.Add(new ItemSelectionPart(Items)); } } // 获取最终排序结果 public List<Item> GetSortedItems() { return ItemsSelection.Select(s => s.Selection).Where(i => i != null).ToList(); } }
5. 修正XAML
<Window x:Class="TestingComboBox.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:TestingComboBox" d:DataContext="{d:DesignInstance Type=local:ComboViewModel}" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <StackPanel> <ItemsControl ItemsSource="{Binding ItemsSelection}"> <ItemsControl.ItemTemplate> <DataTemplate> <ComboBox Margin="5" ItemsSource="{Binding AllItems}" DisplayMemberPath="Name" <!-- 要显示Id就改成"Id" --> SelectedItem="{Binding Selection}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <!-- 添加按钮测试获取排序结果 --> <Button Margin="5" Content="获取排序结果" Click="GetSortedItems_Click"/> </StackPanel> </Window>
6. 主窗口添加按钮事件
public partial class MainWindow : Window { private ComboViewModel _viewModel; public MainWindow() { InitializeComponent(); _viewModel = new ComboViewModel(); DataContext = _viewModel; } private void GetSortedItems_Click(object sender, RoutedEventArgs e) { var sortedItems = _viewModel.GetSortedItems(); // 此处可将结果存入目标列表,或弹窗查看效果 MessageBox.Show(string.Join("\n", sortedItems.Select(i => $"{i.Id}: {i.Name}"))); } }
额外优化建议
- 如果要禁止重复选择同一个Item,可以在ViewModel中维护已选中集合,每次选值时过滤掉已选Item,更新每个
ItemSelectionPart的可选列表。 - 若需要初始时每个ComboBox默认选中不同Item,在创建
ItemSelectionPart时给Selection赋值即可。
内容的提问来源于stack exchange,提问作者thatguy14
相关产品推荐
相关产品推荐

