gpt4 book ai didi

c# - 在不违反 MVVM 的情况下将集合绑定(bind)到 ListBox 中的 SelectedItems

转载 作者:行者123 更新时间:2023-11-30 23:31:02 25 4
gpt4 key购买 nike

我有一个名为 SelectedVNodes' 的 ObservableCollection,它包含来自 ObservableCollection VNodes 的项目。

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

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

我已绑定(bind)我的属性以通过使用此 setter 保持在选择更改时更新

<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />

不过这就是我所知道的。我不知道如何根据此属性更改从 SelectedVNodes 列表中添加/删除此项目。

这是 VNode 类

public class VNode : NotifyBase
{
public string Name { get; set; }
public int Age { get; set; }
public int Kids { get; set; }

private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
Set(ref isSelected, value);
Console.WriteLine("selected/deselected");
}
}
}

NotifyBase 派生自 INotifyPropertyChanged。

最佳答案

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

<NonWhimsicalListBox
ItemsSource="{Binding VNodes}"
SelectedItems="{Binding SelectedVNodes}"
/>

但是如果你使用 System.Windows.Controls.ListBox ,你必须使用附加属性自己编写它,这实际上还不错。这里有很多代码,但几乎完全是样板文件(此附加属性中的大部分 C# 代码都是由 VS IDE 代码片段创建的)。这里的好处是它是通用的,任何随机路人都可以在任何 ListBox 上使用它那里面有任何东西。

public static class AttachedProperties
{
#region AttachedProperties.SelectedItems Attached Property
public static IList GetSelectedItems(ListBox obj)
{
return (IList)obj.GetValue(SelectedItemsProperty);
}

public static void SetSelectedItems(ListBox obj, IList value)
{
obj.SetValue(SelectedItemsProperty, value);
}

public static readonly DependencyProperty
SelectedItemsProperty =
DependencyProperty.RegisterAttached(
"SelectedItems",
typeof(IList),
typeof(AttachedProperties),
new PropertyMetadata(null,
SelectedItems_PropertyChanged));

private static void SelectedItems_PropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var lb = d as ListBox;
IList coll = e.NewValue as IList;

// If you want to go both ways and have changes to
// this collection reflected back into the listbox...
if (coll is INotifyCollectionChanged)
{
(coll as INotifyCollectionChanged)
.CollectionChanged += (s, e3) =>
{
// Haven't tested this branch -- good luck!
if (null != e3.OldItems)
foreach (var item in e3.OldItems)
lb.SelectedItems.Remove(item);
if (null != e3.NewItems)
foreach (var item in e3.NewItems)
lb.SelectedItems.Add(item);
};
}

if (null != coll)
{
if (coll.Count > 0)
{
// Minor problem here: This doesn't work for initializing a
// selection on control creation.
// When I get here, it's because I've initialized the selected
// items collection that I'm binding. But at that point, lb.Items
// isn't populated yet, so adding these items to lb.SelectedItems
// always fails.
// Haven't tested this otherwise -- good luck!
lb.SelectedItems.Clear();
foreach (var item in coll)
lb.SelectedItems.Add(item);
}

lb.SelectionChanged += (s, e2) =>
{
if (null != e2.RemovedItems)
foreach (var item in e2.RemovedItems)
coll.Remove(item);
if (null != e2.AddedItems)
foreach (var item in e2.AddedItems)
coll.Add(item);
};
}
}
#endregion AttachedProperties.SelectedItems Attached Property
}

假设AttachedProperties在 XAML 中的任何“local:”命名空间中定义...

<ListBox 
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
local:AttachedProperties.SelectedItems="{Binding SelectedVNodes}"
/>

View 模型:

private ObservableCollection<Node> _selectedVNodes 
= new ObservableCollection<Node>();
public ObservableCollection<Node> SelectedVNodes
{
get
{
return _selectedVNodes;
}
}

如果你不想去那里,我可以想到三种三种半直接的方式来做到这一点:

  1. 当父 View 模型创建一个 VNode 时, 它向新的 VNode 添加了一个处理程序的 PropertyChanged事件。在处理程序中,它添加/删除 sender来自 SelectedVNodes根据 (bool)e.NewValue

    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
  2. 执行该事件,但不是添加/删除,而是重新创建 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)
    );
    }
    };
  3. 做那个事件,但不要做 SelectedVNodes完全可观察:

    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); }
    }
  4. VNode父属性。当父 View 模型创建一个 VNode , 它给出每个 VNodeSelectedVNodes 所有者的父引用(大概是它自己)。在 VNode.IsSelected.set ,VNode 在 Parent.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如果你有大量的元素。另一方面,它免除了你做这两种方式的责任,即如果一些消费者搞砸了 SelectedVNodes直接,你真的应该处理它的CollectionChanged事件并更新 VNodes有问题。当然,你必须确保你不会意外地递归:不要将一个添加到已经存在的集合中,也不要设置 vn.IsSelected = true。如果vn.IsSelected已经是真的了。如果你的眼睛现在像我一样呆滞,你开始感觉到墙壁正在关闭,请允许我推荐选项 #3。

也许 SelectedVNodes应该公开暴露ReadOnlyObservableCollection<VNode> ,让你摆脱困境。在这种情况下,1 号是您最好的选择,因为 VNodes将无法访问 VM 的私有(private)可变 ObservableCollection<VNode> .

但您可以选择。

关于c# - 在不违反 MVVM 的情况下将集合绑定(bind)到 ListBox 中的 SelectedItems,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34748875/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com