[原创]《C#高级GDI+实战:从零开发一个流程图》第10章:鼠标拖动完成连线、拖动时及时表现半透明虚线连线结果、自界说毗连点样式

[复制链接]
发表于 2025-11-26 17:59:09 | 显示全部楼层 |阅读模式
一、前言

上节课程我们实现了毗连外形差异的毗连点,但在实际使用中会发现很繁琐,必要分别指定开始外形和竣事外形的毗连点,这显着不符合利用履历逻辑。我们本节课程就来实现鼠标拖动完成连线,拖到哪个毗连点就对哪个毗连点完成连线,所见即所得,而且在拖动连线过程中,还会及时表现虚线箭头用于提示。
信任看完的你,肯定会有所劳绩!
本文所在:https://www.cnblogs.com/lesliexin/p/19096107
二、先看结果

通过视频我们可以看到,我们本节课就要依次实现3种结果:
1,鼠标拖动连线、连线过程中有虚线箭头提示,且连线样式也酿成了带箭头的样式。
2,外形所占地区的精准判断、连线点突出表现,特别是在外形配景致与毗连同色时更好区分。
3,每个外形的毗连点都不一样,和外形长得一样。
本节课代码已上传Gitee,读者可自行拉取编译。
Gitee所在:https://gitee.com/lesliexin/flowchartdemo
三、结果1:鼠标拖动连线、连线过程中有虚线箭头提示

这个结果乍看起来很复杂,无从动手。不消关键,我们先拆分一下,一点点的去实现。
(一)鼠标进入外形后,表现全部毗连点

我们看了上面的视频,已经有了开端印象,连线最开始,是鼠标移动到外形上时,鼠标表现全部的毗连点,如下图所示:

我们就先来实现这个结果。
1,外形基类调解

(1)获取毗连点所占地区

要想表现全部的毗连点,也就是画出来毗连点,起主要有获取毗连点所占地区的方法。
我们之前的外形基类已经有了获取毗连点坐标点的方法:

那么我们毗连点所占地区直接以此为根本,就是求一个以毗连点为中央的矩形。
我们设定毗连点的宽高都是10,那么就可以参照上述方法实现获取毗连点地区的方法:

我们界说为虚方法,就是上节讲的一样,有些外形(如:平行四边形)的边并不在矩形地区边上,以是还要提供让派生外形自行盘算的本领。
平行四边形:


(2)获取全部毗连点地区

由于我们现在就支持左、上、右、下四个毗连点,以是获取全部毗连点所点地区直接依次调用上面的方法即可:

(3)绘制全部毗连点

在获取到全部毗连点后,我们就可以举行绘制了,我们同样界说为虚方法,默认就以添补矩形的方式举行绘制:

2,画布调解

我们的外形基类已经支持获取并绘制全部毗连点了,接下来就要让画布支持这个功能了。
(1)增长连线模式

我们要实现的是所见即所得的拖动连线方式,以是增长一个‘连线模式’,此模式下就可以举行连线利用,而默认的就是‘正常模式’,可以拖动外形等等。
读者可以追念一下一样寻常利用流程图、PPT等,一样寻常都是点个连线样式的按钮,然后就可以拖动连线的,这就是进入了‘连线模式’
我们界说一个罗列,来表现画布所支持的模式:

同时界说一个属性,来表现当前画布所处的模式:

(2)移除不必要的属性、字段、方法、事故

当我们实现了拖动添加连线后,原来有一些属性、字段、方法、事故等就不再必要了,以是我们将这些移撤消。
我们已经不必要再提示‘请选择开始外形’,然后在选择了一个外形后再提示‘请选择竣事外形’等等,以是直接移撤消:

这个开始外形和竣事外形的选中毗连点还是必要的,但是不再必要作为公共属性暴袒露来,这个是在拖动选择时主动设置的,以是我们改成私有变量:


我们已经不再必要手动开始连线及中断连线了,以是我们直接将这两个公共方法移撤消:

(3)重绘事故,判断鼠标是否进入了外形中

我们要想在连线模式下,鼠标进入外形中就主动表现全部毗连点,那么就起首必要在鼠标移动事故(MouseMove)中始终使控件重绘,而不是之前那样只在移动外形时才重绘。

然后在OnPaint事故中,我们在绘制完外形和连线后,判断当前有无外形在鼠标之下,有则绘制别的形的全部毗连点:

如许,我们就实现了鼠标进入外形后,表现全部毗连点的结果。
(二)聚焦鼠标所在的毗连点

在能表现出全部的毗连点后,下一步就是鼠标选择毗连点,此时要有反馈结果,也就是令鼠标所在的毗连点聚焦表现、高亮表现。结果如下:

有了上面的根本,这个就很好实现了,在表现全部毗连点后,接着判断下鼠标是否在毗连点上就行了。
我们下面来看一下怎样实现。
1,外形基类调解

(1)确定当前鼠标是否在某一毗连点上

我们要先判断当前鼠标是否在某一个毗连点上:

同样,我们界说为虚方法,默认是以矩形地区举行判断的。
(2)获取鼠标所在毗连点

