绘制图形Draw shapes

了解如何绘制形状,如椭圆、矩形、多边形以及路径。Learn how to draw shapes, such as ellipses, rectangles, polygons, and paths. Path 类是在 XAML UI 中可视化基于相当复杂矢量的绘图语言的方法;例如,可以绘制贝塞尔曲线。The Path class is the way to visualize a fairly complex vector-based drawing language in a XAML UI; for example, you can draw Bezier curves.

重要的 API路径类Windows.UI.Xaml.Shapes 命名空间Windows.UI.Xaml.Media 命名空间Important APIs: Path class, Windows.UI.Xaml.Shapes namespace, Windows.UI.Xaml.Media namespace

可以使用下面的两组类定义 XAML UI 中的空间区域:Shape 类和 Geometry 类。Two sets of classes define a region of space in XAML UI: Shape classes and Geometry classes. 这些类之间的主要区别在于,Shape 具有一个与其关联的画笔并可以呈现到屏幕,而 Geometry 只定义一个空间区域并且不进行呈现,除非它用于帮助将信息提供给另一 UI 属性。The main difference between these classes is that a Shape has a brush associated with it and can be rendered to the screen, and a Geometry simply defines a region of space and is not rendered unless it helps contribute information to another UI property. 你可以将 Shape 视为 UIElement,其边界通过 Geometry 定义。You can think of a Shape as a UIElement with its boundary defined by a Geometry. 本主题主要讨论 Shape 类。This topic covers mainly the Shape classes.

Shape 类包括 LineEllipseRectanglePolygonPolylinePathThe Shape classes are Line, Ellipse, Rectangle, Polygon, Polyline, and Path. Path 非常有趣,因为它可以定义任意几何图形,同时还会在此处介绍 Geometry 类,因为这是定义部分 Path 的一个方法。Path is interesting because it can define an arbitrary geometry, and the Geometry class is involved here because that's one way to define the parts of a Path.

形状的 Fill 和 StrokeFill and Stroke for shapes

为了将 Shape 呈现到应用画布上,必须在它与 Brush 之间建立关联。For a Shape to render to the app canvas, you must associate a Brush with it. ShapeFill 属性设置为所需的 BrushSet the Fill property of the Shape to the Brush you want. 有关画笔的详细信息,请参阅使用笔画For more info about brushes, see Using brushes.

Shape 还可以有一个 Stroke(在形状的外围绘制的线条)。A Shape can also have a Stroke, which is a line that is drawn around the shape's perimeter. Stroke 还需要一个用于定义其外观的 Brush,而且其 StrokeThickness 应当具有非零值。A Stroke also requires a Brush that defines its appearance, and should have a non-zero value for StrokeThickness. StrokeThickness 是一个属性,用来定义形状边缘的外围粗细。StrokeThickness is a property that defines the perimeter's thickness around the shape edge. 如果你没有为 Stroke 指定 Brush 值,或者如果你将 StrokeThickness 设置为 0,则将不绘制形状周围的边界。If you don't specify a Brush value for Stroke, or if you set StrokeThickness to 0, then the border around the shape is not drawn.

EllipseEllipse

Ellipse 是具有弯曲外围的形状。An Ellipse is a shape with a curved perimeter. 若要创建基本的 Ellipse,请为 Fill 指定 WidthHeightBrushTo create a basic Ellipse, specify a Width, Height, and a Brush for the Fill.

下一个示例将创建一个 Ellipse,其 Width 为 200,Height 为 200,而且使用 SteelBlue 颜色的 SolidColorBrush 作为其 FillThe next example creates an Ellipse with a Width of 200 and a Height of 200, and uses a SteelBlue colored SolidColorBrush as its Fill.

<Ellipse Fill="SteelBlue" Height="200" Width="200" />
var ellipse1 = new Ellipse();
ellipse1.Fill = new SolidColorBrush(Windows.UI.Colors.SteelBlue);
ellipse1.Width = 200;
ellipse1.Height = 200;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(ellipse1);

