What is the proper way to disallow an invalid WPF DependencyProperty change?

45 Views Asked by At

My custom control shows an image. It has event handlers that allow the user to pan/drag the image and zoom it in or out. It exposes two DependencyProperties

  • ImageCenter - The current center point of the control in Image coordinates
  • ImageRect - The current bounds of the control in Image coordinates

The client is allowed to bind to both of these properties simultaneously, but if they do, at least one of those bindings MUST use BindingMode.OneWayToSource. So you can force the view to show the image centered on a certain coordinate (without changing zoom) or you can force the view to show a specific Rect of the image (so changing zoom and panning at the same time) but you cannot do both.

(This reminds me a bit of how you may bind ItemsControl.ItemsSource or explicitly set its children in XAML but you may not do both. This is still more complicated behavior than I would like but for now I feel I need it.)

Given this requirement, what is the proper protocol to disallow a violation of the binding rule?

  • I assume I need to check the binding in PropertyChangedCallback function for each property, is that correct? I would prefer to do so less frequently but I do not see any sort of BindingSet or BindingCleared event that I can hook on to. And the ValidateValueCallback signature won't permit this sort of check.
  • When I detect an invalid situation, what is the proper thing to do? Throw an exception?
public static readonly DependencyProperty ImageCenterProperty =
    DependencyProperty.Register(
        nameof(ImageCenter),
        typeof(Point),
        typeof(LayerView),
        new FrameworkPropertyMetadata(
            new Point(-1, -1),
            FrameworkPropertyMetadataOptions.AffectsRender,
            (o, a) => { (o as LayerView)?.OnImageCenterChanged(a); }));

private void OnImageCenterChanged(DependencyPropertyChangedEventArgs args)
{
    var bebCenter = BindingOperations.GetBindingExpressionBase(this, ImageCenterProperty);
    var bebRect = BindingOperations.GetBindingExpressionBase(this, ImageRectProperty);

    if (bebCenter != null && bebRect != null)
    {
        // Code here to 
        //   a) get the specific binding type and mode of each binding
        //   b) check to see if one of them is not readonly and if so...
        //   c) ... do what?  Throw an exception?
    }

}

public Point ImageCenter
{
    get => (Point)GetValue(ImageCenterProperty);
    set => SetValue(ImageCenterProperty, value);
}

public static readonly DependencyProperty ImageRectProperty =
    DependencyProperty.Register(
        nameof(ImageRect),
        typeof(Rect),
        typeof(LayerView),
        new FrameworkPropertyMetadata(
            Rect.Empty,
            FrameworkPropertyMetadataOptions.AffectsRender,
            (o, a) => { (o as LayerView)?.OnImageRectChanged(a); }));

private void OnImageRectChanged(DependencyPropertyChangedEventArgs args)
{
    var bebCenter = BindingOperations.GetBindingExpressionBase(this, ImageCenterProperty);
    var bebRect = BindingOperations.GetBindingExpressionBase(this, ImageRectProperty);

    if (bebCenter != null && bebRect != null)
    {
        // Code here to 
        //   a) get the specific binding type and mode of each binding
        //   b) check to see if one of them is not readonly and if so...
        //   c) ... do what?  Throw an exception?
    }

}

public Rect ImageRect
{
    get => (Rect)GetValue(ImageRectProperty);
    set => SetValue(ImageRectProperty, value);
}


0

There are 0 best solutions below