当我们判断出了当前鼠标在某一毗连点上之后,我们就要获取到鼠标所在毗连点是哪一个:

这里我们将判断与获取分成了两个方法,为什么明显一个方法就能既判断又获取,还要拆分成两个方法呢?除了所谓的责任分别外,最重要的缘故原由是判断的时间多,而且一样寻常都在遍历中,分开更好一些。
(3)绘制聚集状态毗连点

我们取到了毗连点,那么直接取毗连点的地区绘制即可:

2,画布调解

这里就很简朴了,判断鼠标是否在毗连点上,在的话就调用聚焦绘制毗连点的方法绘制即可:

到此我们就实现了鼠标移动到毗连点上时,毗连点聚焦或高亮表现的结果了。
(三)鼠标拖动及时表现半透明虚线箭头

我们接着来看一下拖动连线过程中的结果,就是及时表现一条从开始外形点击的毗连到到当前鼠标的半透明虚线箭头。结果如下:

这个本质上就是及时绘制一条从开始外形毗连点到鼠标点的连线,原理很简朴,我们来看一下具体怎样实现。
1,外形基类调解

我们可以看到,虚线箭头的出发点就是开始外形的毗连点,以是我们要提供一个绘制选中毗连点的方法:

同样,界说为虚方法,默认以矩形方式添补。
2,画布调解

那么,出发点怎么确定的呢?
(1)鼠标点击确定出发点

我们在上一末节里,为了判断鼠标是否在某一毗连点上时,实现了一个方法:IsHitLinkRect,以是我们要在鼠标点击时,也就是MouseDown事故中,判断鼠标是否点在了毗连点地区内,假如点在此中则设置开始外形和开始毗连点信息,并设置进入了连线中的状态:

(2)鼠标移动中绘制虚线箭头

在有了出发点,我们就要及时获取当前鼠标信息,并发起重绘。我们前面的末节里已经改造了MouseMove事故,始终触发重绘,以是我们只必要纪录一下鼠标的当前坐标,并在重绘事故中绘制虚线箭头即可。


(3)带箭头的虚线

上面一末节还是有个小的知识点的,就是怎样绘制一条带箭头的虚线。这着实完满是由画笔(Pen)来实现的,具体请参考MSDN,本文不再赘述:

到此,我们就实现了拖动连线时及时表现出发点到当前鼠标点的虚线箭头。
(四)鼠标松开完成添加连线、或取消连线

还是上一末节的图示,我们在松开鼠标时,要到达的结果只有两个:添加连线和取消连线。
而判断标准就是:松开鼠标时,鼠标是否在另一个外形的某一个毗连点地区内。
我们在鼠标松开事故MouseUp中举行判断,并根据判断结果添加连线或取消连线:

至此,我们的结果1也就实现了,颠末我们一点点的拆解,可以看到复杂的结果也是由一个个根本的结果组合而成的,当一个个根本结果实现之后,复杂结果也就天然而然的实现了。
四、结果2:外形地区正确判断、毗连点突出表现

上面结果1的结果不错,但是还是有些细节必要思量:一好坏矩形时鼠标还未移动到外形内,毗连点就全表现出来了;二是毗连点是赤色的,假如外形颜色也是赤色的,就是导致毗连点分不出来。我们本末节就来优化这两点。
(一)外形地区正确判断

这个不但是本节所遇到的,在前面的课程里着实也有这个题目,就好坏矩形时,我们点击非外形内时,仍可以拖动,就像下面如许:

这是由于我们在判断时是直接按外形所在矩形地区(Rect属性)来判断的,而不是严酷按照外形地区举行判断:

同理,我们看文章开头的演示视频也可以看到,当鼠标还没进入外形中时,全部的毗连点就表现出来了:

缘故原由同样是由于我们是按照外形所在矩形地区(Rect属性)来判断的:

题目定位到了,我们就针对性的对修改即可。
1,外形基类调解

我们增长一个虚方法,来判断鼠标点是否在当前外形中:

同样提供默认实现,就是按矩形地区去判断。
2,派生外形调解

基类调解后,我们要对矩形外的别的派生外形都重写判断方法,来判断鼠标点是否在外形内。
(1)圆形

很简朴,我们构造一个闭合圆形路径,然后判断即可:

(2)菱形

菱形则比力简朴,由于我们的菱形就是以路径实现的:

以是我们把这天生路径的部门提出来,给重写判断方法即可:



(3)平行四边形

同菱形,不再赘述:



(4)圆角矩形

同上,不再赘述:



3,画布调解

我们修改画布中判断鼠标点是否在外形内的地方,改为调用我们的判断方法:
(1)判断是否点击在了外形内


(2)判断是否移动鼠标到了外形内


至此,我们就实现了外形地区的精准判断。
(二)毗连点高亮表现

在上一末节的结果1中,我们会发现,当外形颜色是赤色时,由于毗连点也是赤色,以是会导致毗连点和外形肴杂在了一起,不显着,如下图所示:

以是我们就来为毗连点增长高亮颜色,就像聚焦时表现黄色边框那样。
代码也很简朴,我们就像聚焦里那样,在绘制毗连点时,直接增长上一个白框即可:

