7

I know multiple inheritence is out, but is there a way to create a wrapper for System.Windows.Point that can inherit from it but still implement bindable dependency properties?

I'm trying to code so that for my XAML I can create staments like the following without issue:

<custom:Point X="{Binding Width, ElementName=ParentControlName}" Y="{Binding Height, ElementName=ParentControlName}" />

It would make coding things like Polygons, Paths, LineSegments and other controls so much easier.

The following code is provided as wishful thinking and I understand that it will in no way work, but this is the kind of thing I want to be able to do:

public class BindablePoint: DependencyObject, Point
{
    public static readonly DependencyProperty XProperty =
    DependencyProperty.Register("X", typeof(double), typeof(BindablePoint),
    new FrameworkPropertyMetadata(default(double), (sender, e) =>
    {
        BindablePoint point = sender as BindablePoint;
        point.X = (double) e.NewValue;
    }));

    public static readonly DependencyProperty YProperty =
    DependencyProperty.Register("Y", typeof(double), typeof(BindablePoint),
    new FrameworkPropertyMetadata(default(double), (sender, e) =>
    {
        BindablePoint point = sender as BindablePoint;
        point.Y = (double)e.NewValue;
    }));

    public new double X
    {
        get { return (double)GetValue(XProperty); }
        set 
        { 
            SetValue(XProperty, value);
            base.X = value;
        }
    }

    public new double Y
    {
        get { return (double)GetValue(YProperty); }
        set
        {
            SetValue(YProperty, value);
            base.Y = value;
        }
    }
}

Ross Graeber
  • 179
  • 2
  • 10

4 Answers4

5

Unfortunately, Point is a struct, and structs don't support inheritance. From MSDN

Note Structs do not support inheritance, but they can implement interfaces. For more information, see Interfaces (C# Programming Guide).

Maybe this doesn't answer your question directly but you can easily bind a Point with the help of a Converter. In your case it would be like

<SomeControl.SomePointProperty>
    <MultiBinding Converter="{StaticResource PointConverter}">
        <Binding ElementName="ParentControlName"
                 Path="Width"/>
        <Binding ElementName="ParentControlName"
                 Path="Height"/>
    </MultiBinding>                                        
</SomeControl.SomePointProperty>

PointConverter

public class PointConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double xValue = (double)values[0];
        double yValue = (double)values[1];
        return new Point(xValue, yValue);
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

If you just want to bind the X value and have a static Y value you could do this like

<SomeControl SomePointProperty="{Binding Path=Width,
                                         ElementName=ParentControlName,
                                         Converter={StaticResource PointXConverter},
                                         ConverterParameter=20}"/>

PointXConverter

public class PointXConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double progressBarValue = (double)value;
        double yValue = System.Convert.ToDouble(parameter);
        return new Point(progressBarValue, yValue);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
} 
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • Thank you for the quick reply, Meleak. I have been using converters for structures like Rect and Point for the time being in my code. It's the best I can come up with for a bad situation as well. This is great advice for what I'm guessing is the only real workaround. I just find it strange for they would implement an uninheritable structure with no ability to be bound on their controls. – Ross Graeber Feb 24 '11 at 18:11
  • @Ross Graeber Structures are value types which are much more meaningful and efficient in some context. And I think why structures don't support inheritance is rather obvious. – Matěj Zábský Feb 24 '11 at 18:27
  • 2
    @mzabsky I'm not debating the usefulness of a structure or it's relevance. I'm just wondering why it is used in this particular context. For example, the `RotateTransform` has the properties `CenterX` and `CenterY` instead of a Center `Point`. This allows for much easier binding of those values if I say want to animate a rotation or link it's field to other classes and controls. They opted out of `Point` there and I'm betting because it's a lot harder to manage the bindings if they didn't. – Ross Graeber Feb 24 '11 at 18:37
0
  1. Write a wrapper class for point which implements the INotifyPropertyChanged interface

    public class BindingPoint : INotifyPropertyChanged
    {
        private Point point;
    
        public BindingPoint(double x, double y)
        {
            point = new Point(x, y);
        }
    
        public double X
        {
            get { return point.X; }
            set
            {
                point.X = value;
                OnPropertyChanged();
                OnPropertyChanged("Point");
            }
        }
    
        public double Y
        {
            get { return point.Y; }
            set
            {
                point.Y = value;
                OnPropertyChanged();
                OnPropertyChanged("Point");
            }
        }
    
        public Point Point
        {
            get { return point; }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
  2. Bind startpoint / endPoint X and Y properties for example to a line instance

    Line line = new Line();
    BindingPoint startPoint = new BindingPoint(0,0);
    BindingPoint endPoint = new BindingPoint(0,0);
    
    var b = new Binding("X")
    {
        Source = startPoint,
        Mode = BindingMode.TwoWay
    };
    line.SetBinding(Line.X1Property, b);
    
    b = new Binding("Y")
    {
        Source = startPoint,
        Mode = BindingMode.TwoWay
    };
    line.SetBinding(Line.Y1Property, b);
    
    b = new Binding("X")
    {
        Source = endPoint,
        Mode = BindingMode.TwoWay
    };
    line.SetBinding(Line.X2Property, b);
    
    b = new Binding("Y")
    {
        Source = endPoint,
        Mode = BindingMode.TwoWay
    };
    line.SetBinding(Line.Y2Property, b);
    
vplmaster
  • 131
  • 1
  • 5
0

Try to use Converters instead.

Y.Yanavichus
  • 2,387
  • 1
  • 21
  • 34
  • I have been using converters currently, this has just been one of those things that's eating at me as possibly having wider reaching fix. That, and I'm lazy and don't like having to write long binding segments. I'd also like to avoid having to write a large multibinding clause every time I want to just set one point. – Ross Graeber Feb 24 '11 at 18:25
  • @Ross Graeber: I encountered the same issue, but I didn't find any solution, only Converters. – Y.Yanavichus Feb 25 '11 at 04:32
0

You may have to reimplement all the classes (Point, Polygon, Line etc.) from scratch, because Point is a structure, therefore it doesn't support inheritance (which also explains why Point does not support binding: it cannot inherit DependencyObject, which contains the necessary infrastructure for DPs).

There might be a way though - you could create a subclass of polygon and add a new DependencyProperty called "BindablePoints" which would be an observable collection (you would have to create a custom OC that would fire CollectionChanged when one of the points changed) of the points. The property would in its OnChanged update the main Points property of the Polygon.

I guess this could work, but I'm not sure whether it would be fast enough for whatever you are trying to do. Tou would still have to create subclasses of all the shapes you would want to use, but you wouldn't have to create them all from scratch.

Matěj Zábský
  • 16,909
  • 15
  • 69
  • 114
  • I'm wokring with a CAD style application in WPF, I think I'm mostly just whining that something I want to do to save coding time just won't work. I could go the road of a reimplementation for the types I use the most and I'm sure in the end it would save me a lot of time. It just doesn't solve the overall issue of using unbindable sructures as properties in these controls. – Ross Graeber Feb 24 '11 at 18:22