ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 22499|回复: 45

[原创] [申请精华]背景高亮着色-完美解决冻结,拆分,缩放,隐藏(适用Excel2007以上)

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2012-8-30 11:17 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:Windows API应用
本帖最后由 loze2000 于 2012-8-30 20:02 编辑

代码借鉴了stanleypan模仿XColor 功能的加载宏里面的源代码。
优点:
1,直接计算选中单元格(或区域)的屏幕坐标值
2,直接计算拟画高亮边框矩形值。
3,直接在Windows系统桌面DeskTop的DC上作图,它使用的就是屏幕坐标值。
4,完美解决了冻结,拆分和隐藏单元格的影响。

对stanleypan不足的改进:
1,stanleypan使用了一个比较本色的办法,采用API函数GetCursor获取当前鼠标位置(屏幕像素坐标),左右移动,判断该移动的点下面是不是当前选中的Cell,以此来获得选中单元格的边界。
     可悲的是,键盘移动就会混乱。
2,后面的版本(stanleypan封装成了dll,所以看不见源码),stanleypan应该使用了获取输入光标(不是鼠标)屏幕像素坐标的办法,使用的是API函数GetCaretPos。这解决了键盘移动选中单位各的问题。

不足:使用了Pane.PointsToScreenPixelX函数,这个函数只有Excel2007版本以后版本才支持!

多说两句:
1,Excel的里面Window对象PointsToScreenPixelX函数非常失败,至今没弄明白它内部是怎么个原理。此前介绍说
         Window.PointsToScreenPixelX(X)=
Window.PointsToScreenPixelX(0)+X 。得出的结果为屏幕坐标。其中输入的坐标应该是屏幕像素值。
但是,可悲的是在拆分和冻结下。在不同的Pane里面执行。结果让人混乱了。至今没理顺其关系。

2,Excel2007里面,给Pane添加了同样的函数PointsToScreenPixelX,可喜的是,这才是我们需要的函数。其X值就是Excel系统所使用的Point坐标系统值。输出的就是系统的屏幕坐标Pixel值。

