Geometry 概述

本主题介绍如何使用 Windows Presentation Foundation (WPF) Geometry 类描述形状。 本主题还对照了 Geometry 对象和 Shape 元素之间的区别。

什么是 Geometry?

Geometry 类以及从其派生的类(如 EllipseGeometryPathGeometryCombinedGeometry)可用于描述 2D 形状的几何图形。 这些几何描述具有许多用途,例如定义要绘制到屏幕的形状或定义命中测试和剪裁区域。 甚至可以使用几何定义动画路径。

Geometry 对象可以是矩形和圆形等简单类型,也可以是由两个或更多个几何对象创建的复合类型。 可使用 PathGeometryStreamGeometry 类创建更复杂的几何图形,这些类可用于描述弧线和曲线。

由于 GeometryFreezable 的一种类型,因此 Geometry 对象提供多种特殊功能:它们可以声明为资源、在多个对象之间共享、变为只读以提高性能、克隆以及变为线程安全。 有关 Freezable 对象提供的不同功能的详细信息,请参阅 Freezable 对象概述

几何图形与形状

GeometryShape 类的相似之处在于它们均描述 2D 形状(例如比较 EllipseGeometryEllipse),但它们之间存在一些重要区别。

例如,Geometry 类继承自 Freezable 类,而 Shape 类继承自 FrameworkElement。 由于它们是元素,因此 Shape 对象可以自行呈现并参与布局系统,而 Geometry 对象则不能。

尽管 Shape 对象比 Geometry 对象更易于使用,但 Geometry 对象更通用。 尽管 Shape 对象用于呈现 2D 图形,但 Geometry 对象可用于定义 2D 图形的几何区域、定义剪裁的区域或定义命中测试的区域等。

Path 形状

一个 Shape(即 Path 类)实际上使用 Geometry 描述其内容。 通过使用 Geometry 设置 PathData 属性,并设置其 FillStroke 属性,可以呈现 Geometry

采用 Geometry 的常见属性

上述部分提到了可将 Geometry 对象与其他对象结合用于各种目的,如绘制形状、动画处理和剪裁。 下表列出了多个类,这些类具有采用 Geometry 对象的属性。

类型 properties
DoubleAnimationUsingPath PathGeometry
DrawingGroup ClipGeometry
GeometryDrawing Geometry
Path Data
UIElement Clip

简单几何类型

所有几何图形的基类都是抽象类 Geometry。 派生自 Geometry 类的类可大致分为三个类别:简单几何、路径几何和复合几何。

简单几何类包括 LineGeometryRectangleGeometryEllipseGeometry,用于创建基本几何形状,例如线条、矩形和圆形。

  • LineGeometry 通过指定直线的起点和终点来定义。

  • RectangleGeometry 使用 Rect 结构来定义,该结构指定其相对位置以及高度和宽度。 可以通过设置 RadiusXRadiusY 属性来创建圆角矩形。

  • EllipseGeometry 由中心点、x 半径和 y 半径定义。 以下示例介绍如何创建简单几何来进行呈现和剪裁。

这些相同的形状以及更复杂的形状可使用 PathGeometry 或通过将几何对象组合在一起来创建,但这些类提供更简单的方法来生成这些基本几何形状。

以下示例演示如何创建和呈现 LineGeometry。 如上所述,Geometry 对象无法进行自我绘制,因此该示例使用 Path 形状呈现直线。 由于直线没有面积,因此设置 PathFill 属性没有效果;应仅指定 StrokeStrokeThickness 属性。 下图显示该示例的输出。

A LineGeometry
从 (10,20) 绘制到 (100,130) 的 LineGeometry

<Path Stroke="Black" StrokeThickness="1" >
  <Path.Data>
    <LineGeometry StartPoint="10,20" EndPoint="100,130" />
  </Path.Data>
