1. 引言
文题中所谓技术要点再分享,本意是想在大神Charltsing Liu的博文“简单介绍Excel单元格行列指示的实现原理(俗称聚光灯功能)”的基础上写一点个人开发体会。写本文的初衷有三点,一是自己学习的一个记录。二是,尽管Charltsing Liu公开了原理,还是鲜有人能够开发出来,准确的来说免费的插件里头只有博主他自己的比较完美的实现了,收费的里面也只有Kutools,市面上的聚光灯(Excel单元格行列/阅读模式)实在质量不咋滴。三是,很多Excel插件开发者,尤其VBAer,想在自己的工具箱里加入这么一个功能,但实际往往止步于Charltsing Liu的原理,只能望洋兴叹。基于此,本文权当是Charltsing Liu大神的狗尾续貂。
有过EXCEL插件开发经验或者Excel中重度用户可能都知道“Excel阅读模式“或叫“行列指示”or“聚光灯”,个人认为行列指示似乎更贴切。很多插件里也都能看到这个功能。 比如“易用宝”,“方方格子”“Excel催化剂”,“uuoffice”,“DnaTools”,“Kutools for Excel(收费)”等等(若我没记错,这些都有,总之,凡是有这一功能的几乎我都有测试过)。
所谓Excel阅读模式/行列指示/聚光灯这样一个功能, 其本质就是“实现在选中的单元格所在的行列上加上一个可视化的标记以使其行列信息突出显现。”
这样一个功能在实际中是非常有用的(据说WPS有原生),尤其当面对表格行列外观设计区分度不高,数据量又较多,需要查阅数据和补录数据等操作的时候。如下两图,便可看出。
一种这样的功能,其实现原理,Charltsing Liu在其博文中已有较为概括性的阐述,可以参考阅读。本文再次分类简述如下,共有5种实现方式:
◆ 初级VBAer方式
利用excel选择事件,动态改变target的底色。(影响既定格式,对行列数据可能造成遮罩。)
◆ 中级VBAer方式
利用excel 动态插入线段,在事件中动态删除更改线段的位置,即所谓线段指示。(可能影响操作,可能线段被用户无意选中,影响用户体验。)
◆ 高级VBAer方式
利用excel条件格式,通过设置指示活动单元格行列的格式达到目的。条件格式影响既定的条件格式,也会影响Excel某些原生功能的使用。
这里有个外文网站,有兴趣的可以戳阅,里面有xlsm供下载:http://www.tushar-mehta.com/publish_train/xl_vba_cases/0121%20highlight%20row%20and%20col%20of%20selected%20cell.shtml
◆ 大神级别——这样做
通过一序列WindowsAPI获得相关坐标,基于GDI技术,在Excel7窗口上绘图实现。然而这只能在2010版本之前的excel上实现,具体原因是EXCEL2013改变了刷新方式,会导致这种方式的实现会闪瞎眼。
◆ 真大神级别——这样做
这种技术称为HUD技术,所谓HUD技术就是在正常画面上,叠加一个透明或者半透明效果的窗口,用来显示一些实时的数据。玩过飞行模拟之类游戏的,都会看到这种效果。很多电影的炫酷特效画面也经常能看到。(此段原文见liucaq博文)。这种叠加的窗体是相对独立存在的,只有加上一些特定的消息处理,限定处理窗口与宿主窗口的关系,是完全不会影响原宿主窗口的。在Excel阅读模式/单元格行列指示/聚光灯中具体实现可概括为:为Excel增加一个透明且鼠标可以穿透的层级窗体,通过计算excel单元格行列的位置等确定窗体的位置,然后在事件中动态更改窗体的外观(异形窗体)或者直接在窗体上通过GDI进行绘制显示标记。
以上这5种实现方式均能不同程度的解决实际问题,然而,个人以为,这个功能的实现方式必须满足以下5个条件,才能算是一种比较完美的解决方案:
第一:不影响活动行列的原数据的阅读。
第二:不影响既定的Excel单元格格式。
第三:不影响EXCEL原生的功能使用。
第四:适应表格的各种视图模式,比如拆分,冻结模式,Zoom,以及各种操作。
第五:最大的兼容不同的Excel版本。
显而易见,只有HUD技术的实现能满足这五个条件。
2.阅读模式/行列指示/聚光灯的优秀作品品评
个人认为,目前以这种方式实现的作品比较知名且使用较为完美的有DnaTools(大神liucaq的作品,免费),kutools for EXCEL(收费),Uuoffice(免费),最后的就是当然就是本人的PowerExcel Helper。
DnaTools(v2.9.7.2) 的行列指示,速度非常快,作者是真大神级别^_^。据说有很多国内第一或者填补了空白,真假难辨(^_^,请各位自辨)。目前版本我测试出的bug是 影响Excel插入图表后的图表菜单的使用,且功能开启后,插入图标,新建窗口,有卡屏的bug。另外还有个线段位置计算的问题。
Kutools for EXCEL,是个收费的插件,其阅读模式,我原来一直觉得是最完美的实现。包括运行速度等都不错,如果苛刻地说,启动有点慢。直到有个网友,提供了一个工作文档给我,反馈说kutools在他这个文档里有个严重的bug,会导致Excel奔溃,才断送了在我心目中的最完美实现。
Uuofifce1.6里的阅读模式呢,bug稍微多一些,我也给作者反馈过。比如有些消息的捕获不够准,导致有延迟的现象存在,另外在窗口有拆分和冻结的情况下表现也不是很好,作者似乎没有考虑这些情况。纵使如此,本人觉得依然是比较优秀的插件,而且开发者几乎不打广告,比较低调。
下方这段广告预警,谨慎阅读^_^^_^^_^
此部分最后,不得不谈下我的作品PowerExcel Helper v1.00中的“行列指示(聚光灯)”。就目前测试情况来说,没有上面三款插件里提到的bug,相比之下,算是最完美无暇的了。(牛皮就是吹的 ^_^),速度也一样OK。
3.开发分享——技术要点
最后,也是本文最重要的部分,来谈一谈实现这个功能的几点体会。Liucaq大神说过的,尽量不再重复,核心技术或者要点,概括起来有:
◆1.创建的载体窗体,最重要的是要处理好它与宿主窗口(xlmain?xldesk?excel7?)的关系,比如父子?比如从属?等关系,记得我在QQ群里(Office developer开发者550672198)里提过。这个是有api可以做到的。如果不去处理这个关系,就要花费很多代码去拦截消息。很费力。
◆2.就是如何使用Excel提供的函数Point sToScreenPixelsX/Y来计算单元格相对屏幕像素的位置。我发现几乎很少人知道怎么使用这个函数,至少我真没有发现。在这里说下,要使用Point sToScreenPixelsX/Y进行excel单元格或表格可视区域进行计算,它的父级是Pane而不是Window,这样的原因可能导致了有插件没有去考虑窗格拆分冻结的情况。也直接影响了EH论坛帖子“[原创] 【重磅发布】:聚光灯——我的加载宏系列小工具【单元格小工具】之四”作者对于Zoom情况的计算,最后其工具妥协于仅适用Zoom=100%的情况使用,另外该文作者也没有进行消息处理。(当然,或许作者是有所保留吧,如是,请原谅我的愚钝)。
◆3。在多窗格计算的时候不要过于依赖excel对象提供的函数,否则有意想不到的bug惊喜,而且你很难调试及预想到。
◆4.不同版本的EXCEL子窗口的消息还不太一样,这点也很让人费心。开发者多关注这个消息WM_MDIGETACTIVE。顺提一句,某些插件的bug很有可能是WM_STYLECHANGGED消息造成。
◆5.VBA实现此功能要相对容易得多,因为可以忽略处理窗口关系。若想尝试开发,可以从这句去得到启示。
◆6.消息的拦截光有HOOK 还不行,得接管子窗口的WndProc过程两者相结合。因为鼠标在EXCEL窗口外发生MOUSEWHEEL时候,是没法单纯hook直接处理的。当然如果不接管WndProc,单独用Hook也是没有问题的,32bit/64bit都能支持。只是有个小Bug,完全不影响使用。若用Hook,请查看WH_CALLWNDPROCRET。
◆7.要注意C#在32位/64位平台使用WindowsApi时平台指针地址和委托的转换。
以上七点已经接近开源了,如还不够用,请参看EH论坛帖子“[原创] 【重磅发布】:聚光灯——我的加载宏系列小工具【单元格小工具】之四”和Charltsing Liu的博文。
【总结】
不少人通过QQ私聊问我能不能直接给他源代码,私以为,有了Charltsing Liu大神的实现原理一博文和本文的七条提示,实现阅读模式/行列指示/聚光灯之类功能,应该没有难处了。只需要你有足够的耐心和钻研劲,技术能力总是会得到逐步提升的。
最后感谢Charltsing Liu大神在开发交流中给予的帮助支持,围观者应该多向他们这类人学习,不断提升技术。
另外,还不得不提下,EH论坛帖子“[原创] 【重磅发布】:聚光灯——我的加载宏系列小工具【单元格小工具】之四”的帖主ggmmlol(excelhome注册网名),这篇帖子很想详细的讲述了开发的1/3的内容,正是有了这篇帖子的指引,才促使我去学习开发本文的功能,也才有动力去翻阅各种资料(耗时近一个月的周末时间)。再次感谢ggmmlol,为技术分享精神点赞。
文末来个小小彩蛋吧,给有兴趣的VBAer提个idea以简单的优化实现这个功能:利用选择事件,添加删除shape(线段或者半透明的矩形),然后hook消息,当用户鼠标选中了这些特定的shape的时候,直接删除。这样的好处时,可以不用处理WM_SIZE,WM_Move等繁琐的消息。Shape的位置也可以不用转换屏幕坐标。也不影响表格其他的功能使用。代码简单了很多。成本极低,只需要学习Hook的使用。个人觉得理论是可行的。如有大神觉得idea太Low,请随意拍砖,不要客气。
声明:本文用到的词汇如初级VBAer、大神级等,表述的方式而已,没有贬低用意。