上代码:
  1. Option Explicit

  2. '定义数据结构
  3. Public Type POINTAPI '用于API里面的点
  4.     x As Long
  5.     y As Long
  6. End Type

  7. Public Type RECT
  8.     Left As Long
  9.     Top As Long
  10.     Right As Long
  11.     Bottom As Long
  12. End Type

  13. Public Type CustCOLORS
  14.     crSelBack As Long
  15.     crSelBorder As Long
  16. End Type

  17. '自定义变量
  18. Public Colors As CustCOLORS '用clxXColor来操作它,真系不爽,待修改之
  19. Public Const HWND_DESKTOP = 0 '在获取桌面句柄的参数
  20. Public Const LOGPIXELSX = 88 '调用X方向上的系数
  21. Public Const LOGPIXELSY = 90 '调用Y方向上的系数



  22. '系统需要的变量
  23. 'Public Const OCR_NORMAL = 32512
  24. Public Const WHITE_BRUSH = 0
  25. Public Const LTGRAY_BRUSH = 1
  26. Public Const GRAY_BRUSH = 2
  27. Public Const DKGRAY_BRUSH = 3
  28. Public Const BLACK_BRUSH = 4
  29. Public Const NULL_BRUSH = 5
  30. Public Const HOLLOW_BRUSH = NULL_BRUSH
  31. Public Const WHITE_PEN = 6
  32. Public Const BLACK_PEN = 7
  33. Public Const NULL_PEN = 8
  34. Public Const OEM_FIXED_FONT = 10
  35. Public Const ANSI_FIXED_FONT = 11
  36. Public Const ANSI_VAR_FONT = 12
  37. Public Const SYSTEM_FONT = 13
  38. Public Const DEVICE_DEFAULT_FONT = 14
  39. Public Const DEFAULT_PALETTE = 15
  40. Public Const SYSTEM_FIXED_FONT = 16
  41. Public Const STOCK_LAST = 16


  42. 'SetROP2函数所用常量
  43. '------'绘图模式常数表---------        常数             DrawMode                 像素值
  44. Public Const R2_BLACK = 1    '  'R2_BLACK        vbBlackness                  黑色
  45. Public Const R2_NOTMERGEPEN = 2    '  'R2_NOTMERGEPEN  vbNotMergePen               R2_MERGEPEN的反色
  46. Public Const R2_MASKNOTPEN = 3    '  'R2_MASKNOTPEN   vbMaskNotPen            画笔颜色的反色与显示颜色进行AND运算
  47. Public Const R2_NOTCOPYPEN = 4    '  'R2_NOTCOPYPEN    vbNotCopyPen            R2_COPYPEN的反色
  48. Public Const R2_MASKPENNOT = 5    '  'R2_MASKPENNOT  vbMaskPenNot            显示颜色的反色与画笔颜色进行AND运算
  49. Public Const R2_NOT = 6    '          'R2_NOT          vbInvert                当前显示颜色的反色
  50. Public Const R2_XORPEN = 7    '  'R2_XORPEN        vbXorPen                   显示颜色与画笔颜色进行异或运算
  51. Public Const R2_NOTMASKPEN = 8    '  'R2_NOTMASKPEN    vbNotMaskPen                  R2_MASKPEN的反色
  52. Public Const R2_MASKPEN = 9    '  'R2_MASKPEN       vbMaskPen                   显示颜色与画笔颜色进行AND运算
  53. Public Const R2_NOTXORPEN = 10    '  'R2_NOTXORPEN     vbNotXorPen                   R2_XORPEN的反色
  54. Public Const R2_NOP = 11    '  'R2_NOP           vbNop                       不变
  55. Public Const R2_MERGENOTPEN = 12    'R2_MERGENOTPEN   vbMergeNotPen         画笔颜色的反色与显示颜色进行OR运算
  56. Public Const R2_COPYPEN = 13    '  'R2_COPYPEN     vbCopyPen                   画笔颜色
  57. Public Const R2_MERGEPENNOT = 14    'R2_MERGEPENNOT  vbMergePenNot            显示颜色的反色与画笔颜色进行OR运算
  58. Public Const R2_MERGEPEN = 15    '  'R2_MERGEPEN     vbMergePen                 画笔颜色与显示颜色进行OR运算
  59. Public Const R2_WHITE = 16    '  'R2_WHITE       vbWhitness                    白色
  60. Public Const R2_LAST = 16    '

  61. 'GetSystemMetrics要用到的,获取窗口的边框大小,X表示横轴方向,Y表示纵轴方向
  62. Public Const SM_CXBORDER As Long = 5
  63. Public Const SM_CYBORDER As Long = 6
  64. Public Const SM_CXDLGFRAME As Long = 7
  65. Public Const SM_CYDLGFRAME As Long = 8


  66. Public Declare Function GetSystemMetrics Lib "user32.dll" (ByVal nIndex As Long) As Long

  67. Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
  68. 'Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
  69. 'Public Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
  70. Public Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
  71. Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
  72. Public Declare Function GetCursor Lib "user32" () As Long
  73. Public Declare Function DrawEdge Lib "user32" (ByVal hdc As Long, qrc As RECT, ByVal edge As Long, ByVal grfFlags As Long) As Long


  74. Public Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle&, ByVal nWidth&, ByVal crColor&) As Long
  75. Public Declare Function SetBkMode Lib "gdi32.dll" (ByVal hdc As Long, ByVal nBkMode As Long) As Long
  76. Public Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long
  77. Public Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
  78. Public Declare Function OleTranslateColor Lib "oleaut32.dll" (ByVal lOleColor As Long, ByVal lHPalette As Long, ByRef lColorRef As Long) As Long
  79. Public Declare Function FrameRect Lib "user32.dll" (ByVal hdc As Long, ByRef lpRect As RECT, ByVal hBrush As Long) As Long

  80. Public Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
  81. Public Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long

  82. Public Declare Function Rectangle Lib "gdi32" (ByVal hdc&, ByVal x1&, ByVal y1&, ByVal x2&, ByVal y2&) As Long
  83. Public Declare Function GetDesktopWindow Lib "user32" () As Long
  84. Public Declare Function GetWindowDC Lib "user32.dll" (ByVal hwnd As Long) As Long
  85. Public Declare Function SaveDC Lib "gdi32" (ByVal hdc As Long) As Long
  86. Public Declare Function RestoreDC Lib "gdi32" (ByVal hdc As Long, ByVal nSavedDC As Long) As Long
  87. Public Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
  88. Public Declare Function CreateSolidBrush Lib "gdi32.dll" (ByVal crColor As Long) As Long
  89. Public Declare Function RedrawWindow Lib "user32" (ByVal hwnd As Long, lprcUpdate As RECT, ByVal hrgnUpdate As Long, ByVal fuRedraw As Long) As Long
  90. Public Declare Function SetROP2 Lib "gdi32" (ByVal hdc As Long, ByVal nDrawMode As Long) As Long
  91. Public Declare Function UpdateWindow Lib "user32" (ByVal hwnd As Long) As Long
  92. Public Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal x1 As Long, ByVal y1 As Long, ByVal x2 As Long, ByVal y2 As Long) As Long
  93. Public Declare Function OffsetRect Lib "user32.dll" (ByRef lpRect As RECT, ByVal x As Long, ByVal y As Long) As Long

  94. Public Declare Function GetCapture Lib "user32" () As Long
  95. Public Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
  96. Public Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As Long, ByRef lpRect As RECT) As Long
  97. Public Declare Function GetCaretPos Lib "user32" (lpPoint As POINTAPI) As Long
  98. Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hwndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
  99. Public Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

  100. Public Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

  101.    
  102. Public gButtonDown As Boolean '菜单上高亮是否有效。用来做切换开关,用菜单切换。以便需要时显示
  103.    
  104. Public Sub Do_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
  105. '整理下思路
  106. '难度在于,Excel中所有的长度、宽度单位都是以Point为单位。Point是真是长度单位,和Inch,Twip一样。1Point=1inch/72
  107. '而系统函数(API)的的单位是使用Pixel(屏幕坐标),这是和长度不直接发生关系的。它需要根据系统的对照表来查询,从而得知其与Point等的对应关系。
  108. '因此,我们想要获得当前活动单元格的边界范围,然后转化成系统的屏幕坐标。调用API函数完成画图。
  109. '在画图时,DC的选择非常重要,为了简单,直接用"桌面DeskTop"的DC,这样,转化成的屏幕坐标直接可以用。
  110. '这里特别注意,为什么不选择其他窗口的DC呢,因为其中设计到要将屏幕坐标转换到该窗口中去。由于列标题和行标题的存在,这似乎比较困难。

  111.     '变量定义区
  112.     Dim ActWinRect As RECT '当前主窗口矩形
  113.     Dim VScrBarRect As RECT '定义垂直滚动条(ScrollBar)的矩形
  114.     Dim HScrBarRect As RECT '定义水平滚动条ScrollBar的矩形框
  115.     Dim actCellFrame As RECT '当前单元格的边界,上下左右,最远端
  116.     Dim hdc As Long  '设备句柄变量
  117.     Dim hWndWin As Long '定义Excel的主程序,工作区(XLDesk),当前活动表的窗口句柄
  118.     Dim hWndHScrBar, hWndVScrBar As Long '主窗口下面Scrollbar的句柄
  119.     Dim HwdDesktop As Long '系统桌面的句柄
  120.     Dim PointsPerPixelX, PointsPerPixelY As Single '点坐标转换成屏幕坐标的转换比率,X是横向,Y是纵向
  121.     Dim brush As Long  '刷子
  122.     'Dim color As clsColor '颜色
  123.     Dim HighLightRect As RECT '画框矩形
  124.     Dim winSysBoderWidth, winSysBoderHeight As Long '窗口边框大小
  125.    
  126.    
  127.     On Error Resume Next
  128.     If Not ModHighLight.gButtonDown Then Exit Sub  '菜单上没有选中"高亮显示",则退出
  129.     If Target.Areas.Count > 1 Then Exit Sub '如果是多个区域,则退出
  130.    
  131.         
  132.     'Set color = New clsColor
  133.     'color.WindowStyle = vbGreenStyle    ' vbXPStyle
  134.    
  135.     '获取点(Point)坐标转换成屏幕像素(Pixl)坐标时的转换比率,因为要用到直接画图,所以这个是必须的
  136.     '注意:Point是绝对长度单位(=1/72Inch)。Excel中用的就是Point坐标
  137.     PointsPerPixelX = (100 / ActiveWindow.Zoom) / (GetDeviceCaps(GetWindowDC(HWND_DESKTOP), LOGPIXELSX) / 72)  'X方向-每屏幕像素(Pixel)对应多少点(Point)
  138.     PointsPerPixelY = (100 / ActiveWindow.Zoom) / (GetDeviceCaps(GetWindowDC(HWND_DESKTOP), LOGPIXELSY) / 72)   'Y方向-每屏幕像素(Pixel)对应多少点(Point)

  139.     '获得鼠标所在的窗口句柄,重重查找,从XLMAIN 到XLDESK,再到EXCEL7
  140.     hWndWin = FindWindow("XLMAIN", Application.Caption) 'Excel主程序窗口句柄
  141.     hWndWin = FindWindowEx(hWndWin, 0&, "XLDESK", vbNullString) 'Excel工作区(XLDESK)窗口句柄
  142.     hWndWin = FindWindowEx(hWndWin, 0&, "EXCEL7", vbNullString)    'Excel 主窗体句柄,主要是指当前ActiveSheet的窗口
  143.     '还需要获得两个ScrollBar的句柄,其类名都是“NUIScrollbar”,标题各位“垂直”和“水平"
  144.    
  145.     hWndHScrBar = FindWindowEx(hWndWin, 0&, "NUIScrollbar", "水平") '水平滚动条的句柄
  146.     hWndVScrBar = FindWindowEx(hWndWin, 0&, "NUIScrollbar", "垂直") '垂直滚动条的句柄
  147.    
  148.     '当前单元格的边界(在屏幕Pixl坐标下)
  149.     actCellFrame.Left = CLng(ActiveWindow.PointsToScreenPixelsX(Target.Left / PointsPerPixelX))
  150.     actCellFrame.Top = CLng(ActiveWindow.PointsToScreenPixelsY(Target.Top / PointsPerPixelY))
  151.     actCellFrame.Right = actCellFrame.Left + CLng(Target.Width / PointsPerPixelX)
  152.     actCellFrame.Bottom = actCellFrame.Top + CLng(Target.Height / PointsPerPixelY)
  153.     '当前单元格的边界(在屏幕Pixl坐标下)
  154.     actCellFrame.Left = CLng(ActiveWindow.ActivePane.PointsToScreenPixelsX(Target.Left))
  155.     actCellFrame.Top = CLng(ActiveWindow.ActivePane.PointsToScreenPixelsY(Target.Top))
  156.     actCellFrame.Right = actCellFrame.Left + CLng(Target.Width / PointsPerPixelX)
  157.     actCellFrame.Bottom = actCellFrame.Top + CLng(Target.Height / PointsPerPixelY)
  158.   
  159.     winSysBoderWidth = GetSystemMetrics(SM_CXDLGFRAME) '获得系统窗口X方向边框大小
  160.     winSysBoderHeight = GetSystemMetrics(SM_CYDLGFRAME) '获得系统窗口Y方向边框大小
  161.     GetWindowRect hWndWin, ActWinRect '窗口矩形空间,在屏幕坐标下
  162.     GetWindowRect hWndHScrBar, HScrBarRect '获得水平滚动条的矩形
  163.     GetWindowRect hWndVScrBar, VScrBarRect '获得垂直滚动条的矩形
  164.   
  165.    '计算拟画列矩形的边框(在屏幕Pixel坐标下),Column
  166.     HighLightRect.Left = actCellFrame.Left
  167.     HighLightRect.Top = ActWinRect.Top + winSysBoderHeight
  168.     HighLightRect.Right = actCellFrame.Right
  169.     HighLightRect.Bottom = ActWinRect.Bottom - (HScrBarRect.Bottom - HScrBarRect.Top) - 2 * winSysBoderHeight
  170.    
  171.     Application.ScreenUpdating = True '暂停更新,非常重要

  172.     If hdc = 0 Then hdc = GetWindowDC(HWND_DESKTOP)   '获得Windows桌面绘图设备的句柄,因为我想所有坐标都在它之下,就不需要转换了。
  173.      
  174.     brush = CreateSolidBrush(RGB(193, 210, 238))    '创建蓝色画刷 (&HFF0000)
  175.     Call SelectObject(hdc, brush) '在DC中选中刷子
  176.     SetROP2 hdc, R2_NOTXORPEN    '异或(Xor)作图模式。不影响其他颜色对其的画图。
  177.     Call SaveDC(hdc)    '保存画笔和刷子
  178.     Call SelectObject(hdc, brush)    '设备空刷子
  179.       
  180.     Rectangle hdc, HighLightRect.Left, HighLightRect.Top, HighLightRect.Right, HighLightRect.Bottom '画列方向矩形
  181.    
  182.    
  183.     '计算拟画行矩形的边框(在屏幕Pixel坐标下),Row
  184.     HighLightRect.Left = ActWinRect.Left + winSysBoderWidth
  185.     HighLightRect.Top = actCellFrame.Top
  186.     HighLightRect.Right = ActWinRect.Right - (VScrBarRect.Right - VScrBarRect.Left) - 2 * winSysBoderWidth
  187.     HighLightRect.Bottom = actCellFrame.Bottom
  188.    
  189.     Rectangle hdc, HighLightRect.Left, HighLightRect.Top, HighLightRect.Right, HighLightRect.Bottom '画行方向矩形

  190.     Call RestoreDC(hdc, -1)    '-1是恢复以前的内容'恢复DC设备,即回复SaveDC保存的DC
  191.     ReleaseDC hWndWin, hdc    '删除窗口设备句柄,释放资源
  192.     DeleteObject brush '删除画笔对象
  193.     ReleaseDC hWndWin, hdc '释放句柄
  194.    

  195. End Sub
