行为和双向绑定——如何判断是什么改变了 DependencyProperty

Behaviors and two way binding - how to tell what changed the DependencyProperty

我有一个自定义行为,允许我将 MapControl 范围(边界框)绑定到 ViewModel;它通过在地图的 LoadingStatusChanged 事件触发时计算地图的范围并设置 DependencyProperty 的值来工作。这工作正常——当地图被平移或缩放时,ViewModel 中的绑定 属性 得到更新。

但我还希望能够从 ViewModel 设置地图的范围并相应地拥有地图 pan/zoom。当依赖项 属性 发生变化时,我可以调用 TrySetViewBoundsAsync,但问题是知道依赖项 属性 的变化是源于计算地图的范围,还是源于设置 属性在视图模型中。如果是计算范围,就会改变地图的范围,又会触发changed事件,死循环。

我通过添加 IsFromEvent 属性 并在响应 LoadingStatusChanged 事件计算范围时将其设置为 true 来解决这个问题。这可以防止无限循环,但这种方法似乎......关闭。它给我的印象是骇人听闻且线程安全性不强。

所以...有更好的方法吗?是否可以在不触发 PropertyChanged 事件的情况下以某种方式设置 DP?还是我想多了?

public class ExtentBehavior : DependencyObject, IBehavior
{
    public DependencyObject AssociatedObject { get; private set; }

    public void Attach(Windows.UI.Xaml.DependencyObject associatedObject)
    {
        AssociatedObject = associatedObject;

        var mapControl = associatedObject as MapControl;
        mapControl.LoadingStatusChanged += MapLoadingStatusChanged;
    }

    public static readonly DependencyProperty ExtentProperty =
        DependencyProperty.Register("Extent", typeof(MapControl), typeof(ExtentBehavior), new PropertyMetadata(null, OnExtentPropertyChanged));

    public GeoboundingBox Extent
    {
        get { return GetValue(ExtentProperty) as GeoboundingBox; }
        set { SetValue(ExtentProperty, value); }
    }

    private async static void OnExtentPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var behavior = dependencyObject as ExtentBehavior;
        var mapControl = behavior.AssociatedObject as MapControl;

        if (!behavior.IsFromEvent)
        {
            var result = await mapControl.TrySetViewBoundsAsync(behavior.Extent, null, MapAnimationKind.Default);
        }

        behavior.IsFromEvent = false;
    }

    private bool IsFromEvent { get; set; }

    private void MapLoadingStatusChanged(MapControl sender, object args)
    {
        if (sender.LoadingStatus == MapLoadingStatus.Loaded)
        {
            IsFromEvent = true;
            Extent = GetBounds(sender);
        }
    }

    private GeoboundingBox GetBounds(MapControl mapControl)
    {
        // code omitted
    }
}

无法询问谁更改了依赖项的值 属性。话虽如此,还是有办法的;你可以走回电话 stack/trace。 XAML 开发人员多年来一直这样做,试图在没有文字字符串的情况下动态引发 属性changed。您的解决方案比像这样使用反射要优雅 1,000%。

有时我们必须用解决方案来解决问题 - 有时解决方案不是基本框架的一部分。您的解决方案可能感觉有点 off 但它解决了问题本身就是一个功劳。此外,我认为 "thread safe" 在这种情况下不是相关问题,因为所有 XAML 都在 UI 线程上。有道理吗?

祝你好运。