0

I want to model the behavior of a RangeSlider, using a class RangeSliderModel with properties describing the sizes (widths) for a given set of values.

Then, I created an UserControl where the position and size of some rectangles should represent those properties, and some instances of Thumb from which the DragDelta events are used to resize these rectangles and enable the calculation of a new value for a RangeSliderModel.

At last, this UserControl has a DependencyProperty of type RangeSliderModel:

    public RangeSliderModel Model
    {
        get
        {
            return (RangeSliderModel)GetValue(RangeSliderModelProperty);
            // or instead calculate a RangeSliderModel from children sizes.
        }
        set
        {
            // this IS NOT called when UserControl loads
            SetValue(RangeSliderModelProperty, value);
            // here I could take the value and calculate new sizes for the children.
        }
    }

    public static readonly DependencyProperty RangeSliderModelProperty =
        DependencyProperty.Register("RangeSliderModel",
                                    typeof(RangeSliderModel),
                                    typeof(MyUserControl),
                                    new PropertyMetadata(ModelPropertyChanged));

    private static void ModelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // this IS called when UserControl loads.
        RangeSliderModel model = e.NewValue as RangeSliderModel;
    }

I was expecting to override the SetValue and GetValue properties, but even when I bind this DependencyProperty to a corresponding Source in ViewModel, the setter is never called! And I can't quite understand why PropertyChangedHandler is called instead, and much less what I should do with this information - I was expecting to use just GetValue and SetValue in a way similar to a ValueConverter.

heltonbiker
  • 26,657
  • 28
  • 137
  • 252

1 Answers1

1

Dependency properties don't work that way when you set them through bindings. DependencyObject.SetCurrentValue is called. The Binding is calling (in essence):

yourObject.SetCurrentValue(YourClassName.RangeSliderModelProperty, someValue);

So, yeah, SetCurrentValue vs SetValue. SetValue will overwrite any binding on the dependency property; SetCurrentValue won't. IIRC this has to do with the way Style Triggers in XAML can't override values set by attributes. It's not directly relevant to what you're doing here, fortunately.

The bottom line is that your ModelPropertyChanged handler is where you put code that needs to get called when the value changes. Dependency properties and regular .NET CLR properties are two independent mechanisms.

I have had dependency properties bind correctly in XAML if I left out the public property entirely; I just tested that again and found it to be the case for my test example. In comments you mention that you did not find this to be the case with your code, so clearly I need to do some more reading.

If you need to have subclasses respond to changes, you can either write a virtual method they can override, and call it in ModelPropertyChanged, or else write a ModelChanged event and raise that in ModelPropertyChanged.

They could add a ValueChanged handler via a DependencyPropertyDescriptor, but it'd be right neighborly of you to spare them the trouble of googling how to that, by writing a virtual method or event.

Community
  • 1
  • 1
  • Fine, I started to use SetValue directly instead of the regular setter, and that worked fine. On the other hand, deleting the regular setter/getter did break the XAML binding (I guess it was expecting the given property name), so I let it be. Now things are working as expected, thanks! – heltonbiker Jun 28 '16 at 17:48
  • @heltonbiker Hmm! Let me test that and make sure I'm not spouting nonsense. Thanks! – 15ee8f99-57ff-4f92-890c-b56153 Jun 28 '16 at 17:49
  • Actually, it's not calling `SetValue`, it calls `SetBinding`. – H.B. Jun 28 '16 at 17:54
  • @H.B. Are you saying that you set a binding on a dependency property by calling BindingOperations.SetBinding, or are you saying that you set the value of a dependency property by calling DependencyObject.SetBinding? – 15ee8f99-57ff-4f92-890c-b56153 Jun 28 '16 at 17:56
  • 1
    The XAML parser calls `SetBinding`, `DependencyObject.SetBinding` is just a shortcut for the former as far as i know. I think the binding itself will call `SetCurrentValue`, which unlike `SetValue` does not remove the existing binding. – H.B. Jun 28 '16 at 17:59
  • @H.B. Are you thinking SetCurrentValue? OMG I should've kept my mouth shut, this stuff is a nightmare. – 15ee8f99-57ff-4f92-890c-b56153 Jun 28 '16 at 18:02
  • Yes, just looked at docs again right now myself... – H.B. Jun 28 '16 at 18:03