天天看點

WPF 圖形繪制 及各種線帽、箭頭的實作

原文:

WPF 圖形繪制 及各種線帽、箭頭的實作

 /// <summary>

    /// 矩形類

    /// </summary>

    public sealed class CRectangle : VtShape

    {

        #region Overrides

        protected override Geometry DefiningGeometry

        {

            get

            {

                // Create a StreamGeometry for describing the shape

                StreamGeometry geometry = new StreamGeometry();

                geometry.FillRule = FillRule.EvenOdd;

                using (StreamGeometryContext context = geometry.Open())

                {

                    InternalDrawArrowGeometry(context);

                }

                // Freeze the geometry for performance benefits

                geometry.Freeze();

                return geometry;

            }

        }

        #endregion

        #region Privates

        private void InternalDrawArrowGeometry(StreamGeometryContext context)

            //因為線寬的不同會導緻區域顯示不全,是以在計算時需考慮線寬

            Point ptStart = new Point(this.StrokeThickness / 2, this.StrokeThickness / 2);

            Point pt2 = new Point(this.Width - this.StrokeThickness / 2, this.StrokeThickness / 2);

            Point pt3 = new Point(this.Width - this.StrokeThickness / 2, this.Height - this.StrokeThickness / 2);

            Point pt4 = new Point(this.StrokeThickness / 2, this.Height - this.StrokeThickness / 2);

            Point ptEnd = new Point(this.StrokeThickness / 2, 0);

            context.BeginFigure(ptStart, true, false);

            context.LineTo(pt2, true, true);

            context.LineTo(pt3, true, true);

            context.LineTo(pt4, true, true);

            context.LineTo(ptEnd, true, true);

    }

    /// <summary>

    /// 橢圓類

    public sealed class CEllipse : VtShape

            double w = this.Width - this.StrokeThickness * 2;

            double h = this.Height - this.StrokeThickness * 2;

            if (w > 0 && h > 0)

                Point pt = new Point(w / 2 + this.StrokeThickness, this.StrokeThickness);

                context.BeginFigure(pt, true, true);

                context.ArcTo(new Point(w / 2 + this.StrokeThickness, h + this.StrokeThickness), new Size(w / 2, h / 2), 0, true, SweepDirection.Counterclockwise, true, true);

                context.ArcTo(pt, new Size(w / 2, h / 2), 0, true, SweepDirection.Counterclockwise, true, true);

    /// 直線類

    public sealed class CLine : VtShape

        #region Dependency Properties

        public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        #region CLR Properties

        [TypeConverter(typeof(LengthConverter))]

        public double X1

            get { return (double)base.GetValue(X1Property); }

            set { base.SetValue(X1Property, value); }

        public double Y1

            get { return (double)base.GetValue(Y1Property); }

            set { base.SetValue(Y1Property, value); }

        public double X2

            get { return (double)base.GetValue(X2Property); }

            set { base.SetValue(X2Property, value); }

        public double Y2

            get { return (double)base.GetValue(Y2Property); }

            set { base.SetValue(Y2Property, value); }

        public CLine(Canvas linePanel)

            _panel = linePanel;

            _panel.Children.Add(_arrow.lineCap_start);

            _panel.Children.Add(_arrow.lineCap_end);

            double theta = Math.Atan2(Y1 - Y2, X1 - X2);

            double sint = Math.Sin(theta);

            double cost = Math.Cos(theta);

            Point pt1 = new Point(X1, this.Y1);

            Point pt2 = new Point(X2, this.Y2);

            Point pt3 = new Point(

                X2 + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),

                Y2 + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost));

            Point pt4 = new Point(

                X2 + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),

                Y2 - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint));

            Point pt5 = new Point(

              X1 - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),

              Y1 - (_arrow.TailWidth * sint + _arrow.TailHeight * cost));

            Point pt6 = new Point(

                X1 - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),

                Y1 + (_arrow.TailHeight * cost - _arrow.TailWidth * sint));

            //this.StrokeStartLineCap = PenLineCap.Triangle;

            //this.StrokeEndLineCap = PenLineCap.Triangle;

            //lineCap_start = new Polygon();

            PointCollection pts_start = new PointCollection();

            pts_start.Add(pt5);

            pts_start.Add(pt1);

            pts_start.Add(pt6);

            PointCollection pts_end = new PointCollection();

            pts_end.Add(pt3);

            pts_end.Add(pt2);

            pts_end.Add(pt4);

            SetLineCap( pt1, pt2, pt1, pt2);//設定線帽

            context.BeginFigure(pt1, false, false);

            //context.LineTo(pt5, true, true);

            //context.LineTo(pt1, true, true);

            //context.LineTo(pt6, true, true);

            //繪制第一個點的線帽

            context.LineTo(pt2, true, true);//繪制直線

            //context.LineTo(pt3, true, true);

            //context.LineTo(pt2, true, true);

            //context.LineTo(pt4, true, true);

            //繪制第二個點的線帽

    /// 右直角三角形類

    public sealed class CRightTriangle : VtShape

            double angle = Math.Atan(this.Height / (this.Width / 2));

            double offsetTop = this.StrokeThickness / Math.Cos(angle);

            double offsetLeft = this.StrokeThickness / Math.Tan(angle) + this.StrokeThickness / Math.Sign(angle);

            Point pt1 = new Point(this.Width - offsetLeft, 0);

            Point pt2 = new Point(0, this.Height - this.StrokeThickness);

            Point pt3 = new Point(this.Width - offsetLeft, this.Height - this.StrokeThickness);

            context.BeginFigure(pt1, true, true);

            context.LineTo(pt1, true, true);

    /// 左直角三角形類

    public sealed class CLeftTriangle : VtShape

            Point pt1 = new Point(0, 0);

    /// 等腰三角形類

    public sealed class CTriangle : VtShape

            Point pt1 = new Point(this.Width / 2, this.StrokeThickness);

            Point pt2 = new Point(offsetLeft, this.Height - this.StrokeThickness);

    /// 菱形類

    public sealed class CDiamond : VtShape

            Point pt2 = new Point(this.StrokeThickness, this.Height /2);

            Point pt3 = new Point(this.Width / 2, this.Height - this.StrokeThickness);

            Point pt4 = new Point(this.Width - this.StrokeThickness, this.Height / 2);

    public sealed class CFiveStar : VtShape

        private Point Intersection(Point pt1, Point pt2, Point pt3, Point pt4)

            double A1 = pt1.Y - pt2.Y;

            double B1 = pt2.X - pt1.X;

            double C1 = pt1.Y * (pt2.X - pt1.X) - pt1.X * (pt2.Y - pt1.Y);

            double A2 = pt3.Y - pt4.Y, B2 = pt4.X - pt3.X, C2 = pt3.Y * (pt4.X - pt3.X) - pt3.X * (pt4.Y - pt3.Y);

            //A1 * x + B1 * y = C1

            //A2 * x + B2 * y = C2

            double D = A1 * B2 - B1 * A2, Dx = C1 * B2 - B1 * C2, Dy = A1 * C2 - A2 * C1;

            double x = Dx / (double)D, y = Dy / (double)D;

            return new Point((int)x, (int)y);

        private Point RotateTheta(Point pt,Point center,double theta)

            double x = center.X + (pt.X - center.X) * Math.Cos((theta * Math.PI / 180)) - (pt.Y - center.Y) * Math.Sin((theta * Math.PI / 180));

            double y = center.Y + (pt.X - center.X) * Math.Sin((theta * Math.PI / 180)) + (pt.Y - center.Y) * Math.Cos((theta * Math.PI / 180));

            return new Point(x, y);

            Point ptCenter = new Point(this.Width / 2, this.Height / 2);

            double radius = this.Width / 2;

            Point[] pts = new Point[10];

            pts[0] = new Point(ptCenter.X, (int)(ptCenter.Y - radius ));

            for (int i = 1; i < 5; i++)

                pts[2 * i] = RotateTheta(pts[2 * (i - 1)], ptCenter, 72.0);

                pts[2 * i].X = (int)(ptCenter.X + (pts[2 * i].X - ptCenter.X) );

                pts[2 * i].Y = (int)(ptCenter.Y + (pts[2 * i].Y - ptCenter.Y) );

            //通過直線求交點的方式來取得1,3,5,7,9點坐标

            pts[1] = Intersection(pts[0], pts[4], pts[2], pts[8]);

            pts[3] = Intersection(pts[0], pts[4], pts[2], pts[6]);

            pts[5] = Intersection(pts[2], pts[6], pts[4], pts[8]);

            pts[7] = Intersection(pts[0], pts[6], pts[4], pts[8]);

            pts[9] = Intersection(pts[0], pts[6], pts[2], pts[8]);

            context.BeginFigure(pts[0], true, true);

            context.LineTo(pts[1], true, true);

            context.LineTo(pts[2], true, true);

            context.LineTo(pts[3], true, true);

            context.LineTo(pts[4], true, true);

            context.LineTo(pts[5], true, true);

            context.LineTo(pts[6], true, true);

            context.LineTo(pts[7], true, true);

            context.LineTo(pts[8], true, true);

            context.LineTo(pts[9], true, true);

    /// 三次貝塞爾曲線類

    public sealed class CBezierCurve : VtShape

        //public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        //public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        //public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        //public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(CLine), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        public static readonly DependencyProperty BezierPointsProperty = DependencyProperty.Register("BezierPoints", typeof(List<Point>), typeof(CBezierCurve), new FrameworkPropertyMetadata(new List<Point>(), FrameworkPropertyMetadataOptions.AffectsRender

