繪製圖形

瞭解如何繪製圖形,例如省略號、矩形、多邊形和路徑。 Path 類別是在 XAML UI 中可視化相當複雜的向量型繪圖語言的方式;例如,您可以繪製 Bezier 曲線。

兩組類別定義 XAML UI 中的空間區域:Shape 類別和 Geometry 類別。 這些類別之間的主要差異在於 Shape 有與其相關聯的筆刷,而且可以轉譯到畫面, 而Geometry 只會定義空間區域,而且除非有助於將資訊提供給另一個UI屬性,否則不會轉譯。 您可以將 Shape 視為 UIElement,其界限是由 Geometry定義。 本主題主要 涵蓋Shape 類別。

Shape 類別為 LineEllipseRectanglePolygonPolyline Path。 Path 很有趣,因為它可以定義任意幾何,而 Geometry 類別則在這裡涉及,因為這是定義 Path 部分的其中一種方式。

UWP 和 WinUI 2

重要

本文中的資訊和範例已針對使用 Windows 應用程式 SDKWinUI 3 的應用程式進行優化,但通常適用於使用 WinUI 2 的 UWP 應用程式。 如需平臺特定資訊和範例,請參閱 UWP API 參考。

本節包含您在 UWP 或 WinUI 2 應用程式中使用控件所需的資訊。

這些圖形的 API 存在於 Windows.UI.Xaml.Shapes 命名空間中。

圖形的填滿和筆劃

若要讓 Shape 轉譯至應用程式畫布,您必須將 Brush 與其產生關聯。ShapeFill 屬性設定為您想要的 Brush。 如需筆刷的詳細資訊,請參閱 使用筆刷

圖形也可以有 Stroke,這是在圖形周邊周圍繪製的線條。 Stroke 也需要定義其外觀的 Brush,而且應該具有 StrokeThickness 的非零值。 StrokeThickness 是定義圖形邊緣周圍周長粗細的屬性。 如果您未指定 Stroke 的 Brush 值,或將 StrokeThickness 設定0,則不會繪製圖形周圍的框線。

橢圓形

橢圓形是具有弧形周長的形狀。 若要建立基本的 Ellipse,請指定 FillWidthHeight 以及 Brush

下一個範例會建立寬度為 200 和 Height 為 200 的 Ellipse,並使用 SteelBlue 色彩的 SolidColorBrush 做為其 Fill。

<Ellipse Fill="SteelBlue" Height="200" Width="200" />
var ellipse1 = new Ellipse();
ellipse1.Fill = new SolidColorBrush(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);

以下是轉譯的Ellipse

A rendered Ellipse.

大多數人會將這裡的 Ellipse 視為圓形,事實上您在 XAML 宣告圓形的方式正是:使用相等 WidthHeightEllipse

當 Ellipse 位於 UI 配置中時,其大小會假設與具有該 WidthHeight 的矩形相同;周邊以外的區域沒有轉譯,但仍是其版面配置位置大小的一部分。

一組 6 個 Ellipse 元素是 ProgressRing 控件的控件範本的一部分,而 2 個同心 Ellipse 元素是 RadioButton 的一部分。

矩形

Rectangle 是一個四面圖案,其相反的兩側相等。 若要建立基本 Rectangle,請指定 WidthHeight Fill。

您可以圓角 矩形。 若要建立圓角,請指定RadiusXRadiusY屬性的值。 這些屬性會指定橢圓形的 X 軸和 Y 軸,以定義角落的曲線。 RadiusX 允許的最大值是 Width 除以 2,RadiusY的最大允許值為 Height 除以 2。

以下範例會建立 Width 200 與 Height 100 的 Rectangle。 它的 Fill 使用 SolidColorBrushBlue 值,Stroke 使用 SolidColorBrushBlack 值。 我們將 StrokeThickness 設成 3。 我們將RadiusX屬性設定為50,並將RadiusY屬性設定為10,以提供矩形圓角。

<Rectangle Fill="Blue"
           Width="200"
           Height="100"
           Stroke="Black"
           StrokeThickness="3"
           RadiusX="50"
           RadiusY="10" />
var rectangle1 = new Rectangle();
rectangle1.Fill = new SolidColorBrush(Colors.Blue);
rectangle1.Width = 200;
rectangle1.Height = 100;
rectangle1.Stroke = new SolidColorBrush(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);

以下是轉譯的矩形

A rendered Rectangle.

UI 定義有一些案例,其中,而不是使用 Rectangle線可能更合適。 如果您打算在其他內容周圍建立矩形圖形,最好是使用 Border,因為它可以有子內容,而且會自動調整該內容的大小,而不是像 Rectangle 那樣使用固定尺寸的高度和寬度。 如果您設定 CornerRadius 屬性,Border 也可以選擇使用圓角。

另一 方面,矩形 可能是控件組合的較佳選擇。 許多控件範本中都會看到矩形圖形,因為它用來做為可設定焦點控件的「焦點Visual」部分。 每當控件處於「焦點」視覺狀態時,這個矩形就會成為可見的,在其他狀態中則是隱藏的。

Polygon

多邊形是一個圖形,其界限是由任意數目的點所定義。 界限的建立方式是將線條從一個點連接到下一個點,最後一個點連接到第一個點。 Points 屬性會定義構成界限的點集合。 在 XAML 中,您會使用逗號分隔清單來定義點。 在程式代碼後置中,您會使用 PointCollection 來定義點,並將每個個別點新增為 Point 值至集合。

您不需要明確宣告點,讓起點和終點都指定為相同的 Point 值。 Polygon轉譯邏輯會假設您正在定義封閉的圖形,並將終點隱含地連接到起點。

