We have a DependencyObject which defines a Value property as a DP. It also defines a Presets collection representing friendly names for some pre-defined values.

The way our UI should work is when we bind to the Value property, if the value matches a preset, we show the friendly name, otherwise we just show the value directly.

Using a converter is out since there's no reliable way to both pass in the Presets (which are defined per-item, they aren't shared) and also do two-way binding, so our thought is to expose a FriendlyValue property on the object and use that for binding in the UI, letting it handle the conversion internally.

Since FriendlyValue depends on the already-existing Value DependencyProperty, we figured we'd just wrap the conversion logic in CLR getters/setters, but that means when the actual Value DP changes, it needs to notify the UI that FriendlyValue has been updated too, and since FriendlyValue is a CLR property, we need to support INPC for that specific property.

My question is, is that the correct/suggested way to handle this, or should I go with a second DP, monitoring its change handlers and setting the other property accordingly, adding state variables to stop one from setting the other, which then sets the first, which then again updates the other, etc.

Here's the code for the object's properties...

public static readonly DependencyProperty PresetsProperty = DependencyProperty.Register(
    "Presets",
    typeof(List<Preset>),
    typeof(MyObject),
    new UIPropertyMetadata(null));

public List<Preset> Presets
{
    get { return (List<Preset>)GetValue(PresetsProperty); }
    set { SetValue(PresetsProperty, value); }
}

public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
    "Value",
    typeof(string),
    typeof(MyObject),
    new UIPropertyMetadata(null, (s,e) => {

        var myObject = (MyObject)s;

        myObject.OnPropertyChanged("FriendlyValue");

    }));

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


public string FriendlyValue
{
    get
    {
        var foundPreset = Presets.FirstOrDefault(preset => preset.Value == this.Value);
        return (foundPreset != null)
            ? foundPreset.FriendlyName
            : this.Value;
    }
    set
    {
        var foundPreset = Presets.FirstOrDefault(preset => preset.FriendlyName == value);

        this.Value = (foundPreset != null)
            ? foundPreset.Value
            : value;

        // Note: We don't raise INPC notification here.  It's raised in the Value's change handler
    }
}

#region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

#endregion

So is this considered good practice for built-in converter behavior?

有帮助吗?

解决方案

Why not make both Value and FriendlyValue dependency properties? I see no reasons to use two techniques at the same time.

Consider:

using Preset = Tuple<string, string>;

public class MyObject : DependencyObject
{
    private readonly IList<Tuple<string, string>> _presets = new List<Preset> {
        new Preset("1", "good"),
        new Preset("2", "bad"),
    };

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        "Value", typeof(string), typeof(MyObject),
        new PropertyMetadata(null,
            (o, e) => ((MyObject)o).ValuePropertyChanged((string)e.NewValue)));
    private static readonly DependencyProperty FriendlyValueProperty = DependencyProperty.Register(
        "FriendlyValue", typeof(string), typeof(MyObject),
        new PropertyMetadata(null,
            (o, e) => ((MyObject)o).FriendlyValuePropertyChanged((string)e.NewValue)));

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

    public string FriendlyValue
    {
        get { return (string)GetValue(FriendlyValueProperty); }
        set { SetValue(FriendlyValueProperty, value); }
    }

    private void ValuePropertyChanged (string newValue)
    {
        var preset = _presets.FirstOrDefault(p => p.Item1 == newValue);
        FriendlyValue = preset != null ? preset.Item2 : newValue;
    }

    private void FriendlyValuePropertyChanged (string newValue)
    {
        var preset = _presets.FirstOrDefault(p => p.Item2 == newValue);
        Value = preset != null ? preset.Item1 : newValue;
    }
}

Notes:

  1. Code for presets simplified for brevity.
  2. This will not cause stack overflow, because change callback are not called if value is not changed.

I hope I understood your logic correctly. There's one issue: if you change FriendlyValue to one of the friendly values from the presets, this will change Value to the value from the found preset and in turn change FriendlyValue to the name of the preset. I don't know if that behavior is expected.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top