使用WPF创建画图箭头-阿里云开发者社区

开发者社区> 杰克.陈> 正文

使用WPF创建画图箭头

简介: 原文:使用WPF创建画图箭头 今天要给leader line画个箭头,所以就google一下,找到下面的文章,写的不错,可以实现我的需求,所以就摘录下来。
+关注继续查看
原文:使用WPF创建画图箭头

今天要给leader line画个箭头,所以就google一下,找到下面的文章,写的不错,可以实现我的需求,所以就摘录下来。

我把源代码中的arraw.cs加入到我的工程,修改了namespace,然后写了个方法进行调用:

        private void DrawLeaderLineArrow(Point startPt, Point endPt)
        {
            Arrow arrow = new Arrow();
            arrow.X1 = startPt.X;
            arrow.Y1 = startPt.Y;
            arrow.X2 = endPt.X;
            arrow.Y2 = endPt.Y;
            arrow.HeadWidth = 15;
            arrow.HeadHeight = 5;
            arrow.Stroke = Brushes.Black;
            arrow.StrokeThickness = 1;
            _canvas.Children.Add(arrow);
        }

 

WPF Arrow and Custom Shapes

 

Introduction

WPF is the best UI Framework ever. It provides us with a large arsenal of vector graphic types such as Line, Ellipse, Path and others. Sometimes we need shapes which are not provided in the WPF arsenal (such an Arrow), and with all due respect to the Path shape, which can be used to create any type of 2D shape, we do not want to recalculate each point every time. This is a good reason and opportunity to create a custom shape.

 

Background

WPF provides two kinds of vector types: Shapes and Geometries.

Shape is any type that derives from the Shape base class. It provides Fill, Stroke and other properties for coloring, and is actually a FrameworkElement. Thus we can place shapes inside Panels, we can register shapes routed events and do anything related to FrameworkElement. (MSDN)

Geometry is any type that derives from the Geometry base type. It provides properties for describing any type of 2D geometry. A geometry is actually a Freezable type, thus can be frozen. A frozen object provides better performance by not notifying changes, and can be safely accessed by other threads. Geometry is not Visual, hence should be painted by other types such as Path. (MSDN)

 

Using the Code

Now that we have a little background, and we know what the differences between a Geometry and Shape are, we can create our shape based on one of these two types. Correct?

Well, surprisingly we can't base our custom shape on the Geometry type, since its one and only default constructor is marked as internal. Shame on you Microsoft.

Don't worry! We still have an option to base our custom shape on the Shape base class.

Now, let’s say that we want to create an Arrow shape. An arrow is actually a kind of line, so let’s derive our custom type from the WPF Line type which has X1, Y1, X2 and Y2 properties.

Ooopps... Line is sealed! (Shame on you twice).

Never mind, let's derive directly from the Shape base class, and add X1, Y1, X2, Y2 and two additional properties for defining the arrow's head width and height.

 

Our code should end up with something like this:

Collapse Copy Code
    public sealed class Arrow : Shape
    {
        public static readonly DependencyProperty X1Property = ...;
        public static readonly DependencyProperty Y1Property = ...;
        public static readonly DependencyProperty HeadHeightProperty = ...;
        ...

        [TypeConverter(typeof(LengthConverter))]
        public double X1
        {
            get { return (double)base.GetValue(X1Property); }
            set { base.SetValue(X1Property, value); }
        }

        [TypeConverter(typeof(LengthConverter))]
        public double Y1
        {
            get { return (double)base.GetValue(Y1Property); }
            set { base.SetValue(Y1Property, value); }
        }

        [TypeConverter(typeof(LengthConverter))]
        public double HeadHeight
        {
            get { return (double)base.GetValue(HeadHeightProperty); }
            set { base.SetValue(HeadHeightProperty, value); }
        }
        ...

        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;
            }
        }
        
        /// <summary>
        /// Draws an Arrow
        /// </summary>
        private void InternalDrawArrowGeometry(StreamGeometryContext context)
        {
            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 + (HeadWidth * cost - HeadHeight * sint),
                Y2 + (HeadWidth * sint + HeadHeight * cost));

            Point pt4 = new Point(
                X2 + (HeadWidth * cost + HeadHeight * sint),
                Y2 - (HeadHeight * cost - HeadWidth * sint));

            context.BeginFigure(pt1, true, false);
            context.LineTo(pt2, true, true);
            context.LineTo(pt3, true, true);
            context.LineTo(pt2, true, true);
            context.LineTo(pt4, true, true);
        }
    }

As you can see, it is very easy to implement a custom shape, thanks to the great work in the Shape base class. All we have to do is derive our custom shape type from Shape, and override the DefiningGeometry property. This property should return a Geometry of any type.

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
WPF 使用 Edge 浏览器
原文:WPF 使用 Edge 浏览器 本文告诉大家如何使用 Windows Community Toolkit 的新控件,在 WPF 使用 Edge 浏览器 首先需要通过 VisualStudio 创建 WPF 项目。
1037 0
WPF使用HierarchicalDataTemplate绑定Dictionary生成TreeView
原文:WPF使用HierarchicalDataTemplate绑定Dictionary生成TreeViewDictionary中的CustomeType是一个集合,将其绑定生成一棵树,树的第一层节点是Dictionary的Key,第二层是CustomeType集合,所有代码用XAML实现。
1089 0
解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况
原文:解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况 wpf的ScrollViewer在触摸条件下 默认在尽头时会有一个窗口一起被拖动的FeedBack,但对用户的交互很不友好,尤其是全屏应用,一划就看到了后面的桌面。
1132 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
9217 0
使用WPF教你一步一步实现连连看(二)
连连看算法 第一步:我们考虑在同行或者同列的情况:同行或者同列又分三种情况:  第一:边线,RowNum或者ColNum等于0或者9   第二:两个相邻  第三:同行不相邻,这种事有条件的,所在行(或列)的上下(或左右)紧邻行必须在两个按钮之间已全部消除?好,有出问题了,怎么根据已知的行和列,获取Button是否被消除呢?我们可以定义一个Button的二维数组。
621 0
WPF中使用ObjectDataProvider绑定方法
原文:WPF中使用ObjectDataProvider绑定方法    ObjectDataProvider提供了绑定任意.net类型的功能,具体功能如下: 1.ObjectDataProvider提供了绑定任意CLR类型的公嫩那个。
928 0
WPF的Timer控件的使用
原文:WPF的Timer控件的使用 通过System.Threaing.Timer控件来实现“初始加载页面时为DataGrid的模版列赋初始值” System.Threaing.Timer的用法: 步骤1: //声明定时器 System.
828 0
使用WPF教你一步一步实现连连看(三)
这次首先对以前的结构进行了调整: 第一步:把MyButton按钮的属性独立成一个类,放在一个单独的MyButton.cs中,把图片的初始化也放到里面。 调整代码如下:   public class MyButton : Button     {         //图...
875 0
+关注
杰克.陈
一个安静的程序猿~
10427
文章
2
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载