0

次のWPFコードがあります。OnValueChanged ハンドラーに問題があることがコメントでわかります。UI からの値セット (さまざまなバインディングを介して) とマネージャー クラスからの 1 つのセットを区別するために、そこにコードが必要です。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 に答える