You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

两个ListView选中/取消选中记录的数据绑定问题

解决WPF ListView多选同步到第二个列表的问题

原方案的核心问题是误用了SelectedItem属性——这是为单选场景设计的,当用户取消选中所有项时,SelectedItem会被设为null,导致向第二个列表添加空值;同时它无法跟踪多个选中项的状态变化,自然没法正确移除取消选中的项。以下是更优雅的MVVM解决方案:

1. 创建项包装ViewModel

定义一个ItemViewModel,封装每个列表项的文本和选中状态,让选中状态的变化能被ViewModel监听:

public class ItemViewModel : INotifyPropertyChanged
{
    private string _text;
    public string Text
    {
        get => _text;
        set
        {
            _text = value;
            OnPropertyChanged(nameof(Text));
        }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            _isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 修改主ViewModel

将原有的字符串集合替换为ItemViewModel集合,监听集合项的增减和选中状态变化,自动同步第二个列表:

private ObservableCollection<ItemViewModel> _firstList;
public ObservableCollection<ItemViewModel> FirstList
{
    get => _firstList;
    set
    {
        if (_firstList != value)
        {
            // 移除旧集合的事件监听
            if (_firstList != null)
            {
                foreach (var item in _firstList)
                    item.PropertyChanged -= Item_PropertyChanged;
                _firstList.CollectionChanged -= FirstList_CollectionChanged;
            }

            _firstList = value;

            // 添加新集合的事件监听
            if (_firstList != null)
            {
                foreach (var item in _firstList)
                    item.PropertyChanged += Item_PropertyChanged;
                _firstList.CollectionChanged += FirstList_CollectionChanged;
            }

            RaisePropertyChanged(nameof(FirstList));
            SyncSecondList();
        }
    }
}

private ObservableCollection<string> _secondList = new ObservableCollection<string>();
public ObservableCollection<string> SecondList
{
    get => _secondList;
    set
    {
        if (_secondList != value)
        {
            _secondList = value;
            RaisePropertyChanged(nameof(SecondList));
        }
    }
}

// 监听集合项的增减
private void FirstList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach (ItemViewModel item in e.NewItems)
            item.PropertyChanged += Item_PropertyChanged;

    if (e.OldItems != null)
        foreach (ItemViewModel item in e.OldItems)
            item.PropertyChanged -= Item_PropertyChanged;

    SyncSecondList();
}

// 监听项的选中状态变化
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(ItemViewModel.IsSelected))
        SyncSecondList();
}

// 同步选中项到第二个列表
private void SyncSecondList()
{
    var selectedTexts = FirstList?.Where(x => x.IsSelected).Select(x => x.Text).ToList() ?? new List<string>();
    var currentTexts = SecondList.ToList();

    // 移除不再选中的项
    foreach (var text in currentTexts.Except(selectedTexts))
        SecondList.Remove(text);

    // 添加新选中的项
    foreach (var text in selectedTexts.Except(currentTexts))
        SecondList.Add(text);
}

3. 更新XAML绑定

修改第一个ListView的绑定,直接关联ItemViewModel的属性,无需依赖ListViewItem的选中状态:

<!-- 第一个列表:带复选框的多选列表 -->
<ListView ItemsSource="{Binding FirstList}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding IsSelected}" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

<!-- 第二个列表:显示选中项 -->
<ListView ItemsSource="{Binding SecondList}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

可选优化:用CollectionViewSource直接过滤

如果不需要维护独立的SecondList集合,可以用CollectionViewSource直接过滤选中项,实现更简洁:

修改ViewModel

private ObservableCollection<ItemViewModel> _firstList;
public ObservableCollection<ItemViewModel> FirstList
{
    get => _firstList;
    set
    {
        _firstList = value;
        RaisePropertyChanged(nameof(FirstList));
        RaisePropertyChanged(nameof(SelectedItemsView));
    }
}

// 过滤后的选中项视图
public ICollectionView SelectedItemsView
{
    get
    {
        var view = CollectionViewSource.GetDefaultView(FirstList);
        view.Filter = item => ((ItemViewModel)item).IsSelected;
        return view;
    }
}

修改XAML

第二个ListView直接绑定过滤视图:

<ListView ItemsSource="{Binding SelectedItemsView}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Text}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

内容的提问来源于stack exchange,提问作者KarkMump

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:45:37