复制代码
下一步努力的目标就是:弃用Pane.PointsToScreenPixelX。使其适用与2003

点评

美中不足:当缩放系数为非整数倍,如 115%,点击单元格 BZ300,形成的误差非常明显!如何“修正”是个问题。  发表于 2014-9-10 18:02

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2012-11-15 11:46 | 显示全部楼层
本帖最后由 edwin11891 于 2012-11-15 18:20 编辑

效果很好,在我的2003上已经可以正常运行。改进效果不错,尤其对stanleypan不足的第一条改进,望继续完整。如高亮颜色可自选,另有窗口冻结时高亮显示行会出错。

TA的精华主题

TA的得分主题

发表于 2013-1-23 07:38 | 显示全部楼层
你这里面的想法及用法有些不足,他们其实都不是这么做的

TA的精华主题

TA的得分主题

发表于 2013-5-25 16:23 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
怎么用啊?不会用啊,请指点

TA的精华主题

TA的得分主题

发表于 2013-5-25 20:20 | 显示全部楼层
一楼代码有点问题,简单改改,凑合能用了

GDI写的聚光镜.rar (29.6 KB, 下载次数: 751)

补充内容 (2013-6-10 23:14):
顺便说一下,这个代码绘图的时候写错HDC了,应该写到Excel7窗体上,这样代码简洁多了

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-5-27 08:17 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2013-6-8 18:02 | 显示全部楼层
本帖最后由 三戒 于 2013-6-8 18:08 编辑

