在C# WPF中创建可绑定的点

7

我知道多重继承已经过时了,但是有没有一种方法可以创建一个包装器,使其可以从System.Windows.Point继承,但仍然实现可绑定的依赖属性?

我正在尝试编写代码,以便在我的XAML中可以轻松创建以下语句:

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

这将使编写Polygons、Paths、LineSegments和其他控件变得更加容易。

以下代码只是一种愿望,我知道它根本行不通,但这就是我想要做到的:

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;
        }
    }
}

4个回答

5

很不幸,Point是一个结构体,结构体不支持继承。来源于MSDN

注意 结构体不支持继承,但它们可以实现接口。更多信息请参见C#编程指南中的接口

也许这并没有直接回答你的问题,但你可以通过转换器轻松地绑定Point。在你的情况下,它会像这样

<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();
    }
}

如果您只想绑定X值并具有静态Y值,则可以这样做:
<SomeControl SomePointProperty="{Binding Path=Width,
                                         ElementName=ParentControlName,
                                         Converter={StaticResource PointXConverter},
                                         ConverterParameter=20}"/>
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();
    }
} 

谢谢您的快速回复,Meleak。目前我在我的代码中一直使用Rect和Point等结构体的转换器。这是我能想到的最好的解决办法了。我猜这是唯一真正的解决方法,非常感谢您的建议。我只是觉得奇怪,为什么他们会实现一个无法继承且无法绑定在控件上的结构体。 - Ross Graeber
@Ross Graeber 结构体是值类型,在某些情况下更有意义和高效。我认为结构体不支持继承的原因相当明显。 - Matěj Zábský
2
我并不质疑结构的实用性或相关性。我只是想知道为什么在这种特定的情况下使用它。例如,RotateTransformCenterXCenterY 属性,而不是一个中心点 Point。如果我想要动画旋转或将其字段链接到其他类和控件,这样做可以更轻松地绑定这些值。他们在那里放弃了 Point,我敢打赌是因为如果他们没有这样做,管理绑定会更加困难。 - Ross Graeber

0

我目前一直在使用转换器,但这可能是一个更广泛的修复问题,一直在困扰着我。此外,我很懒,不喜欢编写冗长的绑定代码段。我也想避免每次只想设置一个点时都要编写大量的多重绑定子句。 - Ross Graeber
@Ross Graeber:我遇到了相同的问题,但我没有找到任何解决办法,只有转换器。 - Y.Yanavichus

0

您可能需要从头开始重新实现所有类(PointPolygonLine 等),因为 Point 是一个结构体,因此它不支持继承(这也解释了为什么 Point 不支持绑定:它无法继承 DependencyObject,后者包含 DP 所需的基础设施)。

虽然可能有一种方式 - 您可以创建多边形的子类,并添加一个名为 "BindablePoints" 的新 DependencyProperty,它将是一组可观察的集合(您必须创建一个自定义 OC,该 OC 在一个点发生更改时会触发 CollectionChanged 事件)。该属性在其 OnChanged 中将更新 Polygon 的主要 Points 属性。

我想这可能有效,但我不确定它是否足够快以适应您尝试进行的操作。您仍然需要创建所有您想要使用的形状的子类,但您不必全部从头开始创建。


我正在使用WPF中的CAD样式应用程序,我认为我大多数时间只是在抱怨我想要做的事情以节省编码时间,但它却行不通。我可以重新实现我最常使用的类型,我相信最终这将节省我很多时间。但它并不能解决在这些控件中使用不可绑定结构作为属性的整体问题。 - Ross Graeber

0
  • 编写一个包装类,实现INotifyPropertyChanged接口,用于点的操作

    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));
        }
    }
    
  • 将起始点/终点的X和Y属性绑定到线条实例上

    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);
    

  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接