在UWP UI系統中,使用Shape是繪製2D圖形最簡單的方式,小到圖標,大到圖表都用到Shape的派生類,可以說有舉足輕重的地位。幸運的是從Silverlight以來Shape基本沒有什麼大改動,簡直是UWP中的一股清流。 上圖來自Pro Silverlight 5 in C ,可見Silverl ...
在UWP UI系統中,使用Shape是繪製2D圖形最簡單的方式,小到圖標,大到圖表都用到Shape的派生類,可以說有舉足輕重的地位。幸運的是從Silverlight以來Shape基本沒有什麼大改動,簡直是UWP中的一股清流。
上圖來自Pro Silverlight 5 in C#,可見Silverlight中的Shape和UWP的Shape基本架構一致。Shape的API從WPF以來就幾乎沒變,對熟悉WPF/Silverlight的開發者來說幾乎是零學習成本。
1. Ellipse(圓形)
Ellipse沒有在Shape的基礎上增加任何屬性,是Shape的派生類中最簡單的一個。
1.1 Fill、Stroke與StrokeThickness
public Brush Fill { get; set; }
、public Brush Stroke { get; set; }
與public System.Double StrokeThickness { get; set; }
是Shape中最常用的三個屬性,分別用於設置Shape的填充顏色、邊框顏色、邊框寬度。改變StrokeThickness並不會改變形狀的大小。
<Ellipse Height="100"
Width="100"
Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
Margin="10" />
1.2 Stretch
public Stretch Stretch { get; set; }
用於確定形狀如何填充可用的區域。Stretch的所有枚舉值如下:
- None: 形狀不被拉伸。
- Fill: 形狀拉伸其寬度和高度,從而可以正好適應其容器。
- Uniform: 按比例改變形狀的寬度和高度,直至形狀到達容器板邊緣。
- UniformToFill: 按比例改變形狀的寬度和高度,直到形狀填滿了整個可用控制項的高度和寬度。
效果如下
<StackPanel Orientation="Horizontal">
<Ellipse Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
Height="50"
Width="100"
Stretch="Fill" />
<Ellipse Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
Height="50"
Width="100"
Stretch="None" />
<Ellipse Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
Height="50"
Width="100"
Stretch="Uniform" />
<Ellipse Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
Height="50"
Width="100"
Stretch="UniformToFill" />
</StackPanel>
1.3 StrokeDashArray
public DoubleCollection StrokeDashArray { get; set; }用於將邊框變成虛線。StrokeDashArray的值是一個double類型的有序集合,集合中的值指虛線中每一段的長度,長度單位是邊框值的寬度。例如以下圓形:
<Ellipse StrokeDashArray="1,2,3"
Stroke="#FFFF0EC4"
StrokeThickness="10"
Height="200"
Width="200" />
邊框寬度為10,虛線的第一段是長度為10的實線,第二段為長度為20的空白,第三段為長度為30的實線,然後如此迴圈直到結束。
自從開始WPF工作以來,我一直將StrokeDashArray的值設為一個好看又好記的值:4 2,the answer to life, the universe, and everything。
<Ellipse StrokeDashArray="4 2"
Stroke="#FFFF0EC4"
StrokeThickness="3"
Height="200"
Width="200" />
1.4 StrokeDashCap
public PenLineCap StrokeDashCap { get; set; }
表示虛線中實線兩端的邊緣輪廓。PenLineCap的枚舉值如下:
- Flat: 一個未超出直線上最後一點的線帽。 等同於無線帽。
- Square: 一個高度等於直線粗細、長度等於直線粗細一半的矩形。
- Round: 一個直徑等於直線粗細的半圓形。
- Triangle: 一個底邊長度等於直線粗細的等腰直角三角形。
<Grid Margin="10,10"
Width="200"
Height="10"
Background="Gray">
<Line Stretch="Fill"
Stroke="Red"
StrokeThickness="10.5"
X2="1"
StrokeEndLineCap="Flat" />
</Grid>
<TextBlock Text="Flat"
Grid.Column="1"
VerticalAlignment="Center" />
<Grid Width="200"
Margin="10,10"
Height="10"
Background="Gray"
Grid.Row="1">
<Line Stretch="Fill"
Stroke="Red"
StrokeThickness="10"
X2="1"
StrokeEndLineCap="Round" />
</Grid>
<TextBlock Text="Round"
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center" />
<Grid Width="200"
Margin="10,10"
Height="10"
Background="Gray"
Grid.Row="2">
<Line Stretch="Fill"
Stroke="Red"
StrokeThickness="10"
X2="1"
StrokeEndLineCap="Square" />
</Grid>
<TextBlock Text="Square"
Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Center" />
<Grid Width="200"
Margin="10,10"
Height="10"
Background="Gray"
Grid.Row="3">
<Line Stretch="Fill"
Stroke="Red"
StrokeThickness="10"
X2="1"
StrokeEndLineCap="Triangle" />
</Grid>
<TextBlock Text="Triangle"
Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center" />
1.5 StrokeDashOffset
public System.Double StrokeDashOffset { get; set; }用於指定虛線開始的位置。
2. Rectangle(矩形)
Rectangle 只比Shape多了RadiusX和RadiusY兩個屬性。
2.1 RadiusX和RadiusY
public System.Double RadiusX { get; set; }和public System.Double RadiusY { get; set; }分別用於指定用於使矩形的角變圓的橢圓的x軸和 y軸半徑。
如下麵這個例子,可以看到 RadiusX="50" RadiusY="20"
的Rectangle的圓角和Width="100" Height="40"
的Ellipse(x軸半徑50,y軸半徑20)完全重合在一起。
<Rectangle Height="100"
Width="100"
Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
RadiusX="50"
RadiusY="20" />
<Ellipse HorizontalAlignment="Left"
VerticalAlignment="Top"
StrokeThickness="5"
Stroke="Yellow"
Fill="Red"
Width="100"
Height="40"
Opacity="0.5" />
2.2 StrokeLineJoin和StrokeMiterLimit
public PenLineJoin StrokeLineJoin { get; set; }
和public System.Double StrokeMiterLimit { get; set; }
確定形狀拐角處的形狀。這兩個屬性都是Shape的屬性,但對Ellipse和Line這兩個沒有拐角的形狀不起作用。
3. Line(直線)
Line表示從第一個點(X1,Y1)到第二個點(X2,Y2)的一條直線。
3.1 X1,Y1,X2,Y2
這四個屬性確定了Line的起點和終點。
除了使用絕對值定位Line的位置,還可以使用相對定位。
<Line Stretch="Fill"
X1="0"
X2="1"
StrokeDashArray="4 2"
Stroke="BlueViolet"
StrokeThickness="1" />
3.2 StrokeStartLineCap與StrokeEndLineCap
public PenLineCap StrokeStartLineCap { get; set; }
和public PenLineCap StrokeEndLineCap { get; set; }
決定直線開始端和結束端的輪廓。這兩個屬性都是Shape的屬性,但隻影響Line、Polyline和Path,對Rectangle這類沒有開始和結束端的形狀沒有影響。
4. Polygon(多邊形)和Polyline(折線)
這兩個形狀具有相同的屬性,外觀也相似。區別隻是如果Points的最後一個點和第一個點不一樣,Polygon會自動將這兩個點連接到一起。
4.1 Points
public PointCollection Points { get; set; }
用於設置Polygon和Polyline的頂點。
<Polygon Points="15,200 68,70 110,200 0,125 135,125"
Fill="Red"
Stroke="Blue"
StrokeThickness="4"
Stretch="Fill"
Height="100"
Width="100" />
<Polyline Points="15,200 68,70 110,200 0,125 135,125"
Fill="Red"
Stroke="Blue"
StrokeThickness="4"
Stretch="Fill"
Height="100"
Width="100"
Margin="20,0,0,0" />
4.2 FillRule
public FillRule FillRule { get; set; }
指定如何確定形狀的內部填充。預設值是EvenOdd,如果設置成Nonzero,效果如下:
5 Path(路徑)
Path是功能最強大的形狀,它基本上由上面的其它形狀組成並且可以替代它們中的任何一個。只是Path使用起來也比較複雜,只有在圖表和少量場景用到,很多時候比起構造複雜的Path還不如用直接用圖片。
5.1 Data
public Geometry Data { get; set; }
屬性指定要繪製形狀的Geometry。Geometry 定義幾何圖形並且定義了坐標和尺寸等細節 ,由Path繪製到屏幕。UWP中的Geometry包含以下幾個:
- EllipseGeometry: 表示圓或橢圓的幾何圖形。
- LineGeometry: 表示線條的幾何圖形。
- PathGeometry: 表示一個可能由弧、曲線、橢圓、直線和矩形組成的基於矢量的複雜形狀。
- RectangleGeometry: 描述二維矩形這一幾何圖形。
除此之外,還可以使用由多個Geometry組成的GeometryGroup構造組合形狀。
5.2 PathGeometry
在所有Geometry中PathGeometry是功能最強大的,唯一的缺點是太多複雜。它使用public PathFigureCollection Figures { get; set; }
描述包含的圖形內容,PathFigure由PathSegment的派生類描述,包括:
- ArcSegment: 表示兩點之間的一條橢圓弧。
- BezierSegment: 表示在兩個點之間繪製的一條三次貝塞爾曲線。
- LineSegment: 表示在兩個點之間繪製的一條線,它可能是 PathFigure 在 Path 數據內的一部分。
- PolyBezierSegment: 表示一條或多條三次方貝塞爾曲線。
- PolyLineSegment: 表示由 Point 集合定義的一組線段,每個 Point 指定一個線段的終點。
- PolyQuadraticBezierSegment: 表示一系列二次貝塞爾線段。
- QuadraticBezierSegment: 在 PathFigure 中的兩點之間創建二次貝塞爾曲線。
下麵是一個Path的官方示例,同時使用了各種Geometry及部分PathSegment:
<Path Stroke="Black"
Margin="10"
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>
5.3 圖形微語言(geometry mini-language)
Path的XAML雖然具備很高的可讀性,但在存儲上不具優勢。所以UWP提供了一種替代語法:圖形微語言。圖形微語言使用一組簡單的字元串描述Path的圖形,一般來說不需要學習它的語法,因為通常它是由工具生成的。
<Path Stroke="DarkGoldenRod"
StrokeThickness="3"
Data="M 100,200 C 100,25 400,350 400,175 H 280" />
5.4 使用Blend
通常用一個Path代替多個Shape不止更好管理,用戶界面的性能也會更好。Blend裡面提供了針對Shape的功能,可以對多個Shape進行合併或轉換為路勁。
6. Shape各項屬性比較
下麵表格列出了Shape的各項屬性,標記為“×”的屬性代碼這個屬性對這個形狀無效。
7. ViewBox
ViewBox是拉伸或縮放單個子元素的容器,最常用來搭配Shape(或文字)使用,因為Shape是矢量圖形,放大後不會失真。
ViewBox有以下三個屬性:
- Child: 獲取或設置 Viewbox 元素的單一子元素。
- Stretch: 獲取或設置確定內容如何適合可用空間的 Stretch 模式。
- StretchDirection: 獲取或設置確定縮放如何應用於 Viewbox 內容的 StretchDirection。
<StackPanel Orientation="Horizontal">
<Canvas Width="30" Height="30">
<Rectangle Stroke="Red"
StrokeThickness="1"
Height="20"
Width="20" />
<Rectangle Stroke="Green"
StrokeThickness="1"
Height="20"
Width="20"
Canvas.Left="10"
Canvas.Top="10" />
</Canvas>
<Viewbox Height="100"
Width="100"
Margin="30,0,0,0"
Stretch="Uniform">
<Canvas Height="30" Width="30">
<Rectangle Stroke="Red"
StrokeThickness="1"
Height="20"
Width="20" />
<Rectangle Stroke="Green"
StrokeThickness="1"
Height="20"
Width="20"
Canvas.Left="10"
Canvas.Top="10" />
</Canvas>
</Viewbox>
</StackPanel>
8. 結語
系統地學過Shape相關知識只在很多年前剛開始學WPF/Silverlight時做過,平時除了Rectangle和Line其他的Shape好少會用到,所以即使有多年經驗對Shape的很多知識點還是有點陌生,這次趁學習UWP的機會複習一下。
但因為很多知識點平時就很少會用到,後面越寫越膩味。
9. 參考
繪製形狀
Windows.UI.Xaml.Shapes 命名空間
Windows.UI.Xaml.Media 命名空間