如何绘制带开口的WPF多边形?

时间:2009-09-09 13:48:48

标签: .net wpf data-binding graphics

我有一组自定义的“PlanarMember”对象,它们暴露了Boundary和Openings属性。 Boundary属性是一个自定义的“Polygon”类,用于显示顶点和线段等.Opennings是一个IEnumerable,基本上描述了较大多边形中的开口。我可以保证开口不重叠/交叉,外边界是非自相交和闭合的,开口完全被外边界所包含,还有其他合理的限制。

我想创建一个WPF绑定,允许我将这些对象抛出到带有Canvas的自定义ItemsPanel的ItemsControl集合中,这基本上意味着我需要能够将我的自定义PlanarMember对象转换为可以放置的WPF对象在画布上,看起来像一个带有开口的实心填充多边形。此库在其他上下文中使用,因此使用WPF类而不是原始PlanarMember是不可能的。将外边界表示为WPF多边形将是微不足道的,但它是让我失望的开口。有什么想法吗?

如果它有所帮助,我试图表示的一些例子包括带有窗户和门洞的墙,带有穿透的楼板等。我也不能只在较大的形状上绘制实心填充的形状代表开口,因为后面的物体需要通过这些孔来显示。

1 个答案:

答案 0 :(得分:1)

经过一段时间的实验,我想出了一个满足我需求的解决方案。我会在这里张贴后代。希望它可以节省6个小时左右的其他人,我已经花在这个问题上了。

我使用的偶然类在其他地方定义:

  • PlanarElement :简单描述一个带有Boundary属性的2d“thing”,它暴露出一个多边形对象的IEnumerable列表的Polygon和一个Openings属性
  • 多边形:2d平面几何类,公开类型为Point的IEnumerable类型的属性
  • :3d几何点

以下是解决方案:

1)以下是我的PlanarMembers将被绑定的ItemsControl

<ItemsControl>
<ItemsControl.Resources>
    <DataTemplate DataType="{x:Type o:PlanarMember}">
        <Path Stroke="Black" StrokeThickness="1" 
              Data="{Binding Converter={StaticResource PlanarMemberConverter}}">
            <Path.Fill>
                <SolidColorBrush Opacity="0.5" Color="LightGray" />
            </Path.Fill>
        </Path>
    </DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <Canvas RenderTransform="{StaticResource CurrentDisplayTransform}"/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

2)注意在此处创建的Path的数据设置中使用PlanarMemberConverter:

<this:PlanarMemberConverter x:Key="PlanarMemberConverter" />

3)这里定义了实际的IValueConverter派生类:

[ValueConversion(typeof(PlanarMember), typeof(Geometry))]
class PlanarMemberConverter : IValueConverter
{

#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (targetType != typeof(Geometry)) return null;
    var planarMember = value as PlanarMember;

    var collection = new PathFigureCollection(planarMember.Openings.Count() + 1)
                         {
                             new PathFigure(
                                 planarMember.Boundary.Vertices.First().AsPoint(),
                                 ToSegments(planarMember.Boundary.Vertices), true){IsFilled = true}
                         };

    foreach (var opening in planarMember.Openings)
    {
        collection.Add(new PathFigure(
                           opening.Vertices.First().AsPoint(),
                           ToSegments(opening.Vertices), true) { IsFilled = true });
    }

    return new PathGeometry(collection) {FillRule = FillRule.EvenOdd};
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    throw new NotImplementedException();
}

#endregion

public IEnumerable<PathSegment> ToSegments(IEnumerable<Point> points)
{
    return
        from point in points
         select (PathSegment)new LineSegment(point.AsPoint(), true);
}
}