| FrameworkPropertyMetadataOptions.AffectsMeasure));

        //public static readonly DependencyProperty BezierTmpPointProperty = DependencyProperty.Register("BezierTmpPoint", typeof(Point), typeof(CBezierCurve), new FrameworkPropertyMetadata(new Point(0,0), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        [TypeConverter(typeof(double))]

        public List<Point> BezierPoints

            get { return (List<Point>)base.GetValue(BezierPointsProperty); }

            set { BezierPoints.Clear(); base.SetValue(BezierPointsProperty, value); }

        //[TypeConverter(typeof(double))]

        //public Point BezierTmpPoint

        //{

        //    get { return (Point)base.GetValue(BezierTmpPointProperty); }

        //    set { base.SetValue(BezierTmpPointProperty, value); }

        //}

        private List<Point> _lstCtrPoints = new List<Point>();

        public bool _bDrawing = false;//是否正在繪制

        public Point BezierTmpPoint = new Point(0, 0);

            _lstCtrPoints.Clear();

            List<Point> lstPoints = new List<Point>();

            List<Point> lstCtrPoints = new List<Point>();

            foreach (Point pt in BezierPoints)

                lstPoints.Add(new Point(pt.X, pt.Y));

            if (_bDrawing == true && (BezierTmpPoint.X > 0 || BezierTmpPoint.Y > 0))

                lstPoints.Add(new Point(BezierTmpPoint.X, BezierTmpPoint.Y));

                for (int i = 0; i < lstPoints.Count; i++)

                    lstCtrPoints.AddRange(Control1(lstPoints, i));

                    if (i < lstPoints.Count - 1)

                    {

                        _lstCtrPoints.AddRange(Control1(lstPoints, i));

                    }

            else if (_bDrawing == false)

                    _lstCtrPoints.AddRange(Control1(lstPoints, i));

            if (lstPoints != null && lstPoints.Count > 1)

                Point ptStart = lstPoints[0];

                Point ptEnd = lstPoints[lstPoints.Count - 1];

                //先繪制開始線帽

                SetLineCap(lstPoints[lstPoints.Count - 2], ptEnd, ptStart, lstPoints[1]);

                context.BeginFigure(ptStart, true, false);

                //再繪制曲線

                for (int i = 1; i < lstPoints.Count; i++)

                    context.BezierTo(lstCtrPoints[i * 2 - 1], lstCtrPoints[i * 2], lstPoints[i], true, true);

        private List<Point> Control1(List<Point> list, int n)

            List<Point> point = new List<Point>();

            point.Add(new Point());

            if (n == 0)

                point[0] = list[0];

            else

                point[0] = Average(list[n - 1], list[n]);

            if (n == list.Count - 1)

                point[1] = list[list.Count - 1];

                point[1] = Average(list[n], list[n + 1]);

            Point ave = Average(point[0], point[1]);

            Point sh = Sub(list[n], ave);

            point[0] = Mul(Add(point[0], sh), list[n], 0.6);

            point[1] = Mul(Add(point[1], sh), list[n], 0.6);

            return point;

        private Point Average(Point x, Point y)

            return new Point((x.X + y.X) / 2, (x.Y + y.Y) / 2);

        private Point Add(Point x, Point y)

            return new Point(x.X + y.X, x.Y + y.Y);

        private Point Sub(Point x, Point y)

            return new Point(x.X - y.X, x.Y - y.Y);

        private Point Mul(Point x, Point y, double d)

            Point temp = Sub(x, y);

            temp = new Point(temp.X * d, temp.Y * d);

            temp = Add(y, temp);

            return temp;

        #region Public

        public CBezierCurve(Canvas linePanel)

        public void GetVectorRect(ref Point TopLeft, ref Point BottomRight)

            if (BezierPoints.Count > 0)

                TopLeft.X = BezierPoints[0].X;

                TopLeft.Y = BezierPoints[0].Y;

                BottomRight.X = BezierPoints[0].X;

                BottomRight.Y = BezierPoints[0].Y;

                #region 曲線頂點

                foreach (Point pt in BezierPoints)

                    if (pt.X < TopLeft.X)

                        TopLeft.X = pt.X;

                    if (pt.Y < TopLeft.Y)

                        TopLeft.Y = pt.Y;

                    if (pt.X > BottomRight.X)

                        BottomRight.X = pt.X;

                    if (pt.Y > BottomRight.Y)

                        BottomRight.Y = pt.Y;

                #endregion

                #region 曲線控制點

                foreach (Point pt in _lstCtrPoints)

        public void Clear()

                BezierPoints.Clear();

            if (_lstCtrPoints.Count > 0)

                _lstCtrPoints.Clear();

    /// 線帽樣式

    public enum EnumArrowStyle

        NULL = 0,

        STYLE1 = 1,

        STYLE2 = 2,

        STYLE3 = 3,

        STYLE4 = 4,

        STYLE5 = 5,

        STYLE6 = 6,

        STYLE7 = 7,

        STYLE8 = 8,

        STYLE9 = 9,

        STYLE10 = 10,

        STYLE11 = 11,

        STYLE12 = 12

    /// 線型樣式

    public enum EnumDottedStyle

        STYLE5 = 5

    /// 形狀類,為形狀設定線型、線帽

    public class VtShape : Shape

        #region Arrow Style

        public struct Arrow

            public double HeadWidth;

            public double HeadHeight;

            public double TailWidth;

            public double TailHeight;

            public Polygon lineCap_start;

            public Polygon lineCap_end;

            public Arrow(double _headWidth, double _headHeight, double _tailWidth, double _tailHeight, Polygon lineCapstart, Polygon lineCapend)

                HeadWidth = _headWidth;

                HeadHeight = _headHeight;

                TailWidth = _tailWidth;

                TailHeight = _tailHeight;

                lineCap_start = lineCapstart;

                lineCap_end = lineCapend;

        protected Arrow _arrow = new Arrow(0, 0, 0, 0, new Polygon(), new Polygon());

        public static readonly DependencyProperty ArrowStyleProperty = DependencyProperty.Register("ArrowStyle", typeof(EnumArrowStyle), typeof(VtShape), new FrameworkPropertyMetadata(EnumArrowStyle.NULL, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        protected Canvas _panel = null;

        [TypeConverter(typeof(EnumArrowStyle))]

        public EnumArrowStyle ArrowStyle

            get { return (EnumArrowStyle)base.GetValue(ArrowStyleProperty); }

            set

                base.SetValue(ArrowStyleProperty, value);

                #region 設定箭頭大小

                switch (ArrowStyle)

                    case EnumArrowStyle.NULL:

                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 0; _arrow.TailHeight = 0;

                        break;

                    case EnumArrowStyle.STYLE1:

                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 0; _arrow.TailHeight = 0;

                    case EnumArrowStyle.STYLE2:

                    case EnumArrowStyle.STYLE3:

                    case EnumArrowStyle.STYLE4:

                    case EnumArrowStyle.STYLE5:

                        _arrow.HeadWidth = 0; _arrow.HeadHeight = 0; _arrow.TailWidth = 30; _arrow.TailHeight = 20;

                    case EnumArrowStyle.STYLE6:

                    case EnumArrowStyle.STYLE7:

                    case EnumArrowStyle.STYLE8:

                    case EnumArrowStyle.STYLE9:

                        _arrow.HeadWidth = 30; _arrow.HeadHeight = 20; _arrow.TailWidth = 30; _arrow.TailHeight = 20;

                    case EnumArrowStyle.STYLE10:

                    case EnumArrowStyle.STYLE11:

                    case EnumArrowStyle.STYLE12:

        protected enum LineCapType

            LineCap_Start =0,

            LineCap_End =1

        protected void SetLineCap( Point pt1_start, Point pt1_end,Point pt2_start, Point pt2_end)

            _arrow.lineCap_start.Points = new PointCollection ();

            _arrow.lineCap_end.Points = new PointCollection ();

            _arrow.lineCap_start.Fill = ((Polygon)_arrow.lineCap_start).Stroke = this.Stroke; // Brushes.Yellow;

            _arrow.lineCap_end.Fill = ((Polygon)_arrow.lineCap_end).Stroke  = this.Stroke;//= Brushes.Yellow;

            _arrow.lineCap_start.StrokeThickness = _arrow.lineCap_end.StrokeThickness = this.StrokeThickness;

            switch (ArrowStyle)

                case EnumArrowStyle.NULL:

                    break;

                case EnumArrowStyle.STYLE1:

                    SetTriangle2Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE2:

                    SetTriangleCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE3:

                    SetRectCap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE4:

                    SetTriangle3Cap(LineCapType.LineCap_End, pt1_start, pt1_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE5:

                    SetTriangle2Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE6:

                    SetTriangleCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE7:

                    SetRectCap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE8:

                    SetTriangle3Cap(LineCapType.LineCap_Start, pt2_start, pt2_end, _arrow.lineCap_start, _arrow.lineCap_end);

                case EnumArrowStyle.STYLE9:

                case EnumArrowStyle.STYLE10:

                case EnumArrowStyle.STYLE11:

                case EnumArrowStyle.STYLE12:

        /// <summary>

        /// 封閉的三角形線帽

        /// </summary>

        /// <param name="pt_start"></param>

        /// <param name="pt_end"></param>

        /// <param name="cap_start"></param>

        /// <param name="cap_end"></param>

        private void SetTriangleCap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)

            double theta = Math.Atan2(pt_start.Y - pt_end.Y, pt_start.X - pt_end.X);

            if (lineCapType == LineCapType.LineCap_Start )

               cap_start.Points.Add(

                   new Point(

                  pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),

                  pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))

                  );

               cap_start.Points.Add(new Point(pt_start.X + 20 * cost, pt_start.Y + 20 * sint));

               cap_start.Points.Add( new Point(

                    pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),

                    pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))

                    );

            if (lineCapType == LineCapType.LineCap_End)

                cap_end.Points.Add(

                            new Point(

                                 pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),

                                 pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))

                         );

                cap_end.Points.Add(new Point(pt_end.X - 20 * cost, pt_end.Y - 20 * sint));

                 new Point(

                     pt_end.X + (_arrow.HeadWidth * cost + _arrow.HeadHeight * sint),

                      pt_end.Y - (_arrow.HeadHeight * cost - _arrow.HeadWidth * sint))

               );

        /// 非封閉的三角形線帽

        /// <param name="pt_start">端點1</param>

        /// <param name="pt_end">端點2</param>

        /// <param name="cap_start">端點1形狀</param>

        /// <param name="cap_end">端點2形狀</param>

        private void SetTriangle2Cap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)

            if (lineCapType == LineCapType.LineCap_Start)

                cap_start.Points.Add(

                    new Point(

                   pt_start.X - (_arrow.TailWidth * cost - _arrow.TailHeight * sint),

                   pt_start.Y - (_arrow.TailWidth * sint + _arrow.TailHeight * cost))

                   );

                cap_start.Points.Add(new Point(pt_start.X + 20 * cost, pt_start.Y + 20 * sint));

                cap_start.Points.Add(new Point(

                     pt_start.X - (_arrow.TailWidth * cost + _arrow.TailHeight * sint),

                     pt_start.Y + (_arrow.TailHeight * cost - _arrow.TailWidth * sint))

                     );

                cap_start.Points.Add(new Point(pt_start.X - 5 * cost, pt_start.Y - 5 * sint));

                cap_end.Points.Add(new Point(pt_end.X , pt_end.Y ));

                cap_end.Points.Add(new Point(pt_end.X - 25 * cost, pt_end.Y - 25 * sint));

                           new Point(

                                pt_end.X + (_arrow.HeadWidth * cost - _arrow.HeadHeight * sint),

                                pt_end.Y + (_arrow.HeadWidth * sint + _arrow.HeadHeight * cost))

                        );

        private void SetTriangle3Cap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)

                cap_start.Points.Add(new Point(pt_start.X, pt_start.Y));

                cap_start.Points.Add(new Point(pt_start.X - 1 * cost, pt_start.Y - 1 * sint));

                cap_end.Points.Add(new Point(pt_end.X, pt_end.Y));

                cap_end.Points.Add(new Point(pt_end.X - 1 * cost, pt_end.Y - 1 * sint));

        /// 封閉矩形線帽

        private void SetRectCap(LineCapType lineCapType, Point pt_start, Point pt_end, Polygon cap_start, Polygon cap_end)

                cap_start.Points.Add(new Point(pt_start.X , pt_start.Y ));

                cap_start.Points.Add(new Point(pt_start.X - 60 * cost, pt_start.Y - 60 * sint));

                cap_end.Points.Add(new Point(pt_end.X + 60 * cost, pt_end.Y + 60 * sint));

        #region Dotted Style

        private EnumDottedStyle _DottedStyle = EnumDottedStyle.NULL;

        public EnumDottedStyle DottedStyle

            set { _DottedStyle = value; this.StrokeDashArray = SetDottedStyle(_DottedStyle); }

            get { return _DottedStyle; }

        protected DoubleCollection SetDottedStyle(EnumDottedStyle style)

            DoubleCollection dotted = new DoubleCollection();

            if (style == EnumDottedStyle.NULL)

                dotted = null;

                return dotted;

            if (dotted == null)

                dotted = new DoubleCollection();

            dotted.Clear();

            switch (style)

                case EnumDottedStyle.STYLE1:

                    dotted.Add(3); dotted.Add(1);

                case EnumDottedStyle.STYLE2:

                    dotted.Add(1); dotted.Add(1); dotted.Add(4); dotted.Add(1);

                case EnumDottedStyle.STYLE3:

                    dotted.Add(7); dotted.Add(2);

                case EnumDottedStyle.STYLE4:

                    dotted.Add(5); dotted.Add(1);

                case EnumDottedStyle.STYLE5:

                    dotted.Add(1); dotted.Add(1);

            return dotted;

效果圖如下:

WPF 圖形繪制 及各種線帽、箭頭的實作
WPF 圖形繪制 及各種線帽、箭頭的實作