0

我有以下 WPF 代码。您可以在评论中看到我的 OnValueChanged 处理程序有问题。我需要那里的代码来区分来自 UI 的值集(通过各种绑定)和来自管理器类的值集。我曾希望 DependencyPropertyChangedEventArgs 有某种来源可以用来区分这一点,但我没有看到类似的东西。想法?有没有办法在不触发其 PropertyChanged 处理程序的情况下设置 WPF DependencyProperty?谢谢你的时间。

public class GaugeBaseControl : UserControl
{
    protected readonly AssetModelManager Manager;
    public GaugeBaseControl(AssetModelManager mgr)
    {
        Manager = mgr;
        if(mgr != null)
            mgr.TelemetryValueChanged += MgrOnTelemetryValueChanged; // coming on background thread
    }

    private void MgrOnTelemetryValueChanged(KeyValuePair<string, object> keyValuePair)
    {
        if(_localTelemetryId != keyValuePair.Key)
            return;

        Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
        {
            if (!Equals(Value, keyValuePair.Value))
                Value = keyValuePair.Value;
        }));
    }

    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var gbc = (GaugeBaseControl) d;
        var id = gbc.TelemetryId;
        if (!string.IsNullOrEmpty(id))
        {
            // this is the problem:
            // I need to always set gbc.Manager[id] if this event was triggered from the UI (even when equal)
            // however, if it was triggered by TelemetryValueChanged then we don't want to go around in circles
            if (!Equals(gbc.Manager[id], e.NewValue))
                gbc.Manager[id] = e.NewValue;
        }
    }

    private string _localTelemetryId; // to save us a cross-thread check
    private static void OnTelemetryIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var gbc = (GaugeBaseControl)d;
        var tid = gbc.TelemetryId;
        gbc._localTelemetryId = tid;
        gbc.Value = string.IsNullOrEmpty(tid) ? null : gbc.Manager[tid];
    }

    public static readonly DependencyProperty TelmetryIdProperty = DependencyProperty.Register("TelemetryId", typeof(string), typeof(GaugeBaseControl), new PropertyMetadata(OnTelemetryIdChanged));
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(GaugeBaseControl), new PropertyMetadata(OnValueChanged));

    public object Value
    {
        get { return GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value);}
    }

    public string TelemetryId
    {
        get { return (string)GetValue(TelmetryIdProperty); }
        set { SetValue(TelmetryIdProperty, value); }
    }
}
4

1 回答 1

0

这似乎有点骇人听闻,但这是我在不改变架构的情况下能想到的最好的方法。您可以在进行内部更新以停止往返时停止监听 TelemetryValueChanged 事件,如下所示:

internal void SetManagerIdInternal(string id, object value)
{
    if(mgr != null)
    {
        mgr.TelemetryValueChanged -= MgrOnTelemetryValueChanged;
        mgr[id] = value;
        mgr.TelemetryValueChanged += MgrOnTelemetryValueChanged;
    }
}

并像这样使用它:

if (!Equals(gbc.Manager[id], e.NewValue))
    SetManagerIdInternal(id, e.NewValue);

您也可以使用私有字段跳过工作而无需在 MgrOnTelemetryValueChanged 中取消注册/重新注册事件,这可能会提高性能,但我还没有测试过。

于 2012-10-19T17:46:18.687 回答