此处是呈现的 EllipseHere's the rendered Ellipse.

显示的椭圆。

在这种情况下,许多人会将 Ellipse 视为圆,但是实际上它是在 XAML 中声明圆形的方法:使用 WidthHeight 相等的 EllipseIn this case the Ellipse is what most people would consider a circle, but that's how you declare a circle shape in XAML: use an Ellipse with equal Width and Height.

在将 Ellipse 放置在 UI 布局中时,会假设它的大小与具有该 WidthHeight 的矩形相同;不呈现外围外部的区域,但它仍是该椭圆的布局槽大小的一部分。When an Ellipse is positioned in a UI layout, its size is assumed to be the same as a rectangle with that Width and Height; the area outside the perimeter does not have rendering but still is part of its layout slot size.

6 个 Ellipse 元素为一个组,它们属于 ProgressRing 控件的控件模板,并且 2 个同心 Ellipse 元素属于 RadioButtonA set of 6 Ellipse elements are part of the control template for the ProgressRing control, and 2 concentric Ellipse elements are part of a RadioButton.

RectangleRectangle

Rectangle 形状有四个边而且相对的两个边相等。A Rectangle is a four-sided shape with its opposite sides being equal. 若要创建基本的 Rectangle,请指定 WidthHeightFillTo create a basic Rectangle, specify a Width, a Height, and a Fill.

你可以为 Rectangle 创建圆角。You can round the corners of a Rectangle. 若要创建圆角,请指定 RadiusXRadiusY 属性的值。To create rounded corners, specify a value for the RadiusX and RadiusY properties. 这些属性指定椭圆的 X 轴和 Y 轴,以定义角的曲线。These properties specify the x-axis and y-axis of an ellipse that defines the curve of the corners. RadiusX 的最大允许值为 Width 的一半,RadiusY 的最大允许值为 Height 的一半。The maximum allowed value of RadiusX is the Width divided by two and the maximum allowed value of RadiusY is the Height divided by two.

下一个示例创建 Rectangle,其 Width 为 200,Height 为 100。The next example creates a Rectangle with a Width of 200 and a Height of 100. 它将 SolidColorBrushBlue 值用于其 Fill,并将 SolidColorBrushBlack 值用于其 StrokeIt uses a Blue value of SolidColorBrush for its Fill and a Black value of SolidColorBrush for its Stroke. 我们将 StrokeThickness 设置为 3。We set the StrokeThickness to 3. 我们将 RadiusX 属性设置为 50,并将 RadiusY 属性设置为 10,从而为 Rectangle 创建圆角。We set the RadiusX property to 50 and the RadiusY property to 10, which gives the Rectangle rounded corners.

<Rectangle Fill="Blue"
           Width="200"
           Height="100"
           Stroke="Black"
           StrokeThickness="3"
           RadiusX="50"
           RadiusY="10" />
var rectangle1 = new Rectangle();
rectangle1.Fill = new SolidColorBrush(Windows.UI.Colors.Blue);
rectangle1.Width = 200;
rectangle1.Height = 100;
rectangle1.Stroke = new SolidColorBrush(Windows.UI.Colors.Black);
rectangle1.StrokeThickness = 3;
rectangle1.RadiusX = 50;
rectangle1.RadiusY = 10;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(rectangle1);

此处是呈现的 RectangleHere's the rendered Rectangle.

呈现的矩形。

提示  对于 UI 定义来说,在某些情况下,不使用 Rectangle,使用 Border 可能更合理。Tip  There are some scenarios for UI definitions where instead of using a Rectangle, a Border might be more appropriate. 如果你打算在其他内容周围创建一个矩形形状,最好使用 Border,因为它可能会有子内容而且将自动在子内容周围调整大小,而不是像 Rectangle 那样使用固定的高度和宽度尺寸。If your intention is to create a rectangle shape around other content, it might be better to use Border because it can have child content and will automatically size around that content, rather than using the fixed dimensions for height and width like Rectangle does. Border 在你设置了 CornerRadius 属性的情况下还提供了创建圆角的选项。A Border also has the option of having rounded corners if you set the CornerRadius property.

