Files
2022-11-01 15:26:08 +00:00

120 lines
4.1 KiB
C#

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace TheXamlGuy.UI.WPF.Controls;
public class Arc : Shape
{
public static readonly DependencyProperty DirectionProperty =
DependencyProperty.Register(nameof(Direction),
typeof(SweepDirection), typeof(Arc), new PropertyMetadata(SweepDirection.Clockwise));
public static readonly DependencyProperty EndAngleProperty =
DependencyProperty.Register(nameof(EndAngle),
typeof(double), typeof(Arc), new PropertyMetadata(0.0d, OnEndAnglePropertyChanged));
public static readonly DependencyProperty OriginRotationDegreesProperty =
DependencyProperty.Register(nameof(OriginRotationDegrees),
typeof(double), typeof(Arc), new PropertyMetadata(270.0, new PropertyChangedCallback(OnOriginRotationDegreesPropertyChanged)));
public static readonly DependencyProperty StartAngleProperty =
DependencyProperty.Register(nameof(StartAngle),
typeof(double), typeof(Arc), new PropertyMetadata(0.0d, OnStartAnglePropertyChanged));
public SweepDirection Direction
{
get => (SweepDirection)GetValue(DirectionProperty);
set => SetValue(DirectionProperty, value);
}
public double EndAngle
{
get => (double)GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
public double OriginRotationDegrees
{
get => (double)GetValue(OriginRotationDegreesProperty);
set => SetValue(OriginRotationDegreesProperty, value);
}
public double StartAngle
{
get => (double)GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
protected override Geometry DefiningGeometry => GetDefiningGeometry();
protected override Size MeasureOverride(Size constraint)
{
return constraint;
}
private static void OnEndAnglePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnEndAnglePropertyChanged();
}
private static void OnOriginRotationDegreesPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnOriginRotationDegreesPropertyChanged();
}
private static void OnStartAnglePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
(dependencyObject as Arc)?.OnStartAnglePropertyChanged();
}
private Geometry GetDefiningGeometry()
{
Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle), Direction);
Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle), Direction);
Size arcSize = new(Math.Max(0, (ActualWidth - StrokeThickness) / 2), Math.Max(0, (ActualHeight - StrokeThickness) / 2));
bool isLargeArc = Math.Abs(EndAngle - StartAngle) > 180;
StreamGeometry streamGeometry = new();
using (StreamGeometryContext context = streamGeometry.Open())
{
context.BeginFigure(startPoint, false, false);
context.ArcTo(endPoint, arcSize, 0, isLargeArc, Direction, true, false);
}
streamGeometry.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2);
return streamGeometry;
}
private void OnEndAnglePropertyChanged()
{
InvalidateVisual();
}
private void OnOriginRotationDegreesPropertyChanged()
{
InvalidateVisual();
}
private void OnStartAnglePropertyChanged()
{
InvalidateVisual();
}
private Point PointAtAngle(double angle, SweepDirection sweep)
{
double translatedAngle = angle + OriginRotationDegrees;
double radAngle = translatedAngle * (Math.PI / 180);
double xr = (RenderSize.Width - StrokeThickness) / 2;
double yr = (RenderSize.Height - StrokeThickness) / 2;
double x = xr + xr * Math.Cos(radAngle);
double y = yr * Math.Sin(radAngle);
y = sweep == SweepDirection.Counterclockwise ? yr - y : yr + y;
return new Point(x, y);
}
}