</Path>
LineGeometry myLineGeometry = new LineGeometry();
myLineGeometry.StartPoint = new Point(10,20);
myLineGeometry.EndPoint = new Point(100,130);

Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myLineGeometry;
Dim myLineGeometry As New LineGeometry()
myLineGeometry.StartPoint = New Point(10,20)
myLineGeometry.EndPoint = New Point(100,130)

Dim myPath As New Path()
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myLineGeometry

下一个示例演示如何创建和呈现 EllipseGeometry。 这些示例将 EllipseGeometryCenter 设置为点 50,50,并且 x 半径和 y 半径均设置为 50,从而创建一个直径为 100 的圆。 通过为 Path 元素的 Fill 属性赋值(在本例中为 Gold)来绘制椭圆形的内部。 下图显示该示例的输出。

An EllipseGeometry
在 (50,50) 处绘制的一个 EllipseGeometry

<Path Fill="Gold" Stroke="Black" StrokeThickness="1">
  <Path.Data>
    <EllipseGeometry Center="50,50" RadiusX="50" RadiusY="50" />
  </Path.Data>
</Path>
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(50, 50);
myEllipseGeometry.RadiusX = 50;
myEllipseGeometry.RadiusY = 50;

Path myPath = new Path();
myPath.Fill = Brushes.Gold;
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myEllipseGeometry;
Dim myEllipseGeometry As New EllipseGeometry()
myEllipseGeometry.Center = New Point(50, 50)
myEllipseGeometry.RadiusX = 50
myEllipseGeometry.RadiusY = 50

Dim myPath As New Path()
myPath.Fill = Brushes.Gold
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myEllipseGeometry

以下示例演示如何创建和呈现 RectangleGeometry。 矩形的位置和维度由 Rect 结构定义。 位置为 50,50,高度和宽度均为 25,这将创建一个正方形。 下图显示该示例的输出。

A RectangleGeometry
在 50,50 处绘制的一个 RectangleGeometry

<Path Fill="LemonChiffon" Stroke="Black" StrokeThickness="1">
  <Path.Data>
    <RectangleGeometry Rect="50,50,25,25" />
  </Path.Data>
</Path>
RectangleGeometry myRectangleGeometry = new RectangleGeometry();
myRectangleGeometry.Rect = new Rect(50,50,25,25);

Path myPath = new Path();
myPath.Fill = Brushes.LemonChiffon;
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myRectangleGeometry;
Dim myRectangleGeometry As New RectangleGeometry()
myRectangleGeometry.Rect = New Rect(50,50,25,25)

Dim myPath As New Path()
myPath.Fill = Brushes.LemonChiffon
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myRectangleGeometry

以下示例演示如何使用 EllipseGeometry 作为图像的剪裁区域。 Image 对象使用 200 的 Width 和 150 的 Height 来定义。 将一个 RadiusX 值为 100、RadiusY 值为 75 以及 Center 值为 100,75 的 EllipseGeometry 设置为图像的 Clip 属性。 将仅显示处于椭圆形区域内的图像部分。 下图显示该示例的输出。

An Image with and without clipping
用于剪裁 Image 控件的 EllipseGeometry

<Image
  Source="sampleImages\Waterlilies.jpg"
  Width="200" Height="150" HorizontalAlignment="Left">
  <Image.Clip>
    <EllipseGeometry
      RadiusX="100"
      RadiusY="75"
      Center="100,75"/>
  </Image.Clip>
</Image>

// Create the image to clip.
Image myImage = new Image();
Uri imageUri =
    new Uri(@"C:\\Documents and Settings\\All Users\\Documents\My Pictures\\Sample Pictures\\Water lilies.jpg", UriKind.Relative);
myImage.Source = new BitmapImage(imageUri);
myImage.Width = 200;
myImage.Height = 150;
myImage.HorizontalAlignment = HorizontalAlignment.Left;