下一個範例會建立一個 Polygon,並將 4 點設定為 (10,200)(60,140)(130,140)(180,200) 它會使用 SolidColorBrushLightBlue 值作為 Fill,且 Stroke 沒有值,因此沒有周邊外框。

<Polygon Fill="LightBlue"
         Points="10,200,60,140,130,140,180,200" />
var polygon1 = new Polygon();
polygon1.Fill = new SolidColorBrush(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);

以下是轉譯的多邊形

A rendered Polygon.

提示

除了宣告圖形頂點以外的案例,Point 值通常用來做為 XAML 中的類型。 例如, Point 是觸控事件事件數據的一部分,因此您可以確切地知道觸控動作在座標空間中發生的位置。 如需 Point 以及如何在 XAML 或程式代碼中使用它的詳細資訊,請參閱 PointAPI 參考主題。

線條

線條只是座標空間中兩點之間繪製的線條。 Line 會忽略 Fill 提供的任何值,因為它沒有內部空間。 針對Line,請務必指定 Stroke 和 StrokeThickness 屬性的值,否則Line將不會轉譯。

您不使用 Point 值來指定線條圖形,而是使用 X1、Y1X2 和 Y2 的離散 Double 值。 這可啟用水平或垂直線的最小標記。 例如, <Line Stroke="Red" X2="400"/> 定義長度為 400 像素的水平線。 其他 X,Y 屬性預設為 0,因此就點而言,此 XAML 會從 (0,0) 繪製一行至 (400,0)。 然後,如果您想要從 (0,0) 以外的某個點開始,您可以使用 TranslateTransform 來移動整個 Line

<Line Stroke="Red" X2="400"/>
var line1 = new Line();
line1.Stroke = new SolidColorBrush(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 與 Polygon 相似,因為圖形的界限是由一組點所定義,但 Polyline 中的最後一個點未連接到第一個點。

注意

您可以在 Polyline 設定的 Points明確具有相同的起點和終點,但在此情況下,您可能已改用 Polygon

如果指定 PolylineFillFill 就會繪製圖形的內部空間,即使設定給 PolylinePoints 的起點與終點未交叉也一樣。 如果未指定 FillPolyline 會與指定數個個別 Line 元素的轉譯結果一樣,即連續線條的起點與終點會交叉。

如同 Polygon,Points 屬性會定義構成界限的點集合。 在 XAML 中,您會使用逗號分隔清單來定義點。 在程式代碼後置中,您可以使用 PointCollection 來定義點,並將每個個別點新增為 Point 結構至集合。

此範例會建立一個 Polyline,並將四個點設定為 (10,200)(60,140)(130,140)和 。(180,200) 筆劃已定義,但不是 Fill

<Polyline Stroke="Black"
          StrokeThickness="4"
          Points="10,200,60,140,130,140,180,200" />
var polyline1 = new Polyline();
polyline1.Stroke = new SolidColorBrush(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 請注意,第一個和最後一個點不會由 Stroke 外框連接,因為它們位於 Polygon 中。

A rendered Polyline.

路徑

Path 是最多用途的Shape,因為您可以使用它來定義任意幾何。 但隨著這種多功能性,複雜度也隨之而來。 現在讓我們看看如何在 XAML 中建立基本 路徑

您可以使用 Data 屬性定義路徑的幾何。 設定資料有兩種技術

  • 您可以在 XAML 中設定 Data字串值。 在此表單中 ,Path.Data 值會取用圖形的串行化格式。 您通常不會在第一次建立此值之後,以字串形式文字編輯此值。 相反地,您會使用設計工具,讓您能夠在介面上設計或繪製隱喻。 然後儲存或匯出輸出,這會提供具有 Path.Data 資訊的 XAML 檔案或 XAML 字串片段。
  • 您可以將 Data 屬性設定為單一 Geometry 物件。 這可以在程式代碼或 XAML 中完成。 該單 Geometry 通常是 GeometryGroup,做為容器,可以針對物件模型的目的,將多個幾何定義複合成單一物件。 這樣做最常見的原因是您想要使用一或多個曲線和複雜圖形,這些圖形可以定義為PathFigureSegment值,例如BezierSegment。

此範例顯示路徑,可能是使用 Blend for Visual Studio 產生一些向量圖形,然後將結果儲存為 XAML。 Path 總計是由 Bezier 曲線線段和線條線段所組成。 此範例主要是為了提供一些範例,說明 Path.Data 串行化格式中有哪些元素存在,以及數位代表的內容。

資料 會以 「M」 表示的 move 命令開始,該命令會建立路徑的絕對起點。

第一個線段是一個三次方貝塞爾曲線,其開頭 (100,200) 為 ,並以 (400,175)兩個控制點 (100,25)(400,350)來繪製。 此區段是由 Data 屬性字串中的 「C」 命令表示。

第二個區段的開頭是絕對水平線命令 「H」,它會指定從前一個子路徑端點 (400,175) 繪製到新端點 (280,175)的線條。 因為它是水平線命令,所以指定的值是 x 座標。

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

以下是轉譯 的路徑

Screenshot of a simple rendered path.

下一個範例示範我們討論的其他技術用法:具有PathGeometry的GeometryGroup。 此範例會練習一些可做為 PathGeometry 一部分的參與幾何類型:PathFigure,以及 PathFigure.Segment可以是區段的各種元素。

<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 Microsoft.UI.Xaml.Shapes.Path();
path1.Fill = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 204, 204, 255));
path1.Stroke = new SolidColorBrush(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);

以下是轉譯 的路徑

Screenshot of a complex rendered path.

使用 PathGeometry 可能比填入 Path.Data 字串更容易閱讀。 另一方面, Path.Data 會使用與可調整向量圖形 (SVG) 影像路徑定義相容的語法,因此它可能有助於從 SVG 移植圖形,或作為 Blend 之類的工具的輸出。