如何绑定到非 ObservableCollection?

问题描述 投票:0回答:2

有没有办法直接绑定到模型中的 Collection 并手动告诉 WPF 绑定需要刷新,而不必在视图模型中为其创建 ObservableCollection?

<ListBox ItemsSource="{Binding Position.PossibleMoves}">
...
</ListBox>

Position 是我的模型,是国际象棋库的一部分,PossitionMoves 是其中的一个集合。我不想实现 INotifyProperty 更改或将 ObservableCollections 放入独立的优化库中。

我想避免每次更新位置时将PossibleMoves复制到ObservableCollection中。数据绑定在初始化时起作用,但如果我也可以在视图模型内随意刷新绑定,那就会很方便。

从视图模型调用 OnNotifyPropertyChanged("Position.PossibleMoves") 不起作用,因为对集合本身的引用不会更改。

wpf xaml data-binding mvvm observablecollection
2个回答
4
投票

您可以通过使用附加行为将处理程序绑定到在视图模型中触发的事件来实现此目的。不过,您不能直接绑定到事件,因此您必须将它们包装在一个类中,如下所示:

public class Refresher
{
    public delegate void RefreshDelegate();
    public event RefreshDelegate Refresh;

    public void DoRefresh()
    {
        if (this.Refresh != null)
            this.Refresh();
    }
}

现在将其实例添加到您的视图模型中:

public class MyViewModel
{
    public IList<string> Items { get; set; }
    
    private Refresher _Refresher = new Refresher();
    public Refresher Refresher {get {return this._Refresher;}}
}

接下来创建一个附加行为,该行为向该事件注册委托实例并强制列表框刷新其绑定:

public static class RefreshBehavior
{
    public static readonly DependencyProperty RefresherProperty = DependencyProperty.RegisterAttached(
        "Refresher",
        typeof(Refresher),
        typeof(RefreshBehavior),
        new PropertyMetadata(null, OnRefresherChange));

    public static void SetRefresher(DependencyObject source, Refresher value)
    {
        source.SetValue(RefresherProperty, value);
    }

    public static Refresher GetRefresher(DependencyObject source)
    {
        return (Refresher)source.GetValue(RefresherProperty);
    }

    private static void OnRefresherChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Refresher.RefreshDelegate handler = () =>
        {
            var listBox = d as ListBox;
            listBox.Items.Refresh();
        };

        if (e.NewValue != null)
            (e.NewValue as Refresher).Refresh += handler;
        if (e.OldValue != null)
            (e.OldValue as Refresher).Refresh -= handler;
    }
}

最后将其附加到 xaml 中的列表框:

<ListBox ItemsSource="{Binding Items}"
    local:RefreshBehavior.Refresher="{Binding Refresher}"/>

就是这样。在视图模型中调用

Refresher.DoRefresh()
,它将强制列表框更新。

这可行,但实际上是将方钉锤入圆孔。如果我是你,我会尽我所能尝试在你的视图模型中进行正确的集合更改通知。我理解您希望将 ObservableCollection 排除在模型之外,但有一些方法可以自动代理更改通知(例如 Castle DynamicProxy)。


0
投票

您需要从 Position 类内部为 possibleMoves 通知属性更改,或者创建一个委托给 Position.PossibleMoves 的属性并通知该属性。

© www.soinside.com 2019 - 2024. All rights reserved.