WPF を含め XAML には XY 座標系で始点終点を指定して描く円弧の Path はありますが、始点終点を角度で指定する円弧がありません・・・
ないものは・・・作ってみました!
<UserControl x:Class="ShapeSample.Controls.Arc" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:ShapeSample.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Path x:Name="Shape"/> </UserControl>
まずは XAML、ユーザーコントロールを作成して上記のように Path を追加しただけ
Path.Data プロパティを隠蔽したいので Path を継承したクラスではなくユーザーコントロールにしてコンポジションにします
コードビハインドの中身は次のように記述します
/// <summary> /// 角度指定できる円弧シェイプ /// </summary> public sealed partial class Arc : UserControl { #region StartAngle 依存関係プロパティ /// <summary> /// StartAngle 依存関係プロパティ /// </summary> public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register( "StartAngle", typeof(double), typeof(Arc), new PropertyMetadata( 0d, (s, e) => { var control = s as Arc; if (control != null) { control.OnStartAngleChanged(); } })); /// <summary> /// StartAngle 変更イベントハンドラ /// </summary> private void OnStartAngleChanged() { this.Render(); } /// <summary> /// 始点角度 /// </summary> public double StartAngle { get { return (double)this.GetValue(StartAngleProperty); } set { this.SetValue(StartAngleProperty, value); } } #endregion //StartAngle 依存関係プロパティ #region EndAngle 依存関係プロパティ /// <summary> /// EndAngle 依存関係プロパティ /// </summary> public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register( "EndAngle", typeof(double), typeof(Arc), new PropertyMetadata( 360d, (s, e) => { var control = s as Arc; if (control != null) { control.OnEndAngleChanged(); } })); /// <summary> /// EndAngle 変更イベントハンドラ /// </summary> private void OnEndAngleChanged() { this.Render(); } /// <summary> /// 終点角度 /// </summary> public double EndAngle { get { return (double)this.GetValue(EndAngleProperty); } set { this.SetValue(EndAngleProperty, value); } } #endregion //EndAngle 依存関係プロパティ #region Radius 依存関係プロパティ /// <summary> /// Radius 依存関係プロパティ /// </summary> public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register( "Radius", typeof(double), typeof(Arc), new PropertyMetadata( 100d, (s, e) => { var control = s as Arc; if (control != null) { control.OnRadiusChanged(); } })); /// <summary> /// Radius 変更イベントハンドラ /// </summary> private void OnRadiusChanged() { this.Render(); } /// <summary> /// 半径 /// </summary> public double Radius { get { return (double)this.GetValue(RadiusProperty); } set { this.SetValue(RadiusProperty, value); } } #endregion //Radius 依存関係プロパティ #region StrokeThickness 依存関係プロパティ /// <summary> /// StrokeThickness 依存関係プロパティ /// </summary> public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( "StrokeThickness", typeof(double), typeof(Arc), new PropertyMetadata( 1d, (s, e) => { var control = s as Arc; if (control != null) { control.OnStrokeThicknessChanged(); } })); /// <summary> /// StrokeThickness 変更イベントハンドラ /// </summary> private void OnStrokeThicknessChanged() { this.Shape.StrokeThickness = this.StrokeThickness; } /// <summary> /// 枠線の太さ /// </summary> public double StrokeThickness { get { return (double)this.GetValue(StrokeThicknessProperty); } set { this.SetValue(StrokeThicknessProperty, value); } } #endregion //StrokeThickness 依存関係プロパティ ~ 以下 IShape の依存関係プロパティは省略 ~ #region StrokeStartLineCap 依存関係プロパティ #endregion //StrokeStartLineCap 依存関係 #region StrokeMiterLimit 依存関係プロパティ #endregion //StrokeMiterLimit 依存関係プロパティ #region StrokeLineJoin 依存関係プロパティ #endregion //StrokeLineJoin 依存関係プロパティ #region StrokeEndLineCap 依存関係プロパティ #endregion //StrokeEndLineCap 依存関係プロパティ #region StrokeDashOffset 依存関係プロパティ #endregion //StrokeDashOffset 依存関係プロパティ #region StrokeDashCap 依存関係プロパティ #endregion //StrokeDashCap 依存関係プロパティ #region StrokeDashArray 依存関係プロパティ #endregion //StrokeDashCap 依存関係プロパティ #region Stroke 依存関係プロパティ #endregion //Stroke 依存関係プロパティ #region Fill 依存関係プロパティ #endregion //Stretch 依存関係プロパティ #region Fill 依存関係プロパティ #endregion //Fill 依存関係プロパティ #region GeometryTransform 依存関係プロパティ #endregion //GeometryTransform 依存関係プロパティ /// <summary> /// コンストラクタ /// </summary> public Arc() { this.InitializeComponent(); } /// <summary> /// 描画する /// </summary> public void Render() { var start = this.StartAngle; var end = this.EndAngle; if (this.StartAngle > this.EndAngle) { start = this.EndAngle; end = this.StartAngle; } var figure = new PathFigure(); figure.IsClosed = false; figure.StartPoint = this.ComputeAngle(start); for (double i = start + 1; i < end; i++) { figure.Segments.Add(new ArcSegment() { IsLargeArc = false, RotationAngle = 0, Size = new Size(this.Radius, this.Radius), Point = this.ComputeAngle(i), SweepDirection = SweepDirection.Clockwise, }); } if (Math.Floor(this.EndAngle) < this.EndAngle) { figure.Segments.Add(new ArcSegment() { IsLargeArc = false, RotationAngle = 0, Size = new Size(this.Radius, this.Radius), Point = this.ComputeAngle(this.EndAngle), SweepDirection = SweepDirection.Clockwise, }); } var geometory = new PathGeometry(); geometory.Figures.Add(figure); this.Shape.Data = geometory; } /// <summary> /// 角度を XY 座標に変換する /// </summary> /// <param name="angle">角度</param> /// <returns>XY 座標</returns> private Point ComputeAngle(double angle) { return new Point(this.Radius + (this.Radius * Math.Cos(angle * Math.PI / 180)), this.Radius + (this.Radius * Math.Sin(angle * Math.PI / 180))); } }
始点・終点の角度と半径のプロパティを追加し、残りは Shape から継承したいプロパティを追加しました
始点から終点までを一気に Path で描画することができないので、角度1度ずつ ArcSegment をつないで描画するという愚直な方法です
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ctrl:Arc HorizontalAlignment="Center" VerticalAlignment="Center" EndAngle="150" Radius="100" StartAngle="-60" Stroke="#FFB25920" StrokeThickness="30" /> </Grid>
さっそく画面の XAML に上記のように記述して実行してみると
できました!円グラフとかにも応用できそう