【问题标题】:TwoWay Collection Binding Sync/Lock双向集合绑定同步/锁定
【发布时间】:2014-12-24 13:32:38
【问题描述】:

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

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

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

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

目前,我保留对我的处理程序的引用,并在更新任何集合之前将其删除。例如

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 语句来检查更新是否来自源,以及是否忽略它们。例如

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;
}

在 Google + SO 搜索一无所获后,我想看看其他人是如何做到这一点的,我在这里缺少什么真正简单的东西可以解决这个问题吗? (我知道这些例子不是线程安全的)

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

我问的原因是,目前我正在实现附加行为,并且对于每个行为,我正在创建 2 组字典,它们保存对每个对象的处理程序的引用,因为必须传递状态。

我似乎找不到 .NET Binding 类的绑定机制的源代码,看看 MS 是如何实现它的。如果有人有这些链接,将不胜感激。

【问题讨论】:

  • 抱歉,我可能遗漏了一些东西,但为什么您的设计要求您忽略每次发生变化时触发的一些事件?我真的无法弄清楚你想要完成什么。您能否说明您要解决的问题是什么?
  • 因为如果你订阅双方,你会收到双方的通知。导致无限循环。例如。 Customibject 引发它更改的事件,我的处理程序被调用并更改绑定的集合,这反过来引发更改的事件,因此我的集合更改处理程序被调用,它编辑自定义对象。然后这再次引发事件,你在一个循环中
  • 是的,我的意思是,在设计程序时必须故意阻止它进入无限循环,这是一种相当大的代码味道。您是否有理由不只是通过 INotifyPropertyChanged 处理对集合的更改,还是我误解了您的目的?
  • 它们由 INotifyPropertyChanged 处理,我需要将 DataGrid.SelectedCells、ScrollViewer.Horizo​​ntalOffset/ScrollViewer.VerticalOffset 绑定到 TwoWay 可观察集合/对象。所有这些属性都是只读的,所以不,普通绑定不起作用,它可能以一种方式起作用,但我需要将更新从我的 ViewModel 推送到我的 View
  • 我通常看到这是通过标志或某种using范围设备完成的。

标签: c# .net wpf binding synchronization


【解决方案1】:

您正在使用的机制 - 使用一个布尔值来跟踪更新发生的时间,并在它周围进行阻塞,这是最常见的方法。

就个人而言,我更喜欢将该逻辑包装到一个实现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,您可以随时将其子类化以减少使用量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    相关资源
    最近更新 更多