// Use an EllipseGeometry to define the clip region.
EllipseGeometry myEllipseGeometry = new EllipseGeometry();
myEllipseGeometry.Center = new Point(100, 75);
myEllipseGeometry.RadiusX = 100;
myEllipseGeometry.RadiusY = 75;
myImage.Clip = myEllipseGeometry;


' Create the image to clip.
Dim myImage As New Image()
Dim imageUri As New Uri("C:\\Documents and Settings\\All Users\\Documents\My Pictures\\Sample Pictures\\Water lilies.jpg", UriKind.Relative)
myImage.Source = New BitmapImage(imageUri)
myImage.Width = 200
myImage.Height = 150
myImage.HorizontalAlignment = HorizontalAlignment.Left

' Use an EllipseGeometry to define the clip region. 
Dim myEllipseGeometry As New EllipseGeometry()
myEllipseGeometry.Center = New Point(100, 75)
myEllipseGeometry.RadiusX = 100
myEllipseGeometry.RadiusY = 75
myImage.Clip = myEllipseGeometry

路径几何

PathGeometry 类及其轻型等效项(StreamGeometry 类)提供了描述由弧线、曲线和直线组成的多个复杂图形的方法。

PathGeometry 的核心是 PathFigure 对象的集合,之所以如此命名,是因为每个图形都描述 PathGeometry 中的一个离散形状。 每个 PathFigure 本身都由一个或多个 PathSegment 对象组成,其中每一个都描述了图形的一个线段。

有多种类型的线段。

线段类型 说明 示例
ArcSegment 在两点之间创建一条椭圆弧。 创建椭圆弧
BezierSegment 创建两个点之间的三次方贝塞尔曲线。 创建三次方贝塞尔曲线
LineSegment 创建两个点之间的直线。 在 PathGeometry 中创建 LineSegment
PolyBezierSegment 创建一系列三次方贝塞尔曲线。 请参阅 PolyBezierSegment 类型页。
PolyLineSegment 创建一系列直线。 请参阅 PolyLineSegment 类型页。
PolyQuadraticBezierSegment 创建一系列二次贝塞尔曲线。 请参阅 PolyQuadraticBezierSegment 页面。
QuadraticBezierSegment 创建一条二次贝塞尔曲线。 创建二次贝塞尔曲线

PathFigure 内的多个线段组合成单个几何形状,每个线段的终点是下一个线段的起点。 PathFigureStartPoint 属性指定绘制第一个线段的起点。 每个后续段都从上一段的终点开始。 例如,通过将 StartPoint 属性设置为 10,50 并创建一个 Point 属性设置为 10,150LineSegment,可以定义从 10,5010,150 的垂直线。

以下示例创建由单个 PathFigureLineSegment 组成的简单 PathGeometry,并使用 Path 元素显示它。 PathFigure 对象的 StartPoint 设置为 10,20,而 LineSegment 使用终点 100,130 定义。 下图显示了此示例创建的 PathGeometry

A LineGeometry
包含单个 LineSegment 的 PathGeometry

<Path Stroke="Black" StrokeThickness="1">
  <Path.Data>
    <PathGeometry>
      <PathGeometry.Figures>
        <PathFigure StartPoint="10,20">
          <PathFigure.Segments>
            <LineSegment Point="100,130"/>
          </PathFigure.Segments>
        </PathFigure>
      </PathGeometry.Figures>
    </PathGeometry>
  </Path.Data>
</Path>

// Create a figure that describes a
// line from (10,20) to (100,130).
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(10,20);
myPathFigure.Segments.Add(
    new LineSegment(new Point(100,130),
    true /* IsStroked */ ));

/// Create a PathGeometry to contain the figure.
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);

// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;

' Create a figure that describes a 
' line from (10,20) to (100,130).
Dim myPathFigure As New PathFigure()
myPathFigure.StartPoint = New Point(10,20)
myPathFigure.Segments.Add(New LineSegment(New Point(100,130), True)) ' IsStroked 

