WPF中Polygon.Points绑定替代方案:基于线段坐标生成顶点
可行解决方案整理
方案一:正确使用MultiValueConverter绑定Points
你之前多绑定失败,多半是转换器实现或者绑定写法不对,下面是完整的正确操作步骤:
- 编写MultiValueConverter
实现IMultiValueConverter接口,将X1、Y1、X2、Y2四个值转换为多边形顶点集合,示例以线段中点的三角形为例,你可以根据需求修改顶点计算逻辑:
public class LineToPolygonPointsConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length != 4 || values.Any(v => v == DependencyProperty.UnsetValue)) return new PointCollection(); double x1 = (double)values[0]; double y1 = (double)values[1]; double x2 = (double)values[2]; double y2 = (double)values[3]; // 计算线段中点 var midX = (x1 + x2) / 2; var midY = (y1 + y2) / 2; // 生成线段垂直方向的向量,用于确定多边形顶点位置 var dx = x2 - x1; var dy = y2 - y1; var perpDx = -dy; var perpDy = dx; // 调整多边形大小,10为顶点偏移量,可按需修改 var length = Math.Sqrt(perpDx * perpDx + perpDy * perpDy); if (length == 0) return new PointCollection(); var scale = 10 / length; perpDx *= scale; perpDy *= scale; return new PointCollection { new Point(midX, midY), new Point(midX + perpDx, midY + perpDy), new Point(midX - perpDx, midY - perpDy) }; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); // 反向转换无需实现 } }
- 在XAML中配置绑定
先在资源中声明转换器,再给Polygon的Points属性设置多绑定:
<Window.Resources> <!-- local为转换器所在命名空间,记得替换成你的实际命名空间 --> <local:LineToPolygonPointsConverter x:Key="LineToPolygonConverter"/> </Window.Resources> <Polygon Fill="Black"> <Polygon.Points> <MultiBinding Converter="{StaticResource LineToPolygonConverter}"> <Binding Path="X1"/> <Binding Path="Y1"/> <Binding Path="X2"/> <Binding Path="Y2"/> </MultiBinding> </Polygon.Points> </Polygon>
这样X1/Y1/X2/Y2任一值变化时,转换器都会自动重新计算多边形顶点。
方案二:ViewModel中添加计算属性(无需转换器)
如果不想使用转换器,可直接在ViewModel中添加只读的PolygonPoints属性,依赖X1/Y1/X2/Y2的变化自动更新:
public PointCollection PolygonPoints { get { var midX = (X1 + X2) / 2; var midY = (Y1 + Y2) / 2; var dx = X2 - X1; var dy = Y2 - Y1; var perpDx = -dy; var perpDy = dx; var length = Math.Sqrt(perpDx * perpDx + perpDy * perpDy); if (length == 0) return new PointCollection(); var scale = 10 / length; perpDx *= scale; perpDy *= scale; return new PointCollection { new Point(midX, midY), new Point(midX + perpDx, midY + perpDy), new Point(midX - perpDx, midY - perpDy) }; } }
然后在每个坐标属性的Set方法中,触发PolygonPoints的属性变更通知:
private double _X1; public double X1 { get => _X1; set { SetProperty(ref _X1, value, () => X1); OnPropertyChanged(() => PolygonPoints); } } // Y1、X2、Y2的Set方法需同样添加OnPropertyChanged(() => PolygonPoints);
最后在XAML中直接绑定该属性:
<Polygon Fill="Black" Points="{Binding PolygonPoints}"/>
这种方式将逻辑集中在ViewModel中,适合顶点计算逻辑复杂的场景。
方案三:使用附加属性(视图层封装)
如果希望将逻辑封装在视图层,不修改ViewModel,可以编写附加属性:
public static class PolygonHelper { public static readonly DependencyProperty LineEndPointsProperty = DependencyProperty.RegisterAttached( "LineEndPoints", typeof(double[]), typeof(PolygonHelper), new PropertyMetadata(null, OnLineEndPointsChanged)); public static double[] GetLineEndPoints(DependencyObject obj) { return (double[])obj.GetValue(LineEndPointsProperty); } public static void SetLineEndPoints(DependencyObject obj, double[] value) { obj.SetValue(LineEndPointsProperty, value); } private static void OnLineEndPointsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not Polygon polygon || e.NewValue is not double[] points || points.Length != 4) return; double x1 = points[0]; double y1 = points[1]; double x2 = points[2]; double y2 = points[3]; // 复用顶点计算逻辑 var midX = (x1 + x2) / 2; var midY = (y1 + y2) / 2; var dx = x2 - x1; var dy = y2 - y1; var perpDx = -dy; var perpDy = dx; var length = Math.Sqrt(perpDx * perpDx + perpDy * perpDy); if (length == 0) { polygon.Points = new PointCollection(); return; } var scale = 10 / length; perpDx *= scale; perpDy *= scale; polygon.Points = new PointCollection { new Point(midX, midY), new Point(midX + perpDx, midY + perpDy), new Point(midX - perpDx, midY - perpDy) }; } }
在XAML中通过多绑定给附加属性传递坐标值:
<Polygon Fill="Black"> <local:PolygonHelper.LineEndPoints> <MultiBinding> <Binding Path="X1"/> <Binding Path="Y1"/> <Binding Path="X2"/> <Binding Path="Y2"/> </MultiBinding> </local:PolygonHelper.LineEndPoints> </Polygon>
这种方式适合需要在多个视图中复用该逻辑的场景,无需重复编写转换器或ViewModel属性。
内容的提问来源于stack exchange,提问作者Danial
相关产品推荐
相关产品推荐

