【问题标题】:Creating a bindable Point in C# WPF在 C# WPF 中创建可绑定点
【发布时间】:2011-07-03 17:51:44
【问题描述】:

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

我正在尝试编写代码,以便为我的 XAML 创建如下语句而不会出现问题:

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

这将使像多边形、路径、线段和其他控件这样的编码变得更加容易。

以下代码是一厢情愿的想法,我知道它绝不会起作用,但这是我希望能够做的事情:

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

【问题讨论】:

    标签: c# wpf inheritance dependency-properties


    【解决方案1】:

    不幸的是,Point 是一个结构体,而结构体不支持继承。来自MSDN

    注意结构体不支持 继承,但他们可以实现 接口。有关详细信息,请参阅 Interfaces (C# Programming Guide).

    也许这不能直接回答您的问题,但您可以在转换器的帮助下轻松绑定Point。在你的情况下,它会像

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

    点转换器

    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}"/>
    

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

    【讨论】:

    • 感谢您的快速回复,Meleak。在我的代码中,我一直在使用像 Rect 和 Point 这样的结构的转换器。对于糟糕的情况,这也是我能想到的最好的办法。这是一个很好的建议,因为我猜这是唯一真正的解决方法。我只是觉得很奇怪,因为他们会实现一个不可继承的结构,无法绑定到他们的控件上。
    • @Ross Graeber 结构是在某些情况下更有意义和更有效的值类型。而且我认为为什么结构不支持继承是相当明显的。
    • @mzabsky 我不是在讨论结构的有用性或它的相关性。我只是想知道为什么在这种特定情况下使用它。例如,RotateTransform 具有属性CenterXCenterY 而不是中心Point。如果我说想要动画旋转或将其字段链接到其他类和控件,这允许更容易绑定这些值。他们在那里选择退出Point,我敢打赌,因为如果他们不这样做,管理绑定会困难得多。
    【解决方案2】:

    尝试改用Converters

    【讨论】:

    • 我目前一直在使用转换器,这只是让我感到困扰的事情之一,因为它可能有更广泛的修复。那,我很懒,不喜欢写长的绑定段。我还想避免每次只想设置一个点时都必须编写一个大型的多重绑定子句。
    • @Ross Graeber:我遇到了同样的问题,但我没有找到任何解决方案,只有转换器。
    【解决方案3】:

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

    可能有一种方法 - 您可以创建一个多边形的子类并添加一个名为“BindablePoints”的新 DependencyProperty,这将是一个可观察的集合(您必须创建一个自定义 OC 来触发 @987654328 @当其中一个点发生变化时)的点。该属性将在其OnChanged 中更新Polygon 的主要Points 属性。

    我想这可能会奏效,但我不确定它是否足够快,无论您尝试做什么。 Tou 仍然需要为您想要使用的所有形状创建子类,但您不必从头开始创建它们。

    【讨论】:

    • 我正在 WPF 中使用 CAD 样式的应用程序,我想我主要是在抱怨我想要做的事情来节省编码时间是行不通的。我可以重新实现我最常用的类型,我相信最终它会为我节省很多时间。它只是没有解决在这些控件中使用不可绑定结构作为属性的整体问题。
    【解决方案4】:
    1. 为实现 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));
          }
      }
      
    2. 例如将起点/终点 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);
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-10
      • 1970-01-01
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多