TwoWay Collection绑定同步/锁定

时间:2014-10-28 23:29:42

标签: c# .net wpf binding synchronization

通过Binding同步两组数据的最佳方法是什么?

Target = Custom Setters - raises custom events whenever something changed
Source = ObservableCollection - raises events whenever collection changed

现在我的问题是,当我从一个集合(例如Source.CollectionChanged事件)收到更新时,我需要调用自定义TargetSetters,并忽略源自我的更新的调用事件。

另一方面,当Target自定义事件被触发时,我需要更新源,但忽略CollectionChanged事件。

目前,我正在保留对我的处理程序的引用,并在更新任何集合之前删除它。 e.g。

private void ObservableCollection_OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
    CustomObject.SelectionChanged -= CustomObject_SelectionChanged;
    // Do change logic and update Custom Object....
    CustomObject.SelectionChanged += CustomObject_SelectionChanged;
}

void CustomObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ObservableCollection.CollectionChanged -= ObservableCollection_OnCollectionChanged;
    // Do change logic and update ObservableCollection...
    ObservableCollection.CollectionChanged += ObservableCollection_OnCollectionChanged;
}

我已经看到你可以使用if语句检查更新是否来自源,以及是否忽略它们。 e.g。

private void ObservableCollection_OnCollectionChanged2(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
    if (BindingTargetUpdating) return;
    BindingSourceUpdating = true;
    // Do change logic and update Custom Object....
    BindingSourceUpdating = false;
}

void CustomObject_SelectionChanged2(object sender, SelectionChangedEventArgs e)
{
    if (BindingSourceUpdating) return;
    BindingTargetUpdating = true;
    // Do change logic and update ObservableCollection...
    BindingTargetUpdating = false;
}

在谷歌+ SO搜索没有任何回复后,我想看看其他人是如何做到这一点的,有什么非常简单的我在这里找不到解决这个问题的东西? (我知道这些示例不是线程安全的)

如果没有,首选方式是什么?删除和附加处理程序,或设置布尔标志?什么是更高性能(是的,我知道这不太可能导致瓶颈,但出于好奇心)

我要问的原因是因为,目前我正在实施附加行为,并且对于每个行为,我创建了两组字典,其中包含对每个对象的处理程序的引用,因为状态必须传递。

我似乎无法找到.NET Binding类的绑定机制的源代码,以了解MS如何实现它。如果有人有链接,那将非常感激。

1 个答案:

答案 0 :(得分:3)

您正在使用的机制 - 具有跟踪更新发生时间的布尔值以及围绕它进行阻塞的布尔值是最常用的方法。

就个人而言,我更喜欢将该逻辑包装到一个实现IDisposable的小实用程序中。这样可以更容易地保证您在自己之后始终清理。

您可以使用的实用程序如下所示:

class Guard : IDisposable
{
    readonly Func<bool> getter;
    readonly Action<bool> setter;

    readonly bool acquired = false;
    public Guard(Func<bool> getter, Action<bool> setter)
    {
        this.getter = getter;
        this.setter = setter;

        if (this.getter() == false)
        {
            this.setter(true);
            this.acquired = true;
        }
    }

    public bool Acquired { get { return this.acquired; } }

    void IDisposable.Dispose()
    {
        if (acquired)
        {
            this.setter(false);
        }
    }
}

然后你可以写:

private void ObservableCollection_OnCollectionChanged2(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
    using(var guard = new Guard(() => BindingTargetUpdating, v => BindingTargetUpdating = value))
    {
       if (guard.Acquired)
       {
           // Do change logic and update Custom Object....
       }
    }
}

这不一定更短 - 它可能写的时间更长,但确实提供了在发生异常时释放块的保证。如果您经常使用它,您可以始终将Guard子类化为缩小使用率。