''' Create a PathGeometry to contain the figure.
Dim myPathGeometry As New PathGeometry()
myPathGeometry.Figures.Add(myPathFigure)

' Display the PathGeometry. 
Dim myPath As New Path()
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myPathGeometry

有必要将此示例与上述 LineGeometry 示例进行比较。 用于 PathGeometry 的语法比用于简单 LineGeometry 的语法更详细,并且在此情况下使用 LineGeometry 类更合理,但 PathGeometry 的详细语法允许极其错综复杂的几何区域。

通过使用 PathSegment 对象的组合可以创建更复杂的几何图形。

下一个示例使用 BezierSegmentLineSegmentArcSegment 创建形状。 该示例首先通过定义四个点创建三次方贝赛尔曲线:一个起点(即上一个线段的终点)和一个终点 (Point3) 以及两个控制点(Point1Point2)。 三次方贝赛尔曲线的两个控制点行为像磁铁一样,朝着自身方向吸引本应为直线的部分,从而形成一条曲线。 第一个控制点 Point1 影响曲线的开始部分;第二个控制点 Point2 影响曲线的结束部分。

然后,该示例添加一个 LineSegment,该线段从前面的 BezierSegment 的终点绘制到由其 LineSegment 属性指定的点。

然后,该示例添加一个 ArcSegment,该线段从前面的 LineSegment 的终点绘制到由其 Point 属性指定的点。 该示例还指定弧线的 x 和 y 半径 (Size)、旋转角度 (RotationAngle)、指示生成的弧线角度应为多大的标记 (IsLargeArc) 以及指示弧线绘制方向的值 (SweepDirection)。 下图显示此示例所创建的形状。

A PathGeometry with an arc.
一个 PathGeometry

<Path Stroke="Black" StrokeThickness="1" >
  <Path.Data>
    <PathGeometry>
      <PathGeometry.Figures>
        <PathFigure StartPoint="10,50">
          <PathFigure.Segments>
            <BezierSegment
              Point1="100,0"
              Point2="200,200"
              Point3="300,100"/>
            <LineSegment Point="400,100" />
            <ArcSegment
              Size="50,50" RotationAngle="45"
              IsLargeArc="True" SweepDirection="Clockwise"
              Point="200,100"/>
          </PathFigure.Segments>
        </PathFigure>
      </PathGeometry.Figures>
    </PathGeometry>
  </Path.Data>
</Path>

// Create a figure.
PathFigure myPathFigure = new PathFigure();
myPathFigure.StartPoint = new Point(10,50);
myPathFigure.Segments.Add(
    new BezierSegment(
        new Point(100,0),
        new Point(200,200),
        new Point(300,100),
        true /* IsStroked */  ));
myPathFigure.Segments.Add(
    new LineSegment(
        new Point(400,100),
        true /* IsStroked */ ));
myPathFigure.Segments.Add(
    new ArcSegment(
        new Point(200,100),
        new Size(50,50),
        45,
        true, /* IsLargeArc */
        SweepDirection.Clockwise,
        true /* IsStroked */ ));

/// Create a PathGeometry to contain the figure.
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures.Add(myPathFigure);

// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;

' Create a figure.
Dim myPathFigure As New PathFigure()
myPathFigure.StartPoint = New Point(10,50)
myPathFigure.Segments.Add(New BezierSegment(New Point(100,0), New Point(200,200), New Point(300,100), True)) ' IsStroked 
myPathFigure.Segments.Add(New LineSegment(New Point(400,100), True)) ' IsStroked 
myPathFigure.Segments.Add(New ArcSegment(New Point(200,100), New Size(50,50), 45, True, SweepDirection.Clockwise, True)) ' IsStroked  -  IsLargeArc 

''' Create a PathGeometry to contain the figure.
Dim myPathGeometry As New PathGeometry()
myPathGeometry.Figures.Add(myPathFigure)

' Display the PathGeometry. 
Dim myPath As New Path()
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myPathGeometry