另一方面,Rectangle 可能是控件组合的较好选择。On the other hand, a Rectangle is probably a better choice for control composition. Rectangle 形状可以在很多控件模板中看到,因为它用作可获得焦点的控件的“FocusVisual”部分。A Rectangle shape is seen in many control templates because it's used as a "FocusVisual" part for focusable controls. 当控件处于“已设置焦点”视觉状态时,此矩形可见,在其他状态时,它处于隐藏状态。Whenever the control is in a "Focused" visual state, this rectangle is made visible, in other states it's hidden.

PolygonPolygon

Polygon 是通过任意数量的点来定义边的形状。A Polygon is a shape with a boundary defined by an arbitrary number of points. 边通过用直线将点一个一个连接起来(最后一个点与第一个点相连)而创建。The boundary is created by connecting a line from one point to the next, with the last point connected to the first point. Points 属性定义组成边的点集。The Points property defines the collection of points that make up the boundary. 在 XAML 中,使用逗号分隔的列表定义点。In XAML, you define the points with a comma-separated list. 在代码隐藏文件中,使用 PointCollection 定义各个点,并将每个点作为一个 Point 值添加到集合中。In code-behind you use a PointCollection to define the points and you add each individual point as a Point value to the collection.

你不必为了将起点和终点指定为相同的 Point 值而明确声明点。You don't need to explicitly declare the points such that the start point and end point are both specified as the same Point value. Polygon 的呈现逻辑假设你要定义闭合形状而且会自动将终点与起点连起来。The rendering logic for a Polygon assumes that you are defining a closed shape and will connect the end point to the start point implicitly.

下一个示例创建 Polygon,其 4 个顶点分别设置为 (10,200)(60,140)(130,140)(180,200)The next example creates a Polygon with 4 points set to (10,200), (60,140), (130,140), and (180,200). 它使用 SolidColorBrushLightBlue 值作为其 Fill,而且其 Stroke 没有值,以便它没有外围轮廓。It uses a LightBlue value of SolidColorBrush for its Fill, and has no value for Stroke so it has no perimeter outline.

<Polygon Fill="LightBlue"
         Points="10,200,60,140,130,140,180,200" />
var polygon1 = new Polygon();
polygon1.Fill = new SolidColorBrush(Windows.UI.Colors.LightBlue);

var points = new PointCollection();
points.Add(new Windows.Foundation.Point(10, 200));
points.Add(new Windows.Foundation.Point(60, 140));
points.Add(new Windows.Foundation.Point(130, 140));
points.Add(new Windows.Foundation.Point(180, 200));
polygon1.Points = points;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(polygon1);

此处是呈现的 PolygonHere's the rendered Polygon.

呈现的多边形。

提示  在除了声明形状顶点的其他方案的 XAML 中,Point 值通常用作类型。Tip  A Point value is often used as a type in XAML for scenarios other than declaring the vertices of shapes. 例如,Point 属于触摸事件的事件数据,因此你可以精确了解触摸操作发生在坐标空间的哪个位置。For example, a Point is part of the event data for touch events, so you can know exactly where in a coordinate space the touch action occurred. 有关 Point 以及如何将其用于 XAML 或代码的详细信息,请参阅 Point 的 API 参考主题。For more info about Point and how to use it in XAML or code, see the API reference topic for Point.

LineLine

Line 只是一条在坐标空间中的两个点之间绘制的直线。A Line is simply a line drawn between two points in coordinate space. Line 忽略为 Fill 提供的任何值,因为它没有内部空间。A Line ignores any value provided for Fill, because it has no interior space. 对于 Line,请确保为 StrokeStrokeThickness 属性指定值,否则 Line 将不呈现。For a Line, make sure to specify values for the Stroke and StrokeThickness properties, because otherwise the Line won't render.

