C# 将集合绑定到列表框中的SelectedItems而不违反MVVM

C# 将集合绑定到列表框中的SelectedItems而不违反MVVM,c#,wpf,C#,Wpf,我有一个名为SelectedVNodes”的ObservableCollection,它包含来自ObservableCollectionVNodes的项目 SelectedVNodes应仅包含属性为IsSelected=True的节点,否则如果为“false”,则不应在列表中 ObservableCollection<VNode> SelectedVNodes {...} ObservableCollection<VNode> VNodes {...} NOTIFYPR

我有一个名为SelectedVNodes”的ObservableCollection,它包含来自ObservableCollectionVNodes的项目

SelectedVNodes应仅包含属性为
IsSelected=True
的节点,否则如果为“false”,则不应在列表中

ObservableCollection<VNode> SelectedVNodes {...}
ObservableCollection<VNode> VNodes {...}

NOTIFYPROPERTYCHANGED派生自INotifyPropertyChanged。

不使用INotifyPropertyChanged的一种方法是在
设置程序中添加
if
构造:

private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            Set(ref isSelected, value);

            if(isSelected)
            {
                if(!SelectedVNodes.Any(v => v.Name == this.Name))
                SelectedVNodes.Add(this);
            }
            else{
                if(SelectedVNodes.Any(v => v.Name == this.Name))
                SelectedVNodes.Remove(this);
            }
            Console.WriteLine("selected/deselected");
        }
    }

如果我没记错的话,在上一集结束时,我们使用了一些异想天开的WPF控件,它不允许您正确绑定
SelectedItems
,所以就这样结束了。但如果你能做到,这是迄今为止最好的方法:

<NonWhimsicalListBox
    ItemsSource="{Binding VNodes}"
    SelectedItems="{Binding SelectedVNodes}"
    />
假设
AttachedProperties
在XAML中的“
local:
”命名空间中定义

<ListBox 
    ItemsSource="{Binding VNodes}" 
    SelectionMode="Extended"
    local:AttachedProperties.SelectedItems="{Binding SelectedVNodes}"
    />
  • 执行该事件,而不是添加/删除,只需重新创建
    SelectedVNodes

    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
        if (e.PropertyName == "IsSelected") {
            //  Make sure OnPropertyChanged("SelectedVNodes") is happening!
            SelectedVNodes = new ObservableCollection<VNode>(
                    VNodes.Where(vn => vn.IsSelected)
                );
        }
    };
    
  • VNode
    一个父属性。当父视图模型创建一个
    VNode
    时,它会为每个
    VNode
    提供一个父引用,指向
    selectedvnode
    的所有者(可能是它自己)。在
    VNode.IsSelected.set
    中,VNode对
    父级.SelectedVNodes
    执行添加或删除操作

    //  In class VNode
    private bool _isSelected = false;
    public bool IsSelected {
        get { return _isSelected; }
        set {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
            // Elided: much boilerplate checking for redundancy, null parent, etc.
            if (IsSelected)
                Parent.SelectedVNodes.Add(this);
            else
                Parent.SelectedVNodes.Remove(this);
         }
     }
    
  • 以上这些都不是艺术品。版本1可能最不坏

    如果您有大量项目,请不要使用
    IEnumerable
    one。另一方面,它免除了您进行双向操作的责任,也就是说,如果某些消费者直接干扰了
    SelectedVNodes
    ,您应该真正处理其
    CollectionChanged
    事件并更新相关
    VNodes
    。当然,然后您必须确保不会意外地递归:不要向已经存在的集合中添加一个,如果
    vn.IsSelected
    已经为true,则不要设置
    vn.IsSelected
    。如果你的眼睛现在像我的眼睛一样呆滞,你开始感觉到墙壁在靠近,请允许我推荐选项3

    也许
    SelectedVNodes
    应该公开
    ReadOnlyObservableCollection
    ,让您摆脱困境。在这种情况下,1号是您的最佳选择,因为
    vnode
    将无法访问VM的私有可变
    observateCollection


    但是你自己选吧

    您好,您应该在您的复选框声明中添加一个命令和一个commandParameter。在ur viewmodel中添加命令,命令参数应该是选中的元素(VNode类型),更新属性ISSELECT=false,并且应该正常更新。请注意,由于命令嵌套在listview@Guillaume你能告诉我怎么做吗?它是相同的实现,而不是按钮复选框,在removeCommand中,您将更新ur Vnodes.IsSelected=true,并且您应该更改AncestorType={x:Type DataGrid}}}}“DataGrid”“到Gridview或listView。很抱歉,我在午餐时间得到了快速回复:)您使用什么控件来显示/选择
    VNode
    s?如果控件支持multi-select,那么它应该已经有了一个集合,比如
    SelectedItems
    或类似的集合。在我的例子中,控件没有selected Items属性,因为我使用的是画布。是否有方法向画布添加扩展以收集所选项目?因此,画布是ItemsControl(或其他)使用的ItemsPanel?像这样:还是什么?这会让事情变得更简单吗?@JokerMartini好的,我写了一个附件属性,它将列表框的SelectedItems状态反映到集合中(任何实现viewmodel提供的
    IList
    的东西。ItemsPanel是什么都没有区别。@JokerMartini好的,这样做了。它可以反映通过UI输入的选择中的更改。没有测试相反方向的更改。
    <ListBox 
        ItemsSource="{Binding VNodes}" 
        SelectionMode="Extended"
        local:AttachedProperties.SelectedItems="{Binding SelectedVNodes}"
        />
    
    private ObservableCollection<Node> _selectedVNodes 
        = new ObservableCollection<Node>();
    public ObservableCollection<Node> SelectedVNodes
    {
        get
        {
            return _selectedVNodes;
        }
    }
    
    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
        if (e.PropertyName == "IsSelected") {
            if ((bool)e.NewValue) {
                //  If not in SelectedVNodes, add it.
            } else {
                //  If in SelectedVNodes, remove it.
            }
        }
    };
    
    //  blah blah blah
    
    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
        if (e.PropertyName == "IsSelected") {
            //  Make sure OnPropertyChanged("SelectedVNodes") is happening!
            SelectedVNodes = new ObservableCollection<VNode>(
                    VNodes.Where(vn => vn.IsSelected)
                );
        }
    };
    
    var newvnode = new VNode();
    newvnode.PropertyChanged += (s,e) => {
        if (e.PropertyName == "IsSelected") {
            OnPropertyChanged("SelectedVNodes");
        }
    };
    
    //  blah blah blah much else blah blah
    
    public IEnumerable<VNode> SelectedVNodes {
        get { return VNodes.Where(vn => vn.IsSelected); }
    }
    
    //  In class VNode
    private bool _isSelected = false;
    public bool IsSelected {
        get { return _isSelected; }
        set {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
            // Elided: much boilerplate checking for redundancy, null parent, etc.
            if (IsSelected)
                Parent.SelectedVNodes.Add(this);
            else
                Parent.SelectedVNodes.Remove(this);
         }
     }