WPF椭圆弧形弧线控件

发布时间 2023-10-11 16:13:02作者: HotSky
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp2.Controls
{
    public class Arc : Shape
    {
        Size _size = Size.Empty;
        protected override Geometry DefiningGeometry {
            get
            {
                if(_size == Size.Empty)
                    return Geometry.Empty;
                return GetGeometry();
            }
        }

        Geometry GetGeometry()
        {
            double angle = this.EndAngle - StartAngle;
            if (angle == 0) return Geometry.Empty;
            if (Math.Abs(angle) >= 360d) return new EllipseGeometry(new Point(_size.Width / 2, _size.Height / 2), RadiusXNoThickness, RadiusYNoThickness);

            var path = new PathGeometry();
            var figure = new PathFigure();
            path.Figures.Add(figure);
            var arc = new ArcSegment();
            figure.Segments.Add(arc);

            arc.IsLargeArc = Math.Abs(EndAngle) > 180 || Math.Abs(EndAngle - StartAngle) > 180;
            arc.SweepDirection = EndAngle > StartAngle ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
            arc.Size = new Size(RadiusXNoThickness, RadiusYNoThickness);
            arc.Point = GetPoint(EndAngle);
            figure.StartPoint = GetPoint(StartAngle);
            if (IsStartCenter)
            {
                figure.Segments.Insert(0, new LineSegment(figure.StartPoint, true));
                figure.StartPoint = new Point(_size.Width / 2, _size.Height / 2);
            }
            if(IsClosed)
                figure.IsClosed = true;
            return path;
        }

        Point GetPoint(double angle)
        {
            double d = angle / 180 * Math.PI;
            double a = RadiusXNoThickness, b = RadiusYNoThickness;
            double t = a * b / Math.Sqrt(Math.Pow(a * Math.Sin(d), 2) + Math.Pow(b * Math.Cos(d), 2));
            double x = t * Math.Cos(d);
            double y = t * Math.Sin(d);
            var endPoint = new Point(x + a + (StrokeThickness / 2), b - y + (StrokeThickness / 2));
            return endPoint;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            _size = constraint;
            return base.MeasureOverride(constraint);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            _size = finalSize;
            return base.ArrangeOverride(finalSize);
        }

        double RadiusXNoThickness => Math.Max(_size.Width / 2 - StrokeThickness / 2, 0);
        double RadiusYNoThickness => Math.Max(_size.Height / 2 - StrokeThickness / 2, 0);

        /// <summary>
        /// 起始点角度
        /// </summary>
        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }

        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender));

        /// <summary>
        /// 终点角度
        /// </summary>
        public double EndAngle
        {
            get { return (double)GetValue(EndAngleProperty); }
            set { SetValue(EndAngleProperty, value); }
        }

        public static readonly DependencyProperty EndAngleProperty =
            DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender));

        /// <summary>
        /// 是否从椭圆中心开始画线
        /// </summary>
        public bool IsStartCenter
        {
            get { return (bool)GetValue(IsStartCenterProperty); }
            set { SetValue(IsStartCenterProperty, value); }
        }

        public static readonly DependencyProperty IsStartCenterProperty =
            DependencyProperty.Register("IsStartCenter", typeof(bool), typeof(Arc), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender));

        /// <summary>
        /// 是否封闭图形
        /// </summary>
        public bool IsClosed
        {
            get { return (bool)GetValue(IsClosedProperty); }
            set { SetValue(IsClosedProperty, value); }
        }

        public static readonly DependencyProperty IsClosedProperty =
            DependencyProperty.Register("IsClosed", typeof(bool), typeof(Arc), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsRender));
    }
}
View Code

 

属性:

StartAngle: 起点角度;

EndAngle: 终点角度;

IsStartCenter:是否从圆心开始画线;

IsClosed:是否封闭;

使用方式:

<control:Arc Width="100" Height="100" StartAngle="110" EndAngle="-45" Stroke="Blue" StrokeThickness="5" IsStartCenter="True" IsClosed="True" Fill="Orange"/>