不要使用 Point 值指定 Line 形状,而应针对 X1Y1X2Y2 使用离散的 Double 值。You don't use Point values to specify a Line shape, instead you use discrete Double values for X1, Y1, X2 and Y2. 这会使得横线或竖线的标记最少。This enables minimal markup for horizontal or vertical lines. 例如,<Line Stroke="Red" X2="400"/> 定义一条长为 400 个像素的横线。For example, <Line Stroke="Red" X2="400"/> defines a horizontal line that is 400 pixels long. 另一对 X,Y 属性在默认情况下为 0,因此,从点的角度看,此 XAML 将绘制一条从 (0,0)(400,0) 的直线。The other X,Y properties are 0 by default, so in terms of points this XAML would draw a line from (0,0) to (400,0). 如果你希望它从 (0,0) 之外的任意点开始,则可以使用 TranslateTransform 移动整个 LineYou could then use a TranslateTransform to move the entire Line, if you wanted it to start at a point other than (0,0).

<Line Stroke="Red" X2="400"/>
var line1 = new Line();
line1.Stroke = new SolidColorBrush(Windows.UI.Colors.Red);
line1.X2 = 400;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(line1);

Polyline Polyline

PolylinePolygon 类似,该形状的边也是通过一组点来进行定义,只不过 Polyline 的最后一个点不与第一个点相连。A Polyline is similar to a Polygon in that the boundary of the shape is defined by a set of points, except the last point in a Polyline is not connected to the first point.

注意   你可以在 Points 中为 Polyline 明确设置相同的起点和终点,但是,在这种情况下,你可能已改用 PolygonNote   You could explicitly have an identical start point and end point in the Points set for the Polyline, but in that case you probably could have used a Polygon instead.

如果你指定 PolylineFill,则 Fill 会绘制形状的内部空间,即使为 Polyline 设置的 Points 的起点和终点不相交也是如此。If you specify a Fill of a Polyline, the Fill paints the interior space of the shape, even if the start point and end point of the Points set for the Polyline do not intersect. 如果你没有指定 Fill,则 Polyline 与指定了多个单独的、其连续直线的起点和终点相交的 Line 元素时所呈现的内容相似。If you do not specify a Fill, then the Polyline is similar to what would have rendered if you had specified several individual Line elements where the start points and end points of consecutive lines intersected.

就像 Polygon 一样,Points 属性定义组成边的点集。As with a Polygon, the Points property defines the collection of points that make up the boundary. 在 XAML 中,使用逗号分隔的列表定义点。In XAML, you define the points with a comma-separated list. 在代码隐藏中,使用 PointCollection 来定义点,并将每个单独的点作为 Point 结构添加到集合中。In code-behind, you use a PointCollection to define the points and you add each individual point as a Point structure to the collection.

此示例创建 Polyline,其 4 个顶点分别设置为 (10,200)(60,140)(130,140) 以及 (180,200)This example creates a Polyline with four points set to (10,200), (60,140), (130,140), and (180,200). 定义了一个没有 FillStrokeA Stroke is defined but not a Fill.

<Polyline Stroke="Black"
        StrokeThickness="4"
        Points="10,200,60,140,130,140,180,200" />
var polyline1 = new Polyline();
polyline1.Stroke = new SolidColorBrush(Windows.UI.Colors.Black);
polyline1.StrokeThickness = 4;

var points = new PointCollection();
points.Add(new Windows.Foundation.Point(10, 200));
points.Add(new Windows.Foundation.Point(60, 140));
points.Add(new Windows.Foundation.Point(130, 140));
points.Add(new Windows.Foundation.Point(180, 200));
polyline1.Points = points;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(polyline1);

此处是呈现的 Polyline请注意,第一个点和最后一个点不像在 Polygon 那样由 Stroke 轮廓连接起来。Notice that the first and last points are not connected by the Stroke outline as they are in a Polygon.