可以通过使用 PathGeometry 内的多个 PathFigure 对象创建更复杂的几何图形。

以下示例创建具有两个 PathFigure 对象的 PathGeometry,其中每个对象包含多个 PathSegment 对象。 使用上述示例中的 PathFigure 以及具有 PolyLineSegmentQuadraticBezierSegmentPathFigurePolyLineSegment 使用点的数组进行定义,而 QuadraticBezierSegment 使用控制点和终点进行定义。 下图显示此示例所创建的形状。

A PathGeometry with an arc that includes two PathFigure objects.
带有多个图形的 PathGeometry

<Path Stroke="Black" StrokeThickness="1" >
  <Path.Data>
    <PathGeometry>
      <PathGeometry.Figures>
        <PathFigure StartPoint="10,50">
          <PathFigure.Segments>
            <BezierSegment
              Point1="100,0"
              Point2="200,200"
              Point3="300,100"/>
            <LineSegment Point="400,100" />
            <ArcSegment
              Size="50,50" RotationAngle="45"
              IsLargeArc="True" SweepDirection="Clockwise"
              Point="200,100"/>
          </PathFigure.Segments>
        </PathFigure>
        
        <PathFigure StartPoint="10,100">
          <PathFigure.Segments>
            <PolyLineSegment Points="50,100 50,150" />
            <QuadraticBezierSegment Point1="200,200" Point2="300,100"/>
          </PathFigure.Segments>
        </PathFigure>                
      </PathGeometry.Figures>
    </PathGeometry>
  </Path.Data>
</Path>

PathGeometry myPathGeometry = new PathGeometry();

// Create a figure.
PathFigure pathFigure1 = new PathFigure();
pathFigure1.StartPoint = new Point(10,50);
pathFigure1.Segments.Add(
    new BezierSegment(
        new Point(100,0),
        new Point(200,200),
        new Point(300,100),
        true /* IsStroked */ ));
pathFigure1.Segments.Add(
    new LineSegment(
        new Point(400,100),
        true /* IsStroked */ ));
pathFigure1.Segments.Add(
    new ArcSegment(
        new Point(200,100),
        new Size(50,50),
        45,
        true, /* IsLargeArc */
        SweepDirection.Clockwise,
        true /* IsStroked */ ));
myPathGeometry.Figures.Add(pathFigure1);

// Create another figure.
PathFigure pathFigure2 = new PathFigure();
pathFigure2.StartPoint = new Point(10,100);
Point[] polyLinePointArray =
    new Point[]{ new Point(50, 100), new Point(50, 150)};
PolyLineSegment myPolyLineSegment = new PolyLineSegment();
myPolyLineSegment.Points =
    new PointCollection(polyLinePointArray);
pathFigure2.Segments.Add(myPolyLineSegment);
pathFigure2.Segments.Add(
    new QuadraticBezierSegment(
        new Point(200,200),
        new Point(300,100),
        true /* IsStroked */ ));
myPathGeometry.Figures.Add(pathFigure2);

// Display the PathGeometry.
Path myPath = new Path();
myPath.Stroke = Brushes.Black;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;

Dim myPathGeometry As New PathGeometry()

' Create a figure.
Dim pathFigure1 As New PathFigure()
pathFigure1.StartPoint = New Point(10,50)
pathFigure1.Segments.Add(New BezierSegment(New Point(100,0), New Point(200,200), New Point(300,100), True)) ' IsStroked 
pathFigure1.Segments.Add(New LineSegment(New Point(400,100), True)) ' IsStroked 
pathFigure1.Segments.Add(New ArcSegment(New Point(200,100), New Size(50,50), 45, True, SweepDirection.Clockwise, True)) ' IsStroked  -  IsLargeArc 
myPathGeometry.Figures.Add(pathFigure1)

