介紹
最近,我發現我需要個圓角多邊形。而且是需要在運行時從用戶界面來繪制。WPF有多邊形。但是不支持圓角。我搜索了一下。也沒找到可行的現成例子。於是就自己做吧。本文描述了圓角多邊形的實現,也包括如何用在你的項目里。在Demo里面的RoundedCornersPolygon 類是完整的實現。
下載的Demo包括兩部分
1. 通過XAML繪制圓角多邊形
2. 運行時創建圓角多邊形
背景
多邊形可以被認為是沿着一個給定半徑的圓的邊緣和一些指定點/邊。所構成的點的集合。
在WPF中。你可以給Polygon對象的Points屬性添加一系列的點來制作多邊形。
XAML方式
<Canvas> <Polygon Points="10,50 180,50 180,150 10,150" StrokeThickness="1" Stroke="Black" /> </Canvas>
C#方式
var cnv = new Canvas(); var polygon = new Polygon {StrokeThickness = 1, Fill = Brushes.Black}; polygon.Points.Add(new Point(10, 50)); polygon.Points.Add(new Point(180, 50)); polygon.Points.Add(new Point(180, 150)); polygon.Points.Add(new Point(10, 150)); cnv.Children.Add(polygon); this.Content = cnv;
上面兩個例子會輸出下面的矩形
使用代碼
我寫的RoundedCornersPolygon 類和普通的多邊形類很相似。但是有更多的屬性來控制圓角。首先。看一個例子。展示一下圓角矩形類的使用
XAML方式
<Canvas> <CustomRoundedCornersPolygon:RoundedCornersPolygon Points="10,50 180,50 180,150 10,150" StrokeThickness="1" Stroke="Black" ArcRoundness="25" UseAnglePercentage="False" IsClosed="True"/> <Canvas>
C#方式
var cnv = new Canvas(); var roundedPolygon = new RoundedCornersPolygon { Stroke = Brushes.Black, StrokeThickness = 1, ArcRoundness = 25, UseAnglePercentage = false, IsClosed = true }; roundedPolygon.Points.Add(new Point(10, 50)); roundedPolygon.Points.Add(new Point(180, 50)); roundedPolygon.Points.Add(new Point(180, 150)); roundedPolygon.Points.Add(new Point(10, 150)); cnv.Children.Add(roundedPolygon); this.Content = cnv;
輸出如下:
多邊形有四個主要屬性
ArcRoundness 屬性指定了從距離LineSegment終點多遠的距離開始彎曲,通常和UseRoundnessPercentage 一起使用。UseRoundnessPercentage屬性指定了ArcRoundness 值是百分比還是一個固定的值。
舉個例子。ArcRoundness 被設置成10,而且UseRoundnessPercentage 被設置成false,那么彎曲將會在距離線段終點10的地方開始。而如果UseRoundnessPercentage 被設置成ture。則會是從線段終點10%的地方開始彎曲。
/// <summary> /// Gets or sets a value that specifies the arc roundness. /// </summary> public double ArcRoundness { get; set; } /// <summary> /// Gets or sets a value that specifies if the ArcRoundness property /// value will be used as a percentage of the connecting segment or not. /// </summary> public bool UseRoundnessPercentage { get; set; }
IsClosed 指定多邊形的最后一個點是否和第一個點閉合。為了成為一個多邊形。一般應該被設置為true
/// <summary> /// Gets or sets a value that specifies if the polygon will be closed or not. /// </summary> public bool IsClosed { get; set; }
Points屬性則代表了多邊形點的集合。
/// <summary> /// Gets or sets a collection that contains the points of the polygon. /// </summary> public PointCollection Points{ get; set; }
如何實現
控件實現了Shape 類,被用來畫多邊形的形狀是一個Path對象。我們會往Path對象里添加LineSegment 和QuadraticBezierSegment 對象。QuadraticBezierSegment 對象表示一個貝塞爾曲線。由三個點定義。更多的信息請查看
對於一個普通的多邊形,只有LineSegment是必須的,但是為了設計圓角多邊形。就需要貝塞爾曲線了。每一次一個點被添加/一個屬性被修改。形狀會重繪。做圓角的關鍵方式就是ConnectLinePoints 方法。
/// <summary> /// 使用公共點連接兩條線段, /// 通過3個點的指定來繪制彎曲部分 /// </summary> /// <param name="pathFigure"></param> /// <param name="p1">第一條線段的第一個點</param> /// <param name="p2">第二個點,也是公共點</param> /// <param name="p3">第二條線段的第二個點</param> /// <param name="roundness">角的弧度</param> /// <param name="usePercentage">標志值(百分比還是真值) </param> private static void ConnectLinePoints(PathFigure pathFigure, Point p1, Point p2, Point p3, double roundness, bool usePercentage) { //第一條線段上彎曲部分開始的位置點. Point backPoint; //第二條線段上彎曲部分結束的位置點 Point nextPoint; if (usePercentage) { backPoint = GetPointAtDistancePercent(p1, p2, roundness, false); nextPoint = GetPointAtDistancePercent(p2, p3, roundness, true); } else { backPoint = GetPointAtDistance(p1, p2, roundness, false); nextPoint = GetPointAtDistance(p2, p3, roundness, true); } int lastSegmentIndex = pathFigure.Segments.Count - 1; //設置第一條線段的終點. ((LineSegment)(pathFigure.Segments[lastSegmentIndex])).Point = backPoint; //創建並添加彎曲部分 var curve = new QuadraticBezierSegment { Point1 = p2, Point2 = nextPoint }; pathFigure.Segments.Add(curve); /創建並添加新線段。 var line = new LineSegment { Point = p3 }; pathFigure.Segments.Add(line); }
還有兩個方法計算彎曲部分開始的點GetPointAtDistance 和GetPointAtDistancePercent,第一個是以值計算,第二個是以百分比計算。
/// <summary> ///得到由兩個點定義的線段上,給定距離的點 /// </summary> /// <param name="p1">第一條線段的第一個點</param> /// <param name="p2">第一條線段的第二個點</param> /// <param name="distance">點的距離</param> /// <param name="firstPoint">標志值(百分比還是真值)</param> /// <returns>計算結果點</returns> private static Point GetPointAtDistance(Point p1, Point p2, double distance, bool firstPoint) { double totalDistance = Math.Sqrt(Math.Pow((p2.X - p1.X), 2) + Math.Pow((p2.Y - p1.Y), 2)); double rap = firstPoint ? distance / totalDistance : (totalDistance - distance) / totalDistance; return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y))); } private static Point GetPointAtDistancePercent(Point p1, Point p2, double distancePercent, bool firstPoint) { double rap = firstPoint ? distancePercent / 100 : (100 - distancePercent) / 100; return new Point(p1.X + (rap * (p2.X - p1.X)), p1.Y + (rap * (p2.Y - p1.Y))); }
結論
依然有很多細節應該完善的。但這僅僅是圓角多邊形的一個嘗試。比如。在其他情況。每個角應該有不同晚景的圓角,WPF使得一切皆有可能。本文的目的是創建一個大家可以用的圓角多邊形,他們可以擴展來滿足他們的需求。
Demo下載
許可
本文包括源代碼和文件在CPOL下授權。
原文地址:WPF-rounded-corners-polygon
著作權聲明:本文由http://leaver.me 翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!