呈现的折线。

路径Path

Path 是最通用的 Shape,因为使用它可以定义任意几何图形。A Path is the most versatile Shape because you can use it to define an arbitrary geometry. 但是这种通用性非常复杂。But with this versatility comes complexity. 让我们来看看如何在 XAML 中创建一个基本的 PathLet's now look at how to create a basic Path in XAML.

定义其路径具有 Data 属性的几何图形。You define the geometry of a path with the Data property. 可使用两种技术设置 DataThere are two techniques for setting Data:

  • 你可以在 XAML 中为 Data 定义字符串值。You can set a string value for Data in XAML. 在这种形状中,Path.Data 值对于图形采用序列化格式。In this form, the Path.Data value is consuming a serialization format for graphics. 在首次设置了该值后,你通常无需以字符串对该值进行文本编辑。You typically don't text-edit this value in string form after it is first established. 而是应当使用能够在图面上的设计或绘制标记中工作的设计工具。Instead, you use design tools that enable you to work in a design or drawing metaphor on a surface. 然后,可以保存或导出输出内容,系统会为你提供一个包含 Path.Data 信息的 XAML 文件或 XAML 字符串片段。Then you save or export the output, and this gives you a XAML file or XAML string fragment with Path.Data information.
  • 可以将 Data 属性设置为单个 Geometry 对象。You can set the Data property to a single Geometry object. 这可以通过在代码或在 XAML 中来完成。This can be done in code or in XAML. 这个 Geometry 通常是充当容器的 GeometryGroup,该容器可以将多个几何图形定义组合到单个对象中以形成对象模型。That single Geometry is typically a GeometryGroup, which acts as a container that can composite multiple geometry definitions into a single object for purposes of the object model. 这样做最常见的理由是,你希望使用一个或多个可以定义为 PathFigureSegments 值(例如 BezierSegment)的曲线和复杂形状。The most common reason for doing this is because you want to use one or more of the curves and complex shapes that can be defined as Segments values for a PathFigure, for example BezierSegment.

此示例显示了一个 Path,它是由以下操作生成的:使用 Blend for Visual Studio 生成少数几个矢量形状,然后将结果另存为 XAML。This example shows a Path that might have resulted from using Blend for Visual Studio to produce just a few vector shapes and then saving the result as XAML. 整个 Path 由一条贝塞尔曲线和一条线段组成。The total Path consists of a Bezier curve segment and a line segment. 此示例主要是为了举例说明 Path.Data 序列化格式中存在的元素以及各个数字所代表的含义。The example is mainly intended to give you some examples of what elements exist in the Path.Data serialization format and what the numbers represent.

Data 从 move 命令(由“M”指示)开始,该命令为此路径指定起点的绝对值。This Data begins with the move command, indicated by "M", which establishes an absolute start point for the path.

第一段是三次方贝塞尔曲线,起点为 (100,200),终点为 (400,175),通过两个控制点 (100,25)(400,350) 绘制而成。The first segment is a cubic Bezier curve that begins at (100,200) and ends at (400,175), which is drawn by using the two control points (100,25) and (400,350). 这一段由 Data 属性字符串中的“C”命令指示。This segment is indicated by the "C" command in the Data attribute string.

第二段从横线的绝对值命令“H”开始,该命令指定一条从上一个子路径的终结点 (400,175) 到新的终结点 (280,175) 绘制的线段。The second segment begins with an absolute horizontal line command "H", which specifies a line drawn from the preceding subpath endpoint (400,175) to a new endpoint (280,175). 由于这是一个横线命令,因此指定的值为 x 坐标。Because it's a horizontal line command, the value specified is an x-coordinate.

<Path Stroke="DarkGoldenRod" 
      StrokeThickness="3"
      Data="M 100,200 C 100,25 400,350 400,175 H 280" />