五、结果3:再次抽象同一、每个外形的毗连点样式都不一样

我们在上一末节有没有发现一些重复的代码逻辑?
没错!就是那些判断鼠标点是否在外形内时,除了矩形外,都要通过天生路径的方式去判断,而且除了圆形外别的外形不但判断方法要用,绘制方法也在使用。
既然重复,那就存在抽象和同一的时机,我们下面就来看一下,怎样再次抽象和同一。
(一)抽象、同一

1,外形基类修改

起首,我们上一节修改各外形时,除了矩形外都用到了天生路径利用,而且矩形也是支持用路径方式绘制的,以是我们先增长一个获取路径的方法:

我们界说为虚方法,默认天生矩形路径,各外形自行实现路径。
这里有一点必要注意的是,我们添加一个参数Rectangle rect,为什么不直接用属性Rect呢?这是为了实现让毗连点样式与外形保持划一的结果,此时大概不太明确,由于这相当于二次抽象,只是不好再单开一末节讲这一点,就合一起了,下文随即就能看到了。
然后我们的外形基类的Draw方法就不必要再是抽象方法了,由于已经能获取到路径了,以是直接在基类里绘制即可:

固然现在以我们的需求而言,并不再必要派生外形自行实现绘制了,但是仍界说为虚方法,以保持后续兼容,及特别情况下仍支持外形自行绘制。
2,派生外形调解

(1)矩形

由于外形基类默认就是以矩形方式实现的,以是矩形不必要实现任何重写。

注:以路径方式实现,性能肯定不如直接绘制矩形的方式快,但是这点性能斲丧险些可以忽略不计,而且在代码结构会更轻便。
(2)圆形

前面的精准判断外形时,圆形已经实现了通过路径的方式来判断,以是我们直接将天生圆形路径的方法提出来改下就行了:

(3)菱形

这个更简朴,由于已经有现成的天生路径的方法了,轻微改一下就行了:

(4)平行四边形

同上,不再赘述:

(5)圆角矩形

同上,不再赘述:

至此,全部派生外形均以改造完成,可以看到从上一末节变复杂后,又轻便了起来,这就是抽象和同一的魅力。
(二)每个外形的毗连点都不一样

我们前面课程已经说了,我们将绘制毗连点的方法界说为虚方法,就是能让各派生外形自行实现差异的毗连点。但是在我们上面给抽象同一后,我们就不必要再让各派生外形去自行绘制毗连点外形了,我们直接使用方法GetShapeGPath(Rectangle Rect),然后将毗连点所占矩形地区传入进去就主动返回和外形一样的路径了,之后绘制即可。
以是我们本末节只必要修改外形基类即可。
1,通过路径绘制差异毗连点

由于绘制毗连点时,有绘制默认状态、聚焦状态等结果,但是焦点区别就是边框的颜色差异,以是我也同样抽象同逐一下,提供两个重载方法,可以通过参数绘制差异状态的结果。而内部就是调用GetShapeGPath(Rectangle Rect)获取毗连点的样式路径:

2,绘制全部毗连点方法修改

我们这里要改成遍历实验新增长的绘制连线点方法:

注意,我们这里仍界说为虚方法,由于不是全部的外形作毗连点都符合或方便点击利用,以是我们仍生存自行实现毗连点样式的本领。
3,绘制差异状态的毗连点方法修改

同上,不再赘述:

4,判断鼠标点是否在毗连点地区内方法修改

同上,不再赘述:

至此,我们就实现了让毗连点样式酿成和外形样式划一的结果了。
5,小题目优化

在实现时,我们会发现别的外形的毗连点都正常,但是圆角矩形却不是:

这是由于我们在编写圆角矩形时,将圆角的直径固定为了20导致的:

修改也很简朴,我们直接将半径改为宽度的10分之一即可,就是倒霉用固定值,而是根据外形所占地区动态厘革:

我们再次运行,就可以看到是正常的了:

注:这个也是为后续的缩放结果做修复,假如不改成相对值,在缩放时就会出题目,而改成如许后,圆角就会随着缩放的巨细而动态厘革了。
六、结语

我们本节课实现了拖动完成连线,且连线过程中及时表现虚线箭头以提示正处连线中,同时还举行了再次抽象和同一,不但代码再次轻便,还让我们只用极小的修改就实现毗连点与外形保持划一的结果。这就是抽象的魅力。
估计不少读者在看到上节课程结语时说的本节课的结果时,都一头雾水,不知道该怎样实现,但是真当我们一点点实现后,会发现并不难,乃至太过简朴了。这并不是代码上的难,而是思索实现角度的难,本节课程,并不但实用于GDI+,险些全部的语言只要想实现类似的结果,根本逻辑都是一样的,具备普适性。
那么下节课,我们将实现另一个所见即所得的结果:拖动外形控制点来调解外形巨细。读者们也请想想该怎样实现,信任有了本节的根本,各人可以很轻易的想到怎样实现。
感谢各人的观看,本人水平有限,文章不敷之处接待各人品评指正。
-[END]-

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表