' Create another figure.
Dim pathFigure2 As New PathFigure()
pathFigure2.StartPoint = New Point(10,100)
Dim polyLinePointArray() As Point = { New Point(50, 100), New Point(50, 150)}
Dim myPolyLineSegment As New PolyLineSegment()
myPolyLineSegment.Points = New PointCollection(polyLinePointArray)
pathFigure2.Segments.Add(myPolyLineSegment)
pathFigure2.Segments.Add(New QuadraticBezierSegment(New Point(200,200), New Point(300,100), True)) ' IsStroked 
myPathGeometry.Figures.Add(pathFigure2)

' Display the PathGeometry. 
Dim myPath As New Path()
myPath.Stroke = Brushes.Black
myPath.StrokeThickness = 1
myPath.Data = myPathGeometry

StreamGeometry

PathGeometry 类一样,StreamGeometry 定义可能包含曲线、弧线和直线的复杂几何形状。 与 PathGeometry 不同,StreamGeometry 的内容不支持数据绑定、动画或修改。 当需要描述复杂几何图形,但又不希望产生支持数据绑定、动画或修改的开销时,请使用 StreamGeometry。 由于 StreamGeometry 类的高效性,该类是描述装饰器的不错选择。

有关示例,请参阅使用 StreamGeometry 创建形状

路径标记语法

PathGeometryStreamGeometry 类型使用一系列特殊的移动和绘制命令来支持 Extensible Application Markup Language (XAML) 属性语法。 有关详细信息,请参阅路径标记语法

复合几何

使用 GeometryGroupCombinedGeometry 或通过调用静态 Geometry 方法 Combine,可以创建复合几何对象。

由于它们不执行合并操作,因此使用 GeometryGroup 对象的性能比使用 CombinedGeometry 对象或 Combine 方法的性能高。

合并几何

上一部分提到,CombinedGeometry 对象和 Combine 方法会合并由其包含的几何图形定义的面积。 GeometryCombineMode 枚举指定如何合并几何图形。 GeometryCombineMode 属性的可能值为:UnionIntersectExcludeXor

以下示例使用合并模式 Union 定义了 CombinedGeometryGeometry1Geometry2 定义为了相同半径的圆形,但中心偏移 50。

<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
  <Path.Data>
    
    <!-- Combines two geometries using the union combine mode. -->
    <CombinedGeometry GeometryCombineMode="Union">
      <CombinedGeometry.Geometry1>
        <EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
      </CombinedGeometry.Geometry1>
      <CombinedGeometry.Geometry2>
        <EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
      </CombinedGeometry.Geometry2>
    </CombinedGeometry>
  </Path.Data>
</Path>

Results of the Union combine mode

以下示例使用合并模式 Xor 定义了 CombinedGeometryGeometry1Geometry2 定义为了相同半径的圆形,但中心偏移 50。

<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
  <Path.Data>
    
    <!-- Combines two geometries using the XOR combine mode. -->
    <CombinedGeometry GeometryCombineMode="Xor">
      <CombinedGeometry.Geometry1>
        <EllipseGeometry RadiusX="50" RadiusY="50" Center="75,75" />
      </CombinedGeometry.Geometry1>
      <CombinedGeometry.Geometry2>
        <EllipseGeometry RadiusX="50" RadiusY="50" Center="125,75" />
      </CombinedGeometry.Geometry2>
    </CombinedGeometry>
  </Path.Data>
</Path>

Results of the Xor combine mode

有关其他示例,请参阅创建复合形状创建合并的几何

Freezable 功能

由于继承自 Freezable 类,Geometry 类提供多种特殊功能:Geometry 对象可以声明为 XAML 资源、在多个对象之间共享、变为只读以提高性能、克隆以及变为线程安全。 有关 Freezable 对象提供的不同功能的详细信息,请参阅 Freezable 对象概述

其他几何功能

Geometry 类还提供有用的实用工具方法,例如:

有关其方法的完整列表,请参阅 Geometry 类。

另请参阅