此处是显示的 PathHere's the rendered Path.

简单呈现路径的屏幕截图。

下一示例将介绍我们讨论过的其他技术的用法:具有 PathGeometryGeometryGroupThe next example shows a usage of the other technique we discussed: a GeometryGroup with a PathGeometry. 本例对某些可用作 PathGeometry 的一部分的参与几何类型进行练习:PathFigure 和在 PathFigure.Segments 中作为片段的各种元素。This example exercises some of the contributing geometry types that can be used as part of a PathGeometry: PathFigure and the various elements that can be a segment in PathFigure.Segments.

<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
    <Path.Data>
        <GeometryGroup>
            <RectangleGeometry Rect="50,5 100,10" />
            <RectangleGeometry Rect="5,5 95,180" />
            <EllipseGeometry Center="100, 100" RadiusX="20" RadiusY="30"/>
            <RectangleGeometry Rect="50,175 100,10" />
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure IsClosed="true" StartPoint="50,50">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <BezierSegment Point1="75,300" Point2="125,100" Point3="150,50"/>
                                    <BezierSegment Point1="125,300" Point2="75,100"  Point3="50,50"/>
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </GeometryGroup>
    </Path.Data>
</Path>
var path1 = new Windows.UI.Xaml.Shapes.Path();
path1.Fill = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 204, 204, 255));
path1.Stroke = new SolidColorBrush(Windows.UI.Colors.Black);
path1.StrokeThickness = 1;

var geometryGroup1 = new GeometryGroup();
var rectangleGeometry1 = new RectangleGeometry();
rectangleGeometry1.Rect = new Rect(50, 5, 100, 10);
var rectangleGeometry2 = new RectangleGeometry();
rectangleGeometry2.Rect = new Rect(5, 5, 95, 180);
geometryGroup1.Children.Add(rectangleGeometry1);
geometryGroup1.Children.Add(rectangleGeometry2);

var ellipseGeometry1 = new EllipseGeometry();
ellipseGeometry1.Center = new Point(100, 100);
ellipseGeometry1.RadiusX = 20;
ellipseGeometry1.RadiusY = 30;
geometryGroup1.Children.Add(ellipseGeometry1);

var pathGeometry1 = new PathGeometry();
var pathFigureCollection1 = new PathFigureCollection();
var pathFigure1 = new PathFigure();
pathFigure1.IsClosed = true;
pathFigure1.StartPoint = new Windows.Foundation.Point(50, 50);
pathFigureCollection1.Add(pathFigure1);
pathGeometry1.Figures = pathFigureCollection1;

var pathSegmentCollection1 = new PathSegmentCollection();
var pathSegment1 = new BezierSegment();
pathSegment1.Point1 = new Point(75, 300);
pathSegment1.Point2 = new Point(125, 100);
pathSegment1.Point3 = new Point(150, 50);
pathSegmentCollection1.Add(pathSegment1);

var pathSegment2 = new BezierSegment();
pathSegment2.Point1 = new Point(125, 300);
pathSegment2.Point2 = new Point(75, 100);
pathSegment2.Point3 = new Point(50, 50);
pathSegmentCollection1.Add(pathSegment2);
pathFigure1.Segments = pathSegmentCollection1;

geometryGroup1.Children.Add(pathGeometry1);
path1.Data = geometryGroup1;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(path1);

此处是显示的 PathHere's the rendered Path.

复杂呈现路径的屏幕截图。

使用 PathGeometry 可能比填充 Path.Data 字符串更具可读性。Using PathGeometry may be more readable than populating a Path.Data string. 另一方面,Path.Data 使用兼容于可缩放的向量图形 (SVG) 图像路径定义的语法,因此它可用于从 SVG 移植图形,或者用作工具(例如 Blend)的输出。On the other hand, Path.Data uses a syntax compatible with Scalable Vector Graphics (SVG) image path definitions so it may be useful for porting graphics from SVG, or as output from a tool like Blend.