![第4章,[标签 Win32] :SysMets3 程序讲解05,水平滚动](http://pic.xiahunao.cn/yaotu/第4章,[标签 Win32] :SysMets3 程序讲解05,水平滚动)
专栏导航上一篇第4章[标签 Win32] SysMets3 程序讲解04垂直滚屏重绘回到目录下一篇第5章[标签 Win32] GDI 的结构与原理本节前言对于本节所讲解的知识有可能你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点请你自行查阅。我呢也会提到一部分的参考课节。但是呢你不应该依赖于我的主动提及。最好呢你自己能够多去了解和查看本专栏目录。本节我们需要结合着 SysMets3 的代码来讲解知识。关于 SysMets3 的代码请参考如下课节。参考课节第4章[标签 Win32] SysMets3 程序代码想要学习好本节最好呢你对之前的几节已经掌握了。在这里我姑且推荐几个参考课节。参考课节第4章[标签 Win32] SysMets3 程序讲解01参考课节第4章[标签 Win32] SysMets3 程序讲解02iVertPos参考课节第4章[标签 Win32] SysMets3 程序讲解03垂直滚屏原理参考课节第4章[标签 Win32] SysMets3 程序讲解04垂直滚屏重绘本节我们要去讲解的是 WM_PAINT 消息处理代码中对于水平滚动条的处理部分。一. iHorzPos 的设置与滚屏原理讲述我们来看一看 WM_PAINT 消息处理的开头几行的代码。图1在开头几行最主要的 是做了两件事那就是通过调用 GetScrollInfo 函数获取垂直滚动条和水平滚动条的当前位置值分别将这两个位置值保存在变量 iVertPos 和 iHorzPos 中。如图1 的227 行与 232 行所示。此时所获得的滚动条位置它是滚动条的最新的位置。在这里我以水平滚动条为例来说明这个位置值的含义。一般地我们在使用一个程序比如一个文本编辑器或者是记事本或者是 Notepad或者是 VS Code当我们在使用滚动条来翻看文本的时候在我们拖动滚动条的同时文本就跟着卷动了。而在程序的运行的时候程序的运行机理与我们所看到的可能不是一致的。在这里我以逻辑的思路将其暂且分解开来。这种分解不见得对但是呢暂时这么来分解着还是可以有助于我们的理解。另外呢为了理解与讲解的方便我们假定程序中使用的文本字体为等宽字体所有字符的宽度都一致。当我们单击水平滚动条的滑块右方的滚动条区域的时候在程序的 WM_HSCROLL 消息的处理代码中已经是设置好了滑块的新位置值并且已经通过调用 SetScrollInfo 函数并在其中的最后一个参数中指定 TRUE使得滚动条已经被重绘了。当滚动条重绘好了以后滚动条滑块已经被绘制在新位置上了。在这个时候滚动条滑块已经处在新位置上了但是文本内容尚未完成滚屏工作。所以呢对于单击水平滚动条的滑块右方的滚动条空白位置这一操作其运行效果要分为两部分。第一阶段滚动条滑块要变到新位置上而客户区内容尚未进行滚屏工作。第二阶段客户区内容进行滚屏活动。水平滚动条的滚屏工作有一部分是在 WM_HSCROLL 消息代码中进行的也就是在 ScrollWindow 函数中进行的另一部分则是在 WM_PAINT 消息处理代码中进行。对于滚屏工作根据上一节的讲解我们也可以将其分为了两个阶段。第一阶段客户区的某些可见内容进行滚屏滚屏后依然显示在客户区中。滚屏中还会出现新的列新的列显示为空白列。第二阶段对空白列进行重绘。不过佩措尔德先生在编写水平滚动条的代码的时候他在第二阶段简化了代码逻辑不是单独针对空白列进行重绘而是针对某一行的所有字符进行重绘。我们的代码也采用了佩措尔德先生的做法。也就是说在我们的程序里面水平滚动条的处理逻辑分为两个阶段。第一阶段客户区的某些可见内容进行滚屏滚屏后依然显示在客户区中。滚屏中还会出现新的列新的列显示为空白列。第二阶段对该行文本的所有字符进行重绘。我们接着往下讲。在我们的理解中我们可以认为在 WM_HSCROLL 消息处理代码中滚屏操作完成了第一阶段的任务即客户区中原来的部分可见内容的滚屏与产生新的空白行。第一阶段的工作是在调用 ScrollWindow 函数时进行并完成的。而滚屏的第二阶段的工作需要在 WM_PAINT 消息处理代码中进行。当程序运行到 WM_PAINT 的处理代码刚刚运行完图1 所示的 232 行代码行的时候此时iHorzPos 获取了滚动条滑块的位置值。这个位置值它不是单击滑块右方的滚动条空白区域之前的旧位置值而是单击了滑块右方的滚动条空白区域以后已经重绘了滚动条之后的滑块的新位置值。只是在这个时候滑块已经是变动到新位置了而由滚屏动作所产生的空白行的部分尚未进行重绘。二. 文本宽度假定一般来讲在程序中绘制文本的时候我们既可以使用等宽字体也可以使用变宽字体。并且很多时候我们使用的都是变宽字体。但是呢在本节的讲解过程中以及在你的理解活动中我们将其简化处理而将本节所使用的字体看作是等宽字体所有文本字符的宽度都一致。这一点第一分节其实已经是说过了的。不过呢在这里我还是再次来强调一遍。我们接着往下看。三. for 循环中的文本重绘代码我们来看代码截图。图2图2 中for 循环的开始是设置 x 和 y而这个 x 和 y 分别作为245 行的的 TextOut 调用的 x 实参和 y 实参。再往后的几个 TextOut 函数调用我们在 SysMets1 和 SysMets2 里面是讲过的。因此我们本节不详细展开 249 行到 259 行只重点关注着 245 行的 TextOut 调用。245 行的 TextOut 函数调用中x 和 y 实参位置传过去的便是 242 和 243 行设置的 x 和 y 变量。242 行设置的 x 变量它与水平滚动条有关。243 行设置的 y 变量它与垂直滚动条有关。本节我们不讲垂直滚动条的内容。垂直滚动条的内容我们之前已经讲过了。忘了的请参考本节前言中列出的相关课节。因此在本节对于 y我们可以忽略它。或者你可以在 245 的 TextOut 函数调用中将 y 看作是 0 就可以了。在进一步地你可以将 245 到 259 行中出现的 x全都看作是 0 。总之本节我们不管 y 变量只探讨 x 变量的使用。四. x 的初始化我们来看一看图2 中 242 行的设置 x 的代码。x cxChar * (1 - iHorzPos);其中的【1 - iHorzPos】它是阿拉伯数字 1 减去 iHorzPos而不是英文字母 L 的小写 l 减去 iHorzPos 。这个 1 有啥用我们将这个程序改一改将其中的 1 改为 10然后我们重新编译并运行程序结果如下。图3关闭程序。然后呢再将 刚才的10 改为 30然后重新编译运行程序结果如下。图4关闭程序并将刚刚的 30改回 1如下图所示。图5我们接着讲。比较一下图3 与图4我们可以看出来1 也好10 也好30 也好它所管的是在初始显示的时候文本的第一列的字符距离客户区边的字符数。如果这个数是1则初始显示时第一列字符距离客户区左边的距离为 1 个字符。如果这个数是10则初始显示时第一列字符距离客户区左边的距离为 10 个字符。如果这个数是30则初始显示时第一列字符距离客户区左边的距离为 30 个字符。为什么是这样呢我们之前说过242 行的 iHorzPos 为水平滚动条的新位置值。我们暂且假定【x cxChar * (1 - iHorzPos);】。因为当程序初始运行第一次进入 WM_PAINT消息处理代码时到了程序的第 242 行iHorzPos 的值为 0 。因为刚刚创建的滚动条其滑块位置值就是 0 啊。当 iHorzPos 为 0 时那么它左边的 num 是多少则【num - iHorzPos】就等于多少x 便会等于多少个 cxChar 的值。而图2 的 245 行对应的初始 TextOut 调用中便是用这个 x 值作为实参的。因此num 等于多少初始显示时文本的第一列的字符距离客户区左边缘便是多少个字符宽度。这个问题就算是暂且说完了我们接着往下看。五. 水平重绘说完了初始化显示我们再来看一般化的水平重绘。在初始显示时 iHorzPos 等于 0 。而在使用程序的过程中它的值就不一定了。可能等于 0可能等于 6 9 12等等。假定在某一刻iHorzPos 的值等于 3 。在这个假定之下我们来考察程序的显示情况。遵循本篇文章的第二分节的假定程序中使用等宽字体所有文本字符的宽度一致。我们再次贴出 x 的赋值代码。x cxChar * (1 - iHorzPos);已知 iHorzPos 3且所有文本字符宽度一致。cxChar 表示在当前设备环境中一个字符的平均宽度。当我们假定程序使用等宽字体所有文本字符的宽度一致时我们可以将 cxChar 右边的因子1 - iHorzPos视为客户区的某某号列字符位置值。在这种情况下x cxChar * (1 - 3) cxChar * (-2)这里计算出来的 x 值它会作为 245 行的 TextOut 中的 x 参数的实参它代表的是 TextOut 中文本字符串的起始字符的横坐标值。文本起始字符我们可以将其称作文本0号字符。而【cxChar * (-2)】我们可以将其称作客户区 -2 号字符位置。由此当 iHorzPos 3 时245 行的 TextOut 函数的文本 0 号字符显示在客户区 -2 号列字符的位置上。客户区的 -2 号列字符位置位于客户区左边缘的左边不在客户区范围内不予显示。当 iHorzPos 3 时245 行的 TextOut 函数的文本 1 号字符显示在客户区 -1 号列字符的位置上。客户区的 -1 号列字符位置位于客户区左边缘的左边不在客户区范围内不予显示。当 iHorzPos 3 时245 行的 TextOut 函数的文本 2 号字符显示在客户区 0 号列字符的位置上。客户区的 0 号列字符位置位于客户区范围内予以显示。当 iHorzPos 3 时245 行的 TextOut 函数的文本 3 号字符显示在客户区 1 号列字符的位置上。客户区的 1 号列字符位置位于客户区范围内予以显示。就这样地TextOut 中的每一个文本字符起始字符编号为 0每一个后续字符依次编号加 1它们挨个地计算其显示横坐标。如果位图客户区显示范围内就予以显示。如果不在客户区显示范围内则不予显示。我们在使用程序的过程中通过拖动滚动条滑块或者是单击滑块左右两边的滚动条区域或者单击向左向右三角号都可能改变 iHorzPos 的值导致水平重绘代码被执行。对于任何一个有效的 iHorzPos 值显示逻辑都差不多。都是说对 TextOut 中的文本字符予以编号从 起始字符 的 0 编号 开始后续字符依次加 1 编号然后挨个地计算其在客户区中显示的横坐标位于客户区范围内的就予以显示。不属于客户区范围的就不予显示。结束语关于水平滚动条的滚屏工作与垂直滚动条的滚屏工作个人认为水平滚动条的原理更简单一些。不过对你来讲也许垂直滚动条更简单。因人而异吧。对我来讲水平滚动条与垂直滚动条的讲解任务都不简单。好多的课节讲起来都会有一定的难度啊。想要当好一个讲师讲好课程也不容易啊。希望你能够学好滚动条的滚屏运作机制。专栏导航上一篇第4章[标签 Win32] SysMets3 程序讲解04垂直滚屏重绘回到目录下一篇第5章[标签 Win32] GDI 的结构与原理