测试一下,在office 2013下,能瞬间显示一下,稍候就没有了,估计跟office 2013有关

TA的精华主题

TA的得分主题

发表于 2013-8-24 00:05 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 liucqa 于 2014-6-14 13:51 编辑
三戒 发表于 2013-6-8 18:02
测试一下,在office 2013下,能瞬间显示一下,稍候就没有了,估计跟office 2013有关

俺的DnaTools凑合能用GDI绘制,支持2013   http://club.excelhome.net/thread-1026277-1-1.html

罗刚君的百宝箱2013不是gdi写的,使用起来有限制
王明柏的KuTools 7.5功能比较完善,但是收费

这个是用shape做的,Jan Karel Pieterse作品
follow cell pointer.zip (36.16 KB, 下载次数: 627)

TA的精华主题

TA的得分主题

发表于 2013-9-17 20:51 | 显示全部楼层
liucqa 发表于 2013-5-25 20:20
一楼代码有点问题,简单改改,凑合能用了

效果不错,感谢分享。
测试了一下,选择整行或整列的时候,没有想要效果。

,

TA的精华主题

TA的得分主题

发表于 2013-9-17 21:00 | 显示全部楼层
yf_home 发表于 2013-9-17 20:51
效果不错,感谢分享。
测试了一下,选择整行或整列的时候,没有想要效果。

你想要什么?
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-24 05:10 , Processed in 0.050727 second(s), 10 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表