如何绑定到非ObservableCollection?

时间:2014-01-31 22:41:45

标签: wpf xaml data-binding mvvm observablecollection

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

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

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

我希望每次更新位置时都避免将PossibleMoves复制到ObservableCollection中。数据绑定适用于初始化,但如果我还可以在viewmodel中随意刷新绑定,那么它将非常方便。

从viewmodel调用OnNotifyPropertyChanged(“Position.PossibleMoves”)不起作用,因为对集合本身的引用不会改变。

2 个答案:

答案 0 :(得分: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)。

答案 1 :(得分:0)

您需要从Position类内部对PossibleMoves进行NotifyPropertyChange,或者创建一个委托给Position.PossibleMoves的属性并通知它。