第七章 鼠标(CHECKER1)

CHECKER1程序将客户区划分成25个矩形,构成一个5*5的数组。如果在其中一个矩形内单击鼠标,就用X形填充该矩形。再次单击,则X形消失。

     前面的一篇文章里面,我模仿那本经典的书写了一个windows程序,
对windows的程序设计有了一点点的感性认识,
但是对于程序的设计还是一知半解,因此需要继续自学,
接着看那本经典的书……

相信读者朋友们对OFFICE2000一定非常熟悉吧,它里面的东东可真不少,不管是活泼可爱的”大眼夹”,还是各种平面造型的Windows控件都很受广大用户喜欢。那么这次就让我们来模仿它做个十分常用的控件:”颜色组合框”。如果你现在正在做关于字处理类的软件时这个”东东”一定对你有用。程序编译运行后的界面效果如图一所示:

1、相关类

在Microsoft Windows
中,键盘和鼠标是两个标准的用户输入源,在一些交叠的操作中通常相互补充使用。当然,鼠标在今天的应用程序中比10年前使用得更为广泛。甚至在一些应用程序中,我们更习惯于使用鼠标,例如在游戏、画图程序、音乐程序,以及Web创览器等程序中就是这样。然而,我们可以不使用鼠标,但绝对不能从一般的PC中拆掉键盘。
  相对于个人计算机的其他组件,键盘有非常久远的历史,它起源于1874年的第一台Remington打字机。早期的计算机程序员用键盘在
Hollerith卡片上打孔,以后在哑终端上用键盘直接与大型主机通讯。PC上的键盘在某些方面进行了扩展,包括了功能键、光标定位键和(通常都带有的)单独的数字键盘,但它们的输入原理基本相同。

图片 1图片 2

     下面是我的一点总结吧,贴出来让各位大侠看看,希望大侠们不吝指教,
指出我认识中的不当之处,以帮助我在自学的过程中提高…….

图一、颜色选择组合框

  CStatusBar 
-父类CControlBar,封装了关于状态栏的操作,也包括状态栏的创建

键盘基础

  1 /*--------------------------------------------
  2 CHECKER1.C -- Mouse Hit-Test Demo Program No.1
  3               (c) Charles Petzold, 1998
  4 --------------------------------------------*/
  5 
  6 #include <Windows.h>
  7 
  8 #define DIVISION 5
  9 
 10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 11 
 12 int WINAPI WinMain( __in HINSTANCE hInstance
 13                     , __in_opt HINSTANCE hPrevInstance
 14                     , __in LPSTR lpCmdLine
 15                     , __in int nShowCmd )
 16 {
 17     static TCHAR szAppName[] = TEXT("Checker1");
 18     HWND hwnd;
 19     MSG msg;
 20     WNDCLASS wndclass;
 21 
 22     wndclass.style = CS_HREDRAW | CS_VREDRAW;
 23     wndclass.lpfnWndProc = WndProc;
 24     wndclass.cbClsExtra = 0;
 25     wndclass.cbWndExtra = 0;
 26     wndclass.hInstance = hInstance;
 27     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 28     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 29     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 30     wndclass.lpszMenuName = NULL;
 31     wndclass.lpszClassName = szAppName;
 32 
 33     if (!RegisterClass(&wndclass))
 34     {
 35         MessageBox(NULL, TEXT("Program requires Windows NT!")
 36             , szAppName, MB_ICONERROR);
 37         return 0;
 38     }
 39 
 40     hwnd = CreateWindow(szAppName, TEXT("Checker1 Mouse Hit-Test Demo")
 41         , WS_OVERLAPPEDWINDOW
 42         , CW_USEDEFAULT, CW_USEDEFAULT
 43         , CW_USEDEFAULT, CW_USEDEFAULT
 44         , NULL, NULL, hInstance, NULL);
 45 
 46     ShowWindow(hwnd, nShowCmd);
 47     UpdateWindow(hwnd);
 48 
 49     while (GetMessage(&msg, NULL, 0, 0))
 50     {
 51         TranslateMessage(&msg);
 52         DispatchMessage(&msg);
 53     }
 54 
 55     return msg.wParam;
 56 }
 57 
 58 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 59 {
 60     static BOOL fState[DIVISION][DIVISION];
 61     static int cxBlock, cyBlock;
 62     HDC hdc;
 63     int x, y;
 64     PAINTSTRUCT ps;
 65     RECT rect;
 66 
 67     switch (message)
 68     {
 69     case WM_SIZE:
 70         cxBlock = LOWORD(lParam) / DIVISION;
 71         cyBlock = HIWORD(lParam) / DIVISION;
 72         return 0;
 73 
 74     case WM_LBUTTONDOWN:
 75         x = LOWORD(lParam) / cxBlock;
 76         y = HIWORD(lParam) / cyBlock;
 77 
 78         if (x < DIVISION && y < DIVISION)
 79         {
 80             fState[x][y] ^= 1;
 81 
 82             rect.left = x * cxBlock;
 83             rect.top = y * cyBlock;
 84             rect.right = (x + 1) * cxBlock;
 85             rect.bottom = (y + 1) * cyBlock;
 86 
 87             InvalidateRect(hwnd, &rect, FALSE);
 88         }
 89         else
 90             MessageBeep(0);
 91         return 0;
 92 
 93     case WM_PAINT:
 94         hdc = BeginPaint(hwnd, &ps);
 95 
 96         for (x = 0; x < DIVISION; ++x)
 97             for (y = 0; y < DIVISION; ++y)
 98             {
 99                 Rectangle(hdc, x * cxBlock, y * cyBlock
100                     , (x + 1) * cxBlock, (y + 1) * cyBlock);
101 
102                 if (fState[x][y])
103                 {
104                     MoveToEx(hdc, x * cxBlock, y * cyBlock, NULL);
105                     LineTo(hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);
106                     MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);
107                     LineTo(hdc, (x + 1) * cxBlock, y * cyBlock);
108                 }
109             }
110 
111         EndPaint(hwnd, &ps);
112         return 0;
113 
114     case WM_DESTROY:
115         PostQuitMessage(0);
116         return 0;
117     }
118 
119     return DefWindowProc(hwnd, message, wParam, lParam);
120 }

     哈哈哈……….

  一、实现方法

2、状态栏的使用

  Windows程序获得键盘输入的方式:键盘输入以消息的形式传递给程序的窗口过程。实际上,第一次学习消息时,键盘就是一个明显的例子:消息应该传递给应用程序的信息类型。
  Windows用8种不同的消息来传递不同的键盘事件。这好像太多了,但是(就像我们所看到的一样)程序可以忽略其中至少一半的消息而不会有任何问题。并且,在大多数情况下,这些消息中包含的键盘信息会多于程序所需要的。处理键盘的部分工作就是识别出哪些消息是重要的,哪些是不重要的。
一、键盘基础知识
  虽然应用程序在很多情况下可以通过鼠标实现信息的输入,但到现在为止键盘仍然是PC机中不可替代的重要输入设备。
  用键盘当作输入设备,每当用户按下或释放某一个键时,会产生一个中断,该中断激活键盘驱动程序KEYBOARD.DRV来对键盘中断进行处理。
KEYBOARD.DRV程序会根据用户的不同操作进行编码,然后调用Windows用户模块USER.EXE生成键盘消息,并将该消息发送到消息队列中等候处理。

1.扫描码和虚拟码
  扫描码对应着键盘上的不同键,每一个键被按下或释放时,都会产生一个唯一的扫描码作为本身的标识。扫描码依赖于具体的硬件设备,即当相同的键被按下或释放时,在不同的机器上可能产生不同的扫描码。在程序中通常使用由Windows系统定义的与具体设备无关的虚拟码。在击键产生扫描码的同时,键盘驱动程序KEYBOARD.DRV截取键的扫描码,然后将其翻译成对应的虚拟码,再将扫描码和虚拟码一齐编码形成键盘消息。所以,最后发送到消息队列的键盘消息中,既包含了扫描码又包含了虚拟码。
  经常使用的虚拟码在WINDOWS.H文件中定义,常用虚拟码的数值、常量符号和含义如表所示。

CHECKER1.C

4.1   显示和更新
windows应用程序的显示区域:
      
除标题栏、应用程序边框、菜单栏、工具栏、滚动条和状态条外的系统分配给应用程序的窗口区域。
Windows显示文字和图形的方式:
       1、通过绘制应用程序的显示区域来实现
      
2、当应用程序的某些部分被覆盖后,windows不会保存被覆盖的区域,当程序移开后,windows通过发送消息通知
            应用程序需要重新绘制被覆盖的区域来实现显示
      
3、windows通过发送WM_PAINT消息来通知应用程序的窗口消息处理程序,需要重新绘制显示区域
4.2  WM_PAINT消息:
  WM_PAINT消息的产生时机:
      
1、在调用UpdateWindow函数时,windows会像应用程序发送一个WM_PAINT消息,通知应用程序需要重绘显示区域。
       2、在应用程序的使用者移动窗口时,窗口中先前被隐藏的区域重新可见
       3、使用者改变窗口的大小,同时wndclass.style 中设定了CS_VREDRAW
和CS_HREDRAW
       4、程序中调用ScrollWindow或ScrollDC函数滚动显示区域的一部分
       5、程序中使用InvalidateRect或InvalidateRgn函数产生WM_PAINT消息

  首先让我们先来了解一下画控件的基本原理和过程,也许这个才是本文的原意。大家都知道Windows中所有可视的东西都是画出来的,那么这个画画的内部过程又是怎样的呢?一般画Windows控件的过程分为三大部分:一是在WM_MEASUREITEM消息影射函数中设置当前要画的Item的大小尺寸;二是在WM_DRAWITEM消息影射函数中根据Item的大小尺寸来画该Item(图标/位图/字符串等);三是在WM_PAINT消息映射函数中不断的绘制当前的控件内容。

  2.1创建状态栏 CStatusBar::CreateEx / CStatusBar::Create

取值(16进制) 常量符号 含义
01 VK_LBUTTON 鼠标左键
02 VK_RBUTTON 鼠标右键
03 VK_CANCEL Break中断键
04 VK_MBUTTON 鼠标中键
05-07 — 未定义
08 VK_BACK (BackSpace)键
09 VK_TAB Tab键
0A-0B — 未定义
0C VK_CLEAR Clear键
0D VK_RETURN Enter键
0E-0F — 未定义
10 VK_SHIFT Shift键
11 VK_CONTROL Ctrl键
12 VK_MENU Alt键
13 VK_PAUSE Pause键
14 VK_CAPTIAL CapsLock键
15-19 — 汉字系统保留
1A — 未定义
1B VK_ESCAPE Esc键
1C-1F — 汉字系统保留
20 VK_SPACE 空格键
21 VK_PRIOR PageUp键
22 VK_NEXT PageDown键
23 VK_END End键
24 VK_HOME Home键
25 VK_LEFT ←(Left Arrow)键
26 VK_UP ↑(Up Arrow)键
27 VK_RIGHT →(Right Arrow)键
28 VK_DOWN ↓(Down Arrow)键
29 VK_SELECT Select键
2A — OEM保留
2B VK_EXECUTE Execute键
2C VK_SNAPSHOT Print Screen键
2D VK_INSERT Insert键
2E VK_DELETE Delete键
2F VK_HELP Help键
30-39 VK_0-VK_9 数字键0-9
3A-40 — 未定义
41-5A VK_A-VK_Z 字母键A-Z
5B-5F — 未定义
60-69 VK_NUMPAD0-VK_NUMPAD9 小键盘数字键0-9
6A VK_MULTIPLY *(乘号)键
6B VK_ADD +(加号)键
6C VK_SEPAPATOR 分隔符键
6E VK_SUBTRACT -(减号)键
6F VK_DECIMAL .(小数点)键
70-87 VK_DIVIDE /(除号)键
88-8F VK_F1-VK_F24 F1-F24功能键
90 VK_NUMBERLOCK Number lock键
91 VK_SCROLL Scroll lock键
92-B9 — 未定义
BA-C0 — OEM保留
C1-DA — 未定义
DB_E4 — OEM保留
E5 — 未定义
E6 — OEM保留
E7-E8 — 未定义
E9-F5 — OEM保留
F6-FE — 未定义

CHECKER1程序显示结果如图所示:

 可能产生WM_PAINT的时机:
       1、Windows擦除了覆盖应用程序的对话框或者消息框
       2、菜单下拉下拉后然后被释放
       3、显示ToolTip提示信息后
   以下情况Windows通过保存信息来恢复显示区域
       1、鼠标的光标穿越显示区域
       2、图标拖过显示区域
   处理过程:
     
应用程序保留重新绘制显示区域的所有信息,并且当接收到WM_PAINT消息时进行显示区域的重新绘制或者第一绘制(UpdateWindow时)
4.3 无效矩形和有效矩形
   无效矩形:
      
1、应用程序的显示区域中被覆盖的区域就是无效矩形区域。无效矩形也称作无效区域。
      
2、正是因为无效矩形的存在才使Windows发送WM_PAINT消息给应用程序,只有在显示区域的某一部分失效后应用程序窗口才会接收WM_PAINT消息
   绘图信息结构:
       1、 windows内部为每一个窗口保存一个绘图信息结构,
这个结构包含了包围无效区域的最小矩形的坐标以及其他信息
       2、如果在应用程序处理WM_PAINT消息之前,
显示区域的另一个区域变为无效,则windows会计算出一个包含两个区域的新的无效区域,并将这种变化信息保存在绘图信息结构
       3、windows不会将多个WM_PAINT消息放到消息对列
   InvalideateRect函数
      
1、窗口消息处理程序可以通过呼叫InvalidateRectangular使显示区域内的矩形无效
      
2、调用InvalidateRect函数时如果消息队列中已经存在WM_PAINT,即已经存在无效区域,windows将计算新的无效区域。如果消息队列无WM_PAINT则将
          一个WM_PAINT消息投递到消息队列。
      
3、在接收到WM_PAINT消息时,窗口处理程序/函数可以取得无效区域的坐标。
       4、在任何时刻可以通过呼叫GetUpdateRect取得无效区域的坐标
   Tip:
      
在处理WM_PAINT消息时,窗口消息处理程序在呼叫BeginPaint之后整个区域立即变为有效,
程序也可以通过呼叫ValidateRect函数使显示区域内的任意矩形都变为有效。
      
如果呼叫具有令整个显示区域变为有效的效果,则当前消息队列中的WM_PAINT都将被删除。
4、4 GDI
   GDI:
        要在窗口显示区域绘图,可以使用windows的图像设备接口:GDI
   文字绘制:
        1、DrawText函数
        2、TextOut函数
           原型: TextOut(HDC hdc, int x, int y,char *psText,int
iLength);
           hdc: 设备内容句柄
           x:显示文字开始的x坐标
           y: 显示文字开始的y坐标
           psText: 指向要显示的字符串的指针
           iLength: 待显示字符串的长度
   设备内容:
       1、设备内容句柄是GDI函数的通行证,通过设备内容句柄,
程序就可以在显示区域上绘图。
       2、设备内容 DC:
是GDI内部保存的数据结构,设备内容与特定的显示设备相关。对于系统桌面,
设备内容总是与显示器上的特定窗口相关
      
3、设备内容中有些值是图形属性,这些属性定义了GDI绘图函数的细节。
   通过设备内容绘图的过程:
      
1、程序需要绘图时,必须取得设备内容句柄,在取得该句柄后,windows系统用内定的属性值填入内部设备内容结构
       2、可以呼叫不同的GDI函数获取设备内容的属性值
       3、可以呼叫GDI函数改变设备内容的属性值
       4、利用其他GDI函数在显示区域绘图
       5、当程序在显示区域绘图完毕后,必须释放设备内容句柄,
被释放的句柄不能再被使用
       6、程序必须在处理单个消息处理期间取得和释放设备内容句柄。
       Tip:
        
除了呼叫CreateDC建立的设备内容外,程序不能在两个消息之间保存其他设备内容句柄。
   获取设备内容句柄:  
   方法一:处理WM_PAINT消息时使用
       1、BeginPaint和EndPaint函数
           
BeginPaint和EndPaint函数需要窗口句柄(在windows呼叫应用程序窗口处理函数时,作为参数传递给应用程序窗口处理函数)
          和PAINTSTRUCT结构的地址作为参数。
            BeginPaint函数返回一个与窗口相关联的设备内容句柄。
           其原型如下:
                        HDC  BeginPaint(HWND, PAINTSTRUCT *)
          
而EndPaint函数释放由BeginPaint函数取得的设备内容句柄,其原型如下:
                             EndPaint(HWND, PAINTSTURCT *)
       2、WM_PAINT消息处理过程
          PreStep:
                  HDC hdc;
                  PAINTSTRUCT  ps;
          step1:
首先呼叫BeginPaint函数取得一个与窗口相关联的设备内容句柄;
                  hdc=BeginPaint(hwnd,&ps);
          Step2:  使用GDI函数进行绘图;
          Step3: 释放由BeginPaint函数取得设备内容句柄:
                  EndPaint(hwnd,&ps);
          EndStep: return
0;//在每一个消息处理完后,均需要返回到windows系统,并且如果成功执行窗口回调函数则返回0.
       3、WM_PAINT消息处理结构:
            case WM_PAINT:
                     hdc=BeginPaint(hwnd,&ps);
                     call other GDI function
                     EndPaint(hwnd,&ps);
                     return 0;
       4、
如果在窗口消息处理函数中不处理WM_PAINT消息,则必须将WM_PAINT消息传递给DefWindowProc函数进行处理,DefWindowProc函
           数以下面的代码处理WM_PAINT消息:
            case WM_PAINT:
                 BeginPaint(hwnd,&ps);
                 EndPaint(hwnd,&ps);
                 return 0;
           说明以下DefWindowProc的原型:
                  DefWindowProc(HWND,
MSG,WPARAM,LPARAM);//与窗口消息处理函数基本一样的结构
       5、windows产生WM_PAINT消息是因为显示区域的某个区间变为无效,
如果不呼叫BeginPaint和EndPaint,或者ValidateRect,则windows
        
不会是无效区域变为有效,相反windows会发送另一个WM_PAINT消息,且一直发送过去。 
   绘图信息结构  PAINTSTRUCT:
       1、绘图信息结构定义:
              typedef struct tagPAINTSTRUCT
                {
                     HDC   hdc;   //设备内容句柄
                     BOOL  fErase;  //是否已经擦除无效矩形的背景
                     RECT  rcPaint;
                     BOOL  fRestore;
                     BOOL  fIncUpdate;
                     BYTE  rgbReserved[32];
                 }PAINTSTRUCT;
        2、windows 呼叫
             
在程序呼叫BeginPaint函数时,windows会将该结构的各个成员填充好,应用程序只使用前三个字段,其他由windows内部使用。
          
呼叫BeginPaint函数时填充ps.fErase字段来表示是否已经擦除无效区域的背景:
                  ps.fErase = FALSE (0)
则表示已经擦除无效区域背景;同时windows会使用wndclass.hbrBcakground指定的画刷来擦除背景。

    BOOL CreateEx(

2.输入焦点
  同一时刻,Windows中可能有多个不同的程序在运行,也就是说有多个窗口同时存在。这时,键盘由多个窗口共享,但只有一个窗口能够接收到键盘消息,这个能够接收键盘消息的窗口被称为拥有输入焦点的窗口。
  拥有输入焦点的窗口应该是当前的活动窗口,或者是活动窗口的子窗口,其标题和边框会以高亮度显示,以区别于其他窗口。拥有输入焦点的也可以是图标而不是窗口,此时,Windows也将消息发送给图标,只是消息的格式略有不同。
  窗口过程可以通过发送WM_SETFOCUS和
WM_KILLFOCUS消息使窗体获得或失去输入焦点。
程序也可以通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来判断窗体何时获得或失去输入焦点。其中WM_SETFOCUS消息表示窗口正获得输入焦点,WM_
KILLFOCUS消息表示窗口正失去输入焦点。
3.键盘消息
  键盘消息分为系统键消息和非系统键消息。系统键消息是指由Aft键和其他键组合而产生的按键消息。当系统键被按下时产生WM_
SYSKEYDOWN消息,当系统键被释放时产生WM_SYSKEYUP消息。

Aft键与其他键形成的组合键通常用于对程序菜单和系统菜单进行选择,或用于在不同的程序之间进行切换。因此,系统键消息应该交由Windows进行处理,用户所编制的程序一般不处理系统键消息,而是将这些消息交由DefWindowProc函数进行处理。如果用户想对系统键消息进行处理,应该在处理完这些消息后,再将其发送给DefWindowProc函数,使得Windows系统能够正常工作。
  某些击键消息可以被转换成字符消息,例如字母键、数字键等。有些键只能产生按键消息而没有字符消息,例如
Shift键、Insert键等。
消息循环中的
TranslateMessage函数可以实现从击键消息向字符消息的转化。当GetMessage函数捕获一个WM_SYSKEYDOWN消息或
WM_KEYDOWN消息后,TranslateMessage函数判断产生该消息的键是否能够被转换成字符消息,如果能,就将该消息转换成字符消息,再通过DispatchMessape函数将转换后的字符消息发送到消息队列中去。字符消息共有以下四种,如表所示。

图片 3

       
3、如果程序呼叫InvalidateRect使显示区域中的矩形失效,则InvalidateRect函数的最后一个参数会指定是否擦除背景。如果这个参数为FALSE
           则windows不会擦除背景,并且在呼叫完BeginPaint后将置
ps.fErase=TRUE 非零。
          
在处理WM_PAINT消息时,如果需要在无效区域外进行重绘,可以呼叫InvalidateRect函数:
            Exp:
                 InvalidateRect(hwnd,NULL,TRUE);
           
在BeginPaint函数之前呼叫InvalidateRect函数使整个显示区域变为无效,并擦除背景;但如果最后一个参数等于FALSE,则不会擦除背景,窗口
            中原有的信息还保留在原处。
        4、RECT rcPaint 是一个RECT型的结构体,
PAINTSTRUCT结构中的rcPaint成员定义了无效区域的边界。

下面我们针对CBSColorComboBox类的这几个过程来做个简单的介绍:

      CWnd* pParentWnd, 
//指定状态栏的父窗口指针,通常都是指向程序的框架类窗口对象

字符 系统字符 非系统字符
普通字符 WM_SYSCHAR WM_CHAR
死字符 WM_SYSDEADCHAR WM_DEADCHAR

所有25个矩形具有相同的宽度和高度,这些宽度和高度被保存在cxBlock、cyBlock中。无论何时改变客户区的大小,程序都将重新计算cxBlock、cyBlock。WM_LBUTTONDOWN处理逻辑利用鼠标的坐标来判断哪个矩形被单击,然后在fState数组中使用位运算符(按位异或^)计算那个矩形的新状态,最后强制使该矩形失效,从而产生WM_PAINT消息。

前面的内容简单的描述了利用BeginPaint和EndPaint函数来处理WM_PAINT消息的机制,理解不到位的地方请各位见谅。

  在WM_MEASUREITEM消息影射函数中设定Item的大小尺寸的时候,我们只需要设置Item的高度即可。这里的高度我们设置为2倍的系统小图标(SMALL
ICON)的高度,其尺寸用::GetSystemMetrics(SM_CXSMICON)取得。

      DWORD dwCtrlStyle = 0 , 
//设置内嵌在状态栏上的CStatusBarCtrl对象创建时的扩展风格

  其中死字符是由某些特殊键盘上的按键所造成的,Windows一般忽略死字符所产生的消息。
  Windows的消息一般是通过一个MSG结构体变量传送给消息处理函数的。对于键盘消息,
MSG结构体变量的各个域中较重要的是lParam域和
wParam域。wParam域用于保存按键的虚拟键代码或字符的ASCII码。对于非字符消息,wParam域保存按键的虚拟健代码;对于字符消息,
wParam域不保存字符的ASCII码。lParam域则用于保存击键时产生的附加信息,实际上一个32位的lParam变量被分为六部分,记录了以下相关信息:重复次数、OEM扫描码、扩展键标志、关联键标志、前一击键状态和转换状态。lParam域各位的含义如表所示。

如果客户区的宽度和高度不能被5整除,客户区的左边或底部就会出现一个小长条区域,不被举行覆盖。在这片区域进行鼠标单击时,CHECKER1程序会调用MessageBeep函数进行响应。

    上面的一些函数的原型没有指明返回值类型,
大家可以通过编译环境自己查看, 因为我有点懒,所以就没查看了。

  Visual
C++的程序开发人员可以在Item的矩形区域内画各种各样的信息,例如:图标/位图/字符串等等。那么有人会疑问:”我们用什么来画?我们在哪里画?又如何来画呢?”。答案其实都在这个LPDRAWITEMSTRUCT结构中。hDC成员为设备上下文环境(HDC),获得了该设备句柄也就意味着我们拥有了画任何位图/图标/文本的能力;那么接下来的问题就是:我们在哪里来画呢?答案也很简单:获得LPDRAWITEMSTRUCT结构中Item的矩形区域(rcItem),那么这就是你施展才华的空间了,要充分利用它哦!

      DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, 
//状态栏的风格

位数 含义
0-15 击键重复次数累加
16-23 OEM扫描码
24 是否为扩展键
25-28 未定义
29 是否便用关联键,及Alt键是否同时按下。
30 前一次击键状态,0表示该键前一次状态为抬起,1表示前一次状态为按下
31 转换状态

当CHECKER1程序收到WM_PAINT消息时,它会重新绘制整个客户区,利用GDI中的Rectangle函数画出所有的矩形。若fState值为真,则CHECKER1程序调用函数MoveToEx和函数LineTo函数画出两条直线。

    哈哈哈哈…………….原来windows下的程序设计有C的基础,加上学习一大堆windows定义各种API函数,
加上一些运行机制的理解,就算是菜鸟级的不入门的程序编程人员(就像我似的)也能实现一个简单的应用………………..

  最后一步就是如何来画的问题了,说白了就是如何分配每个元素的空间,如何在它们各自的空间上画出你想要的东西。按照常规一般分别计算出ICON所占的矩形区域/文本所占的矩形区域/位图的矩形区域,如果你还有其他元素那么也应该计算出该元素所占的矩形区域/位图所占的矩形区域。接下来的一切都很简单了,不外乎CDC类的几个常用函数:画图标用DrawIcon()、画位图用BitBlt()、画文字用DrawText()等函数。如果你觉得视觉上还不够COOL,你还可以来设置各个Item的文本颜色,背景颜色,以及图标的突起和凹陷的视觉效果。

      UINT nID = AFX_IDW_STATUS_BAR  
//指定状态栏这个子窗口的ID,默认为AFX_IDW_STATUS_BAR

  按键的次序不同,产生的消息也不相同。例如,按下并释放1键,读过程依次产生如表所示三条消息。按下1键所产生的消息和wParam的取值

    我发现用LiveWrite这个东西比较好…….

  不过在上述过程中需要注意三个问题,一是为了消除不断绘制所带来的闪烁现象,需要在WM_ERASEBKGND消息响应中作些特殊处理;在WM_PAINT消息中直接把组合框的客户区当成一幅位图来不断更新,而不是对ICON区域和文本区域分别重绘。二是每当用户改变了组合框的当前内容后,在画新的Item之前一定要记得清除前次组合框内的内容。三是如果想选择更多的颜色,那么只要选择组合框中的最后一个Item(More
Colors)即可,这个Item是为用户自定义颜色而专门设置的。

    );

消息 wParam变量取值
WM_KEYDOWN 虚拟码1
WM_CHAR ASCII码“1”
WM_KEYUP 虚拟码1

  二、编程步骤

    BOOL Create(

  如果按下Shift键后再按下1键并释放,则依次产生如表所示的消息。按下
Shift键后按 1健所产生的消息和 wParam的取值

  1、启动Visual
C++6.0,生成一个基于对话框的项目,将该项目命名为”WW”;

      CWnd* pParentWnd, 
//指定状态栏的父窗口指针,通常都是指向程序的框架类窗口对象

消息 wParam变量取值
WM_KEYDOWN 虚拟码 VK_SHIFT
WM_KEYDOWN 虚拟码 VK_1
WM_CHAR ASCII码 “1”
WM_KEYUP 虚拟码 VK_1
WM_KEYUP 虚拟码 VK_SHIFT

  2、使用Class
Wizard新建一个类CBSColorComboBox,其基类选择为CComboBox类;

      DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, 
//状态栏的风格

二、键盘应用实例
  下面通过一个应用程序实例来说明在实际编程中如何处理键盘消息。
#include <windows.h>
#include <stdio.h>
// 全局变量
RECT rc; //记录滚屏的矩形区域
?
int xChar, yChar; //文本输入点坐标

  3、在程序的对话框中放置一个ComboBox控件,使用CLASSWIZARD添加相应的CComboBox类成员变量,然后将该成员变量的类型修改为CBSColorComboBox;

      UINT nID = AFX_IDW_STATUS_BAR 
//指定状态栏这个子窗口的ID,默认为AFX_IDW_STATUS_BAR

WNDCLASSEX wnd; //窗口类结构变量

  4、添加代码,编译运行程序。

    );

char szAppName[] = “键盘消息监视程序”; //窗口类名
//函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance,int iCmdShow);
//函数:WinMain
//作用:入口函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
szCmdLine,int iCmdShow)
{
  MSG msg;
  if(!MyRegisterClass(hInstance))
  {
    return FALSE;
  }
  
   if(!InitInstance(hInstance,iCmdShow))
  {
    return FALSE;
  }
  
  while (GetMessage (&msg, NULL, 0, 0))
  {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }
  return msg.wParam;
}
//函数:ShowKey
//作用:实现在窗口中显示按键信息
void ShowKey (HWND hwnd, int iType,char *szMessage,WPARAM wParam,LPARAM
lParam)
{
  static char *szFormat[2] ={“%-14s %3d %c %6u %4d %5s %5s %6s
%6s”,
                ”%-14s %3d %c %6u %4d %5s %5s %6s %6s”
};
  char szBuffer[80];
  HDC hdc;
  ScrollWindowEx(hwnd, 0, -yChar, &rc,&rc,NULL,NULL,SW_INVALIDATE);
  hdc = GetDC (hwnd);
  SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT));
  TextOut (hdc,
       xChar,
       rc.bottom – yChar,
       szBuffer,
       wsprintf szBuffer,
       szFormat[iType],
       szMessage, //消息
       wParam, //虚拟键代码
       (BYTE) (iType ? wParam :‘ ’),//显示字符值
       LOWORD (lParam), // 重复次数
       HIWORD (lParam) & 0xFF, // OEM键盘扫描码
       //判断是否为增强键盘的扩展键
       (PSTR) (0x01000000 & lParam ? “是” : “否”),
       //判断是否同时使用了ALT键
       (PSTR) (0x20000000 & lParam ? “是” : “否”),
       (PSTR) (0x40000000 & lParam ? “按下” : “抬”),
       //判断前一次击键状
       (PSTR)(0x80000000 & lParam ? “按下” : “抬起”))
       //判断转换状态?
       );
  ReleaseDC (hwnd, hdc); ?
  ValidateRect (hwnd, NULL); ?
}
//函数:WndProc
//作用:处理主窗口的消息
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM
lParam)
{
  static char szTop[] =”消息键 字符 重复数 扫描码 扩展码 ALT
前一状态 转换状态”;
  static char szUnd[] =”_______ __ ____ _____
______ ______ ___ _______ ______”;

**三、程序代码

    在程序的CMainFrame类的OnCreate函数中可见MFC自动创建状态栏的方法:      

  //在窗口中输出文字作为信息标题
  HDC hdc;
  PAINTSTRUCT ps;
  TEXTMETRIC tm;

**

if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(
    indicators,sizeof(indicators)/sizeof(UINT)))
{
    TRACE0("Failed to create status barn");
    return -1;      // fail to create
}

  switch (iMsg)
  {
    case WM_CREATE://处理窗口创建的消息
    hdc = GetDC (hwnd); //设定字体
    SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT));
//检取当前字体的度量数据
    GetTextMetrics (hdc, &tm);
    xChar = tm.tmAveCharWidth;//保存字体平均宽度
    yChar = tm.tmHeight; //保存字体高度
    ReleaseDC (hwnd, hdc);
    rc.top = 3 * yChar / 2;
    return 0;

//////////////////////////////////////////////////////////////CBSColorComboBox类的头文件;
#if !defined(_BS_BSCOLORCB)
#define _BS_BSCOLORCB
#include <afxtempl.h>
//系统常用颜色的自定义名称
const static char* strColorName[] =
{
 "crSCROLLBAR","crBACKGROUND","crACTIVECAPTION", "crINACTIVECAPTION", "crMENU", "crWINDOW", "crWINDOWFRAME",  "crMENUTEXT", "crWINDOWTEXT", "crCAPTIONTEXT", "crACTIVEBORDER","crINACTIVEBORDER", "crAPPWORKSPACE",  "crHIGHLIGHT", "crHIGHLIGHTTEXT", "crBTNFACE", "crBTNSHADOW", "crGRAYTEXT", "crBTNTEXT",  "crINACTIVECAPTIONTEXT",
 "crBTNHIGHLIGHT","cr3DDKSHADOW", "cr3DLIGHT", "crINFOTEXT", "crINFOBK",
 "crHOTLIGHT","crGRADIENTACTIVECAPTION", crGRADIENTINACTIVECAPTION"
};

typedef struct BSCBITEM
{
 int iIndex;
 COLORREF crColor;
 LPCTSTR lpCaption;
}BSCBITEM, *LPBSCBITEM;

class CBSColorComboBox : public CComboBox
{
 DECLARE_DYNCREATE(CBSColorComboBox)
 public:
  CBSColorComboBox();
  virtual ~CBSColorComboBox();
  BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
  //初始化组合框(第一个被调用的函数)
  void InitBSColorCB(void);
  //得到当前的颜色值或R/G/B值
  COLORREF GetColor();
  void GetRGBValue(int* R, int* G, int* B);
 public:
  //{{AFX_VIRTUAL(CBSColorComboBox)
   public:
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
  //}}AFX_VIRTUAL
 protected:
  bool m_bOverControl; //鼠标的状态(是否处于按钮上)
  int iIconX, iIconY; //SMALL ICON的大小尺寸
  COLORREF m_crColor; //当前选中的颜色
  CList<LPBSCBITEM, LPBSCBITEM> m_crItem;

  void OnCBPaint(CDC* pDC);
  LPBSCBITEM GetItem(int iIndex = 0);
 protected:
  //{{AFX_MSG(CBSColorComboBox)
   afx_msg BOOL OnEraseBkgnd(CDC* pDC);
   afx_msg void OnPaint();
   afx_msg void OnTimer(UINT nIDEvent);
   afx_msg void OnMouseMove(UINT nFlags, CPoint point);
   afx_msg void OnSelchange();
   afx_msg void OnSelendok();
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
};
#endif // !defined(_BS_BSCOLORCB)

///////////////////////////////////////////////////////////////CBSColorComboBox的实现文件;
#include "stdafx.h"
#include "BSColorComboBox.h"
CBSColorComboBox::CBSColorComboBox()
{
 //当前鼠标是否在对象上
 m_bOverControl = false;
 //小图标尺寸
 iIconX = ::GetSystemMetrics(SM_CXSMICON);
 iIconY = ::GetSystemMetrics(SM_CYSMICON);
}

CBSColorComboBox::~CBSColorComboBox()
{
 while(!m_crItem.IsEmpty())
 {
  LPBSCBITEM lpItem = m_crItem.RemoveHead();
  delete lpItem;
 }
}

BOOL CBSColorComboBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
 DWORD dw = dwStyle;

 if( !CComboBox::Create(dw, rect, pParentWnd, nID) )
  return false;
 CFont * font = CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT));
 SetFont(font);

 return true;
}
IMPLEMENT_DYNCREATE(CBSColorComboBox, CComboBox)

BEGIN_MESSAGE_MAP(CBSColorComboBox, CComboBox)
//{{AFX_MSG_MAP(CBSColorComboBox)
 ON_WM_ERASEBKGND()
 ON_WM_PAINT()
 ON_WM_TIMER()
 ON_WM_MOUSEMOVE()
 ON_CONTROL_REFLECT(CBN_SELCHANGE, OnSelchange)
 ON_CONTROL_REFLECT(CBN_SELENDOK, OnSelendok)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CBSColorComboBox::InitBSColorCB(void)
{
 int iMinColor = COLOR_SCROLLBAR,
 iMaxColor = COLOR_BTNHIGHLIGHT;
 if(WINVER >= 0x0400)
  iMaxColor = COLOR_INFOBK;
 if(WINVER >= 0x0500)
  iMaxColor = 28;
 //初始化CB颜色列表框的Item(常见的SysColor值)
 for(int iLoop = iMinColor; iLoop <= iMaxColor; ++iLoop)
 {
  LPBSCBITEM lpItem = new BSCBITEM;
  lpItem->iIndex = AddString(strColorName[iLoop]);
  lpItem->crColor = ::GetSysColor(iLoop);
  lpItem->lpCaption = strColorName[iLoop];
  //
  if(m_crItem.IsEmpty())
   m_crItem.AddHead(lpItem);
  else
   m_crItem.AddTail(lpItem);
 }
 //该Item是为了用户自定义颜色而设置
 LPBSCBITEM lpItem = n

      SetIndicators函数的第一个参数indicators为一个静态数组,其定义在CMainFrame类的源文件中:

    case WM_SIZE://处理窗口大小改变的消息
    //窗体改变后保存新的滚屏区域右下角坐标
    rc.right = LOWORD (lParam);
    rc.bottom = HIWORD (lParam);
    UpdateWindow (hwnd);
    return 0;

        static UINT indicators[] =

    case WM_PAINT: //处理窗口重绘消息
    InvalidateRect (hwnd, NULL, TRUE);
    hdc = BeginPaint (hwnd, &ps);
    SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
    SetBkMode (hdc, TRANSPARENT) ;
    TextOut (hdc, xChar, yChar / 2, szTop, (sizeof szTop) – 1) ;
    TextOut (hdc, xChar, yChar / 2, szUnd, (sizeof szUnd) – 1) ;
    EndPaint (hwnd, &ps);
    return 0;

        {

    case WM_KEYDOWN:
    //处理键盘上某一键按下的消息
    ShowKey (hwnd, 0, “WM_KEYDOWN”,wParam, lParam);
    return 0;

              ID_SEPARATOR,       //提示行

    case WM_KEYUP:
    //处理键盘上某一按下键被释放的消息
    ShowKey (hwnd, 0, “WM_KEYUP”, wParam, lParam);
    return 0;

              ID_INDICATOR_CAPS,  //Caps Lock键的状态指示器

    case WM_CHAR:
    //处理击键过程中产生的非系统键的可见字符消息
    howKey (hwnd, 1, “WM_CHAR”, wParam, lParam);
    return 0;

              ID_INDICATOR_NUM,   //Num Lock键的状态指示器

    case WM_DEADCHAR:
    //处理击键过程中产生的非系统键”死字符”消息
    ShowKey (hwnd, 1, “WM_DEADCHAR”, wParam, lParam);
    return 0;

              ID_INDICATOR_SCRL,  //Scroll Lock键的状态指示器

    case WM_SYSKEYDOWN:
    //处理系统键按下的消息
    ShowKey (hwnd, 0, “WM_SYSKEYDOWN”,wParam, lParam);
    break;

        };

    case WM_SYSKEYUP:
    //处理系统键抬起的消息
    ShowKey (hwnd, 0, “WM_SYSKEYUP”, wParam, lParam);
    break;

        该数组中的后3个ID都是MFC预先定义好的字符串资源ID,在工程的资源窗口中的String
Table的字串表中可见:

    case WM_SYSCHAR://处理系统键可见字符消息
    ShowKey (hwnd, 1, “WM_SYSCHAR”, wParam, lParam);
    break;

          图片 4

    case WM_SYSDEADCHAR://处理系统键”死字符”消息
    ShowKey (hwnd, 1, “WM_SYSDEADCHAR”, wParam, lParam);
    break;

      若要修改状态栏的外观,如添加或减少状态栏上的窗格,只需要在indicators数组中添加或减少相应的字符串资源ID即可;

    case WM_DESTROY:
    //处理结束应用程序的消息
    PostQuitMessage (0);
    return 0;
  }
  return DefWindowProc (hwnd, iMsg, wParam, lParam);
}
//函数:MyRegisterClass
//作用:注册窗口类
BOOL MyRegisterClass(HINSTANCE hInstance)
{
  wnd.cbSize= sizeof (wnd);
  wnd.style = CS_HREDRAW | CS_VREDRAW;
  wnd.lpfnWndProc = WndProc;
  wnd.cbClsExtra = 0;
  wnd.cbWndExtra = 0;
  wnd.hInstance = hInstance;
  wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION);?
  wnd.hCursor = LoadCursor (NULL, IDC_ARROW);
  wnd.hbrBackground = (HBRUSH)
  GetStockObject (WHITE_BRUSH);
  wnd.lpszMenuName = NULL;
  wnd.lpszClassName = szAppName;
  wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  return RegisterClassEx (&wnd);
}
//函数:InitInstance
//作用:创建主窗口
BOOL InitInstance(HINSTANCE hInstance,int iCmdShow)
{
  HWND hwnd;
  hwnd = CreateWindow (szAppName,
             “键盘消息监视程序”,
             WS_OVERLAPPEDWINDOW,
             CW_USEDEFAULT,CW_USEDEFAULT,
             CW_USEDEFAULT,CW_USEDEFAULT,
             NULL,NULL,hInstance,NULL
             );
  if(!hwnd)
  {
    return FALSE;
  }

  2.2设置状态栏指示器(窗格)

  ShowWindow (hwnd, iCmdShow);
  UpdateWindow (hwnd);
  return TRUE;
}

    CStatusBar::SetIndicators

  本实例的作用是通过程序捕获键盘消息,然后将wParam参数所包含的数据进行分解,最后将各项信息通过窗口显示出来。实例的源文件包含了
Initlnstance、MyRegisterClass、ShowKey、WinMain和WndProc五个函数。程序的基本思路是以
WinMain函数作为程序入口,再调用 MyRegisterClass函数和
InitInstance函数注册窗口类并创建和保存窗日,然后创建和显示窗口,最后进入消息循环。
  下面重点分析函数WndProc和 ShowKey。
1.WndProc函数
在本实例中WndProc函数处理的消息主要有WM_CREATE、WM_SIZE、WM_PAINT和键盘消息。
  case WM_CREATE://处理窗口创建的消息
  hdc = GetDC (hwnd);//设定字体
  SelectObject (hdc, GetStockObject
(SYSTEM_FIXED_FONT));//检取当前字体的度量数据
  GetTextMetrics (hdc, &tm);
  xChar = tm.tmAveCharWidth;//保存字体平均宽度
  yChar = tm.tmHeight;//保存字体高度
  ReleaseDC (hwnd, hdc);
  rc.top = 3 * yChar / 2;
  return 0;
  这一程序段的主要作用是将字体对象选入当前窗体的设备描述表中,同时取得字体高度和平均宽度,再初始化编辑区的滚屏区域的右上角Y坐标。进入该程序段后,首先通过GetDC函数获得当前窗体的设备描述表,再通过GetStockObject函数获得系统字体,然后用
SelectObject函数将字体对家选入窗体的设备描述表中。其中,hdc为设备描述表句柄。在完成所有操作后,程序还必须通过ReleaseDC函数释放设备描述表。在该程序段中使用了GetTextMetrics函数来获得字体的几何尺寸。GetTextMetrics函效的原型定义如下:
BOOL GetTextMetrics(HDC hdc,// 指向设备描述表的句柄
          LPTEXTMETRIC lptm // TEXTMETRIC结构体变量的指针
          // 所获得的所有信息保存在TEXTMETRIC结构体变量中
          );
  其中lptm是一个指向
TEXTMETRIC结构体的指针。TEXTMETRIC结构体包含了与字体的几何尺寸相关的基本信息。该结构体的具体定义如下:
typedef struct tagTEXTMETRIC
{ // tm
  LONG tmHeight;// 字体高度
  LONG tmAscent;//字体高于基准线的高度
  LONG tmDescent;// 字体低于基准线的高度
  LONG tmInternalLeading;// 给大写字母留出的空间
  LONG tmExtenalLeading; // 由字体设计者推荐的附加行距
  LONG tmAveCharWidth;// 字体平均宽度
  LONG tmMaxCharWidth;// 字体最大宽度
  LONG tmWeight; // 字体黑度
  LONG tmOverhang; // 在合成斜体或黑体时加在字符上的附加宽度值
  LONG tmDigitizedAspectX;// 字体所适合的高宽比的宽
  LONG tmDigitizedAspectY; // 字体所适合的高宽比的高
  BCHAR tmFirstChar; // 字体中定义的第一个字符
  BCHAR tmLastChar; //字体中定义的最后一个字符
  BCHAR trnDefaultChar; //字体中的默认字符
  BCHAR trnBreakChar; // windows在调整文本时用于分裂词的字符
  BYTE tmItalic; // 取非零值时表示斜体字体
  BYTE tmUnderLined; // 取非零值时表示下划线字体
  BYTE tmStruckOut;// 取非零值时为删除线字体
  BYTE tmPitchAndFamily; // 低二位为字符间距,高四位为系列值
  BYTE tmCharSet; // 指定字符集
} TEXTMETRIC;
  该结构中所有的字体大小都是按逻辑单位给出的,这就是说字体的大小取决于当前显示设备的映射模式。
  在例中,所获得的字体几何尺寸保存在TEXTMETRIC结构体变量tm中。滚屏区域的范围是通过RECT结构体变量re保存的,RECT结构体变量可以通过记录矩形区域的右上角和左下角的坐标来确定一个矩形区域。
RECT结构的原型定义如下:
typedef struc RECT{
  LONG left; // 矩形左上角 X坐标
  LONG top; // 左上角 Y坐标
  LONG right; // 右下角 X坐标
  LONG bottom; // 右下角Y坐标
} RECT;
  该结构定义了一个矩形区域的左上角和右下角的坐标。由结构的原型定义我们可以知道该结构包括四个域,其中left域表示矩形的左上角X坐标,top域表示左上角Y坐标,right域表示右下角X坐标,bottom域表示右下角Y坐标。通常用于一个矩形区域范围的记录和传递。
  例如,通过RECT结构的变量将一个矩形区域范围的四个角的值传递FillRect函数,则调用该函数后,矩形区域除了最下方的一行和最右方一列外都被填充。在本实例中,初始化编辑区的滚屏区域的左上角Y坐标时,使用了如下程序:
  rc.top= 3 * yChar/2;
  这是因为在窗口中首先要输出两行的题头信息,一行为中文,一行为下划线。中文字符的高度为1个字体高度单位,而下划线的高度为半个字体高度单位。这两行信息是一直保持,不参与滚屏的。因此,滚屏区域的左上角Y坐标从3/2个字体高度处开始。
在WndProc函数中,处理WM_ SIZE
消息的程序段如下:
  case WM_SIZE: //处理窗口大小改变的消息
  //窗体改变后保存新的滚屏区域右下角坐标
  rc.right = LOWORD (lParam);
  rc.bottom = HIWORD (lParam);
  UpdateWindow (hwnd);
  return 0;
  该程序段比较简单,只是当窗口的尺寸改变时重新设定滚屏区域的右下角坐标,并更新窗口。值得注意的是,
WM_SIZE消息的wParam变量保存了窗体新尺寸的左上角坐标,变量的32位分为两个部分,低16位保存X坐标,高16位保存Y坐标。
lParam变量保存了窗体新尺寸的右下角坐标,保存方式与wParam变量相同。在编程过程中,通常通过LOWORD宏定义来获得32位变量的低16位数值,通过HIWORD宏定义来获得32位变量的高历位数值。
  该程序段比较简单,只是当窗口的尺寸改变时重新设定滚屏区域的右下角坐标,并更新窗口。值得注意的是,WM_SIZE消息的wParam变量保存了窗体新尺寸的左上角坐标,变量的32位分为两个部分,低16位保存X坐标,高16位保存Y坐标。
lParam变量保存了窗体新尺寸的右下角坐标,保存方式与wParam变量相同。在编程过程中,通常通过LOWORD宏定义来获得32位变量的低16位数值,通过HIWORD宏定义来获得32位变量的高历位数值。
WndProc函数中,处理WM_PAINT消息的程序段如下:
  case WM_PAINT: //处理窗口重绘消息 ?
  InvalidateRect (hwnd, NULL, TRUE); ?
  hdc = BeginPaint (hwnd, &ps); ?
  SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; ?
  SetBkMode (hdc, TRANSPARENT) ; ?
  TextOut (hdc, xChar, yChar / 2, szTop, (sizeof szTop) – 1) ; ?
  TextOut (hdc, xChar, yChar / 2, szUnd, (sizeof szUnd) – 1) ; ?
  EndPaint (hwnd, &ps); ?
  return 0;
  该程序段首先调用InvalidateRect函数使窗口无效,InvalidateRect函数的功能是使窗口的某一部分无效,也就是通知Windows该部分需要被刷新和重画。
  在InvalidateRect函数之后,程序调用函数BeginPaint准备重画窗口。
BeginPaint 函数的原型定义如下:
HDC BeginPaint (HWND hwnd , // 重画窗口的句柄
        LPPAINTSTRUCT lpPaint // 指向一个用于保存所有重
        // 画信息的 PAINTSTRUCT 结构体变量的指针);
  BeginPaint 函数的作用是完成重画窗体之前的准备,并将重画窗体
的数据保存在一个 PAINTSTRUCT 结构体变量中。 PAINTSTRUCT
结构体可以用于保存窗口重画时的数据以方便以后使用。
PAINTSTRUCT结构体的定义如下:
typedef struct tagPAINTSTRUCT{ // ps
  HDC hdc; // 重画区域所在窗口的句柄
  BOOL fErase;// 是否擦去背景
  RECT rcPaint; // 指定重画窗体的范围
  BOOL fRestore; // 系统保留域
  BOOL fIncUpdate;// 系统保留域
  BYTE rgbReserved[32];// 系统保留
}PA INTSTRU CT;
  BeginPaint函数如果操作成功会返回一个被操作窗口的设备描述表的句柄。如果操作不成功则函数返回NULL值,表明显示设备不可用。该函数在运行过程中,会进行自动调整,使得所有区域都包含在刷新区域的范围内。而原有需要刷新的区域是由InvalidateRect函数或
InvalidateRgn函数指定的。一般来说,只有当程序处理
WM_PAINT消息时才调用BeginPaint函数,而且,每次调用BeginPaint函数都需要对应调用一个EndPaint函数来结束重画过程。在BeginPaint函数调用后,会将插入符光标自动隐藏。EndPaint函数原型定义如下:
BOOL EndPaint ( HWND hWnd, // 窗口句柄
        CONST PAINTSTRUCT* lpPaint // 指向
PAINTSTRUCT结构体变量的指针
       );
  EndPaint函数标志着窗口重画过程的结束。该函数执行后总返回一个非零值。如果在BeginPaint函数执行时将插入符号隐藏了,那么EndPaint函数会重新显示插入符号。
消息处理函数 WndProc处理的键盘消息有:
WM_ KEYDOWN、WM_KEYUP
WM_CHAR、WM_DEADCHAR、
WM_SYSKEYDOWN、WM_SYSKEYUP、
WM_SYSCHAR 和 WM_SYSDEADCHAR。
  根据不同的消息,程序会用不同的参数调用
ShowKey函数在窗口中显示各键盘消息的相关信息。
2.ShowKey函数
ShowKey函数是用户自定义函数,其作用是从键盘消息的各域中提取信息并显示在窗口中。
ShowKey函数的具体定义如下:
// 作用:实现在窗口中显示按键信息
void ShowKey (HWND hwnd, int iType, char *szMessage,WPARAM wParam,
LPARAM lParam)
{
  static char *szFormat[2] = {“%-14s %3d %c %6u %4d %5s %5s %6s
%6s”,
                ”%-14s %3d %c %6u %4d %5s %5s %6s %6s” }
;
  char szBuffer[80];
  HDC hdc;
  SelectObject( hdc,
         GetStockObject(SYSTEM_FIXED_FONT));
         TextOut (hdc, xChar, rc.bottom – yChar,
         szBuffer,wsprintf (szBuffer, szFormat [iType],
         szMessage, //消息
         wParam, //虚拟键代码
         (BYTE) (iType ? wParam : ‘ ‘),//显示字符值
         LOWORD (lParam), //重复次数
         HIWORD (lParam) & 0xFF, //OEM键盘扫描码
         //判断是否为增强键盘的扩展键
         (PSTR) (0x01000000 & lParam ? “是” : “否”),
         //判断是否同时使用了ALT键
         (PSTR) (0x20000000 & lParam ? “是” : “否”),
         (PSTR) (0x40000000 & lParam ? “按下” : “抬起”),
         //判断前一次击键状态
         (PSTR) (0x80000000 & lParam ? “按下” : “抬起”))
         //判断转换状态
         );
}

  2.3设置指示器(窗格)的宽度和风格

  ShowKey函数首先定义了szFormat字符串,并在其中针对字符消息和非字符消息定义了两种不同的输出格式。然后调用ScrollWindowEx函数使显示区域滚屏,为信息输出作准备。ScrollWindowEx函数的主要功能是使窗口编辑区中的某一矩形区域产生滚屏效果。
ScrollWindowEx函数的原型定义如下:
int ScrollWindowEx (HWND hwnd, // 发生滚屏的窗口的句柄
          int dx, // 水平滚屏的数值
          int dy, // 垂直滚屏的数值
          CONST
RECT*prcScroll,//记录发生滚屏的矩形区域的RECT结构体的地址
          CONST RECT* prcClip, //记录发生剪切的矩形区域的
RECT结构体的地址
          HRGN hrgnUpdate,// 需要更新区域的句柄
          LPRECT prcUpdate, // 记录需要更新矩形区域的
RECT结构体的地址
          UINT flags // 滚屏控制标志
          );
  其中,dx参数给出了以设备单位尺寸(对于显示器为像素)为单位的每一次水平滚屏的度量值。dx参数取正值表示向右滚屏,取负值表示向左滚屏。如参数给出了以设备单位尺寸(对于显示器为像素)为单位的每一次垂直滚屏的度量值。如参数取正值表示向下滚屏,取负值表示向上滚屏。dx和dy两个参数不能同时取非零值,也就是说,ScrollWindowEx函数不能使编辑区同时向水平和垂直方向滚屏。
  prcScroll参数为一个指向记录滚屏的矩形区域的RECT结构体变量的指针,如果取值为NULL,则整个编辑区发生滚屏。
  hrgnUpdate参数为因滚屏而变得无效的矩形区域的句柄,多数情况下可以取NULL。
prcUpdate参数指向一个记录因为滚屏而变得无效的矩形区域的
RECT结构体变量。多数情况下取NULL。
flags变量可以通过不同的取值来控制滚屏的状况,其取值和意义如下所示。
  SW_ ERASE当和 SW_INVALIDATE值同时使用时,会通过向
window发送一个WM_ ERASEBKGND消息将最近变得无效的区域抹去;
  SW_INVALIDATE在发生滚屏后使由hrgnUpdate参数指定的区域无效;
  SW_SCROLLCHILDREN使所有的子窗口都发生滚屏;
  SW_ SMOOTHSCROLL在 Windows
95及以后的版本中使窗口发生平滑滚屏。如果ScrollWindowEx函数执行成功,则返回值为以下三者之一:
  SIMPLEREGION表示有一个矩形的无效区域;
  COMPLEXREGION表示没有无效区域和重叠区域;
  NULLREGION表示没有无效区域。
  如果ScrollWindowEx函数执行不成功,则返回ERROR。
ScrollWindowEx函数的功能也可以通过ScrollWindow函数来实现,ScrollWindow
函数的原型定义如下:
BOOL Scrollwindow(HWND hwnd //窗口句柄
         int XAmount, // 水平滚屏的数值
         int YAmount, // 垂直滚屏的数值
         CONST RECT* lpReCt, //记录发生滚屏的矩形区域的
RECT结构体的地址
         CONST RECT* lpClipRect, //记录发生剪切的矩形区域的
RECT结构体的地址
         );
  可以看出,ScrollWindow函数与ScrollWindowEx函数十分相似,其参数的意义也基本相同。事实上,ScrollWindow函数是为了保持对较低版本的Windows兼容而设计的,用户在编程时,除非需要考虑程序的向下兼容,否则一般都应使用ScrollWindowEx函数。
  在滚屏后,函数开始调用TextOut函数进行信息输出。TextOut函数的原型定义如下:
BOOL TextOut( HDC hdc,// 设备描述表句柄
       int nXStart, // 文本输出起始点 X坐标
       int nYStart, // 文本输出起始点 Y坐标
       LPCTSTR lpString, // 指向输出字符串的指针
       int cbString // 字符串中字符的数目
       );
  TextOut函数能够用当前设定的字体在窗口的指定部位输出一段文本信息。如果操作成功则返回一非零值,否则返回零值。捕获键盘消息的信息主要根据表中的描述,通过使用按位操作确定某些特定位的值,然后再判断具体的状态。
  在TextOut函数调用过程中,还调用了wsprintf函数,并使其返回值作为TextOut函数的一个参数值。wsprintf函数的原型定义如下:
int wsprintf (LPTSTR lpOut,// 指向需要输出的字符串的指针
       LPCTSTR lpFmt, //指向格式控制字符串的指针
       …… // 其他可选参数
       );
  wsprintf函数能够将一组字符序列按lpFmt参数指定的格式转换,然后保存在lpOut参数指定的字符缓冲区中等待输出。其中,字符序列由可选参数决定,而可选参数的数目和具体内容应该与lpFmt所指定的格式一致。
  如果wsprintf函数操作成功,则返回输出字符的数目,但这个字符数目不包括表示结束的NULL标志。如果操作失败,返回的整数值将与输出的字符数目不相符。
  实例主要说明了如何处理键盘消息,读者应该着重理解各种信息在MSG结构体变量中是如何保存的,怎样才能够对其中的具体信息进行识别和提取。程序运行后将产生一个背景色为灰色的简单窗口,并在窗口的顶部出现标题提示信息。这时用户如果进行键盘操作,则窗体中便会显示该操作所产生的键盘消息,每显示一条消息程序都会滚屏和重绘窗口,滚屏区域的颜色为白色。

    CStatusBar::SetPaneInfo

键盘消息实例2:
#include <windows.h>
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);
LRESULT CALLBACK WndProc( HWND,UINT, WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
  wcex.lpfnWndProc = (WNDPROC)WndProc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = hInstance;
  wcex.hIcon= LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);
  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  wcex.lpszMenuName = NULL;
  wcex.lpszClassName = “SeeKeyMessage”;
  wcex.hIconSm = LoadIcon(NULL,(LPCTSTR)IDI_APPLICATION);

    void SetPaneInfo(

  if(!RegisterClassEx(&wcex)) return FALSE;

      int nIndex,  //指定要设置其样式的窗格的位置索引

  int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
  int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
  HWND hWnd;
  hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
             ”SeeKeyMessage”,
             ”Trace Key Operation”,
             WS_OVERLAPPEDWINDOW,
             0, 0, SW_XFS, SW_YFS-25,
             NULL,
             NULL,
             hInstance,
             NULL);
  if(!hWnd) return FALSE;

      UINT nID,    //为指定窗格重新设置的新ID

  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  MSG msg;

      UINT nStyle,
//窗格的样式:SBPS_NORMAL/SBPS_STRETCH/SBPS_DISABLED/SBPS_POPOUT/SBPS_NOBORDERS

  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
  HDC hDC;
  PAINTSTRUCT ps;
  static char Buffer[256];
  switch(message)
  {
    case WM_KEYDOWN:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,40,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_KEYDOWN %3d %3d%3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,40,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

      int cxWidth  //指定窗格的宽度

    case WM_KEYUP:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,60,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_KEYUP %3d %3d
%3d”,wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,60,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

    );

    case WM_PAINT:
    hDC = BeginPaint(hWnd,&ps);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,20,Buffer,strlen(Buffer));
    wsprintf(Buffer,” Message wParam LOWORD(lParam)
HIWORD(lParam)”);
    TextOut(hDC,20,20,Buffer,strlen(Buffer));
    EndPaint(hWnd,&ps);
    break;

  2.4设置指示器(窗格)的文本内容

    case WM_DESTROY:
    PostQuitMessage(0);
    break;

    CStatusBar::SetPaneText

    default:
    return DefWindowProc(hWnd,message,wParam,lParam);
  }
  return 0;
}

    BOOL SetPaneText(

鼠标消息

      int nIndex, 
//当前窗格在指示器数组(indicators)中的位置索引

  随着 Windows
操作系统的流行,鼠标因为其精确定位和操作方便的优点而成为计算机不可缺少的输入设备。
一、鼠标的基础知识
  本节将介绍在程序中用鼠标作为输入设备的方法和技巧。 1
.鼠标操作和鼠标消息用户在使用鼠标操作的过程中,经常会使用的主要方式有五种
,如表所示。

      LPCTSTR lpszNewText,  //要在当前窗格中显示的文本

操作名称 描述
单击(Click) 按下并迅速释放鼠标按钮。
双击(Double Click) 连续快速完成两次单击操作。
移动(Move) 鼠标光标移动。
拖动(Drag) 按下鼠标一键不放,同时执行鼠标移动操作。
与键盘的特殊键组合 在按下Ctrl键或Shift键的同时执行鼠标单击操作。

      BOOL bUpdate = TRUE

  其中,前三种操作是最为基本的操作,可以产生Windows内部定义的消息,并通过这些消息来判断用户具体执行了哪种操作。
  Windows定义的鼠标消息共有20条,其中非编辑区的鼠标消息一般交由系统处理,程序只处理编辑区内的鼠标消息。编辑区内的鼠标消息共有10条,如表所示。

    );

消息常量 操作描述
WM_MOUSEMOVE 移动鼠标
WM_LVBUTTONDOWN 按下鼠标左键
WM_LBUTTONUP 释放鼠标左键
WM_LBUTTONDBLCLK 双击鼠标左键
WM_RVBUTTONDBLCLK 按下鼠标右键
WM_RBUTTONUP 释放鼠标右键
WM_RBUTTONDBLCLK 双击鼠标右键
WM_MVBUTTONDOWM 按下鼠标中键
WM_MBUTTONUP 释放鼠标中键
WM_MBUTTONDBLCLK 双击鼠标中键

    若不知道当前窗格在指示器数组(indicators)中的位置索引,可利用CStatusBar::CommandToIndex

  对于前表所列的鼠标操作中的最后两种,不能直接使用Windows定义的消息来判断,只能通过编程,将多种消息和数据组合之后判断。例如,判断用户是否按下鼠标左键之后进行拖动操作可以通过以下程序段来实现,用case语句来实现:
case WM_MOUSEMOVE:

    int CommandToIndex( UINT nIDFind ); 
//根据指定的资源ID获得相应的位置索引

if (wParam&MK_LBUTTON) //只处理鼠标拖动的消息
{ …… // 处理程序
}

 

  在处理鼠标消息的过程中,消息的wParam参数和lParam参数起了重要的作用。wParam参数中保存了在消息产生时其他操作进行的状态;用户可以通过位屏蔽操作来判断在该消息产生的同时,其余操作是否正在进行。这正是在程序中判断复杂鼠标操作的基本方法。例如,上面判断拖动操作的程序段就用了位操作
wParam& MK_LBUTTON,
判断在鼠标移动(WM_MOUSEMOVE)的同时鼠标左键是否同时被接下。如果,鼠标左键同时按下,则位操作的结果为TRUE,说明当前操作为拖动操作,程序可以继续进行下一步处理。又如需要判断单击鼠标左键时是否同时按下了Ctrl键或Shift键,可以用以下程序段来处理:
case WM_ LBUTTONDOWN:
if(wParam& MK_CTROL)
{//Ctrl键同时按下
  if (wParam&MK_ SHIFT)
  {// Ctrl 键和Shift键都同时按下
    …… // 处理程序
  }
  else { // Ctrl健同时按下,但 Shift键没有被按下
    ……. // 处理程序
  }
}
else if(wParam&MK_ SHIFT)
{ // Shift键同时按下,但 Ctrl键没有被接下
  …… // 处理程序
}
else
{// Shift 键和Ctrl键都未按下
  …… // 处理程序
}
  lParam参数保存了消息产生时鼠标所在点的坐标,其中低16位为X坐标,高16位为Y坐标。
  在处理鼠标消息的时候,如果需要处理鼠标双击消息,则在注册窗口类时,窗口的风格必须包括CS_DBCLCKS。否则即使执行了双击操作,窗口也只能收到两条WM_
BUTTONUP和
WM_BUTTONDOWN消息。
区分双击操作和两次单击操作是以两次击键的时间间隔为标准的。当两次击键的时间间隔小于
500毫秒时,
Windows将其视为双击操作:如果两次击键的时间间隔大于500毫秒,Windows将其视为两次单击操作。500毫秒为默认的时间间隔,用户可以通过调用SetDoubleClickTime函数来修改这一时间间隔。SetDoubleClickTime函数的原型定义如下:
BOOL SetDoubleClickTime(UINT uInterval // 新的击键时间间隔)
2.鼠标捕捉
  在通常情况下,只有当鼠标位于窗体内时,窗体才能接收到鼠标的消息。如果需要接收所有的鼠标消息而不论鼠标是否在窗口内,这时可以调用SetCapture函数来实现。SetCapture函数的原型定义如下:
HWND SetCapture (
  HWND hwnd // 窗口句柄
);
  调用SetCapture函数后,所有鼠标操作所产生的消息都直接发送到指定窗口。因为此时鼠标可能位于窗口之外,所以鼠标的坐标可能为负值。由于调用该函数会使其他窗口不能接收到键盘和鼠标的消息,因此在完成操作后应及时调用ReleaseCapture
函数释放鼠标捕获。ReleaseCapture函数的原型定义如下:
BOOL ReleaseCapture(VOID);
二、鼠标应用实例
下面是一个在程序设计中如何捕获鼠标消息的实例。
#include <windows.h>
//全局变量
WNDCLASSEX wnd;
static char szAppName[] = “mouse”;//窗口类名
//函数声明
long WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);
BOOL MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
//函数:WinMain
//作用:入口函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR
szCmdLine,int iCmdShow) ?
{
  MSG msg;
  if(!MyRegisterClass(hInstance))
  {
    return FALSE;
  }

本例将在状态栏上显示当前系统的时间和一个进度条控件:

  if(!InitInstance(hInstance,iCmdShow))
  {
    return FALSE;
  }

  1、显示系统时间

  while (GetMessage (&msg, NULL, 0, 0))
  {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }
  return msg.wParam;
}
//函数:WndProc
//作用:处理主窗口的消息
long WINAPI WndProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
  static POINT points[256];//保存点坐标
  static POINT center;//保存中心点坐标
  static int iCount;//点数目累加值
  HDC hdc;
  PAINTSTRUCT ps;
  int i;//循环计数
  RECT rect;
  switch (msg)
  {
    case WM_MBUTTONDOWN:
    //处理鼠标中键按下的消息
    iCount = 0;//重新初始化点数目
    InvalidateRect (hwnd, NULL, TRUE);
    //通知系统重画窗口
    hdc = BeginPaint (hwnd,&ps);
    GetClientRect(hwnd,&rect);
    if(wParam&MK_CONTROL)//判断Shift键和Ctrl键是否被按下
    {
      if(wParam&MK_SHIFT)
      { //根据不同的情况给出不同的提示
        DrawText(hdc,”Ctrland Shift”,
-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER?);
      }
      else
      {
        DrawText(hdc,”Ctrl Only” ,-1,&rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
      }
    }
    else if(wParam&MK_SHIFT)
    {
      DrawText(hdc,”Shift
Only”,-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
    }
    else
    {
      DrawText(hdc,
           “Middle Button of mouse only”,
           -1,
           &rect,
           DT_SINGLELINE|DT_CENTER|DT_VCENTER);
    }
    EndPaint(hWnd,&ps);
    return 0;

  在程序资源窗口中的字串表中增加两个新的字符串资源:

    case WM_RBUTTONDOWN:
    //处理鼠标右键按下的消息
    iCount = 0;//重新初始化点数目
    center.x=LOWORD (lParam);
    //保存新的中心点坐标
    center.y=HIWORD (lParam);
    InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口
    return 0;

  图片 5

    case WM_MOUSEMOVE://处理鼠标移动的消息
    if (wParam & MK_LBUTTON && iCount <
256)//只处理鼠标拖动的消息
    {
      points[iCount].x = LOWORD (lParam);//保存点的X坐标
      points[iCount++].y = HIWORD (lParam);//保存点的Y坐标
      hdc = GetDC (hwnd);//获得窗口的设备描述表句柄
      SetPixel (hdc, LOWORD (lParam), HIWORD (lParam),
0L);//绘点
      ReleaseDC (hwnd, hdc);//释放设备描述表句柄
    }?return 0;

  将这两个新的字符串资源ID添加到CMainFrame类的源文件中的indicators数组中:  

    case WM_LBUTTONUP:
    //处理鼠标左键抬起的消息
    InvalidateRect (hwnd, NULL, FALSE);
    //通知系统重画窗口
    return 0;

static UINT indicators[] =
{
    ID_SEPARATOR,           

    IDS_TIMER,
    IDS_PROGRESS,

    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
};

    case WM_PAINT://处理窗口重画的消息
    hdc = BeginPaint (hwnd, &ps);//获得设备描述表句柄
    SetCursor (LoadCursor (NULL, IDC_WAIT));//设置新的鼠标光标
    ShowCursor (TRUE);//显示鼠标光标
    for (i = 0 ; i < iCount ; i++)
    {
      MoveToEx(hdc, center.x, center.y,NULL);//绘制直线
      LineTo(hdc, points.x, points.y);
    }
    ShowCursor(FALSE);//隐藏鼠标
    SetCursor(LoadCursor (NULL, IDC_ARROW));
    //恢复原来的鼠标光标 ?
    EndPaint(hwnd, &ps);
    return 0;

  利用CTime类的静态成员函数GetCurrentTime获取系统当前的时间对象;再调用CTime类的另一个成员函数Format对得到的时间对象进行格式化,得到一个包含格式化时间的字符串;调用SetPaneInfo函数修改窗格的显示宽度以装下将要显示的字符串文本;调用SetPaneText函数将该字符串显示到状态栏的窗格中;  

    case WM_DESTROY://处理销毁窗口的消息
    PostQuitMessage (0);
    return 0;
  }
  return DefWindowProc (hwnd, msg, wParam, lParam);
}
//函数:MyRegisterClass
//作用:注册窗口类
BOOL MyRegisterClass(HINSTANCE hInstance)
{
  wnd.cbSize= sizeof (wnd);
  wnd.style= CS_HREDRAW | CS_VREDRAW;
  wnd.lpfnWndProc = WndProc;
  wnd.cbClsExtra = 0;
  wnd.cbWndExtra = 0;
  wnd.hInstance = hInstance;
  wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION);
  wnd.hCursor = LoadCursor (NULL, IDC_ARROW);
  wnd.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
  wnd.lpszMenuName= NULL;
  wnd.lpszClassName = szAppName;
  wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  return RegisterClassEx (&wnd);
}
//函数:InitInstance
//作用:创建窗口
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow)
{
  HWND hwnd;
  hwnd = CreateWindow(szAppName,
            ”跟踪鼠标移动”,
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT,
            CW_USEDEFAULT, CW_USEDEFAULT,
            NULL, NULL, hInstance, NULL);
  if(!hwnd) return FALSE;
  ShowWindow (hwnd, iCmdShow);
  UpdateWindow (hwnd);
  return TRUE;
}
  例题的主要功能是在窗口中某个位置单击鼠标右键时,程序保存捕获的鼠标点处的坐标。紧接着按下鼠标左键在窗口中拖动,程序会记录下鼠标运动的轨迹,并以刚才右击鼠标时确定的点为中心绘制一簇射线。本实例最多可以绘制256条射线。程序的另一目的是为了让读者进一步了解如何捕获鼠标与Ctrl键或
Shift键组合时的复杂鼠标消息。如果在窗口中单击鼠标中键,程序会在窗口中央显示文本信息说明用户是否同时按下Ctrl键和Shift键。
  源文件与本书前面所介绍的其他实例一样,都具有基本的 Windows API
程序的结构。即以WinMain函数作为程序入口,调用MyRegisterClass函数和InitInstance函数注册窗口类和创建窗口,再进入消息循环。并在消息循环中调用WndProc函数处理鼠标消息。下面主要介绍WndProc函数处理鼠标消息的方法和技巧。
  在例中WndProc函数能够处理的消息包括
WM_MBUTTONDOWN、WM_RBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP和WM_PAINT。
处理WM_ MBUTTONDOWN消息的程序段如下:
case WM_MBUTTONDOWN:
//处理鼠标中键按下的消息
iCount = 0;//重新初始化点数目
InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口
hdc = BeginPaint (hwnd,&ps);
GetClientRect(hwnd,&rect);
if(wParam&MK_CONTROL)//判断Shift键和Ctrl键是否被按下
{
  if(wParam&MK_SHIFT)
  { //根据不同的情况给出不同的提示
    DrawText(hdc,”Ctrland Shift”,
-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  }
  else
  {
    DrawText(hdc,”Ctrl Only” ,-1,&rect,
    DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  }
}
else
{
  if(wParam&MK_SHIFT)
  {
    DrawText(hdc,”Shift
Only”,-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  }
  else
  {
    DrawText(hdc,”Middle Button of mouse
only”,-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  }
}
EndPaint(hWnd,&ps);
return 0;
  这种判断复杂鼠标操作的方法在基础知识部分已经详细介绍过,这里不再赘述。这一段程序的作用是根据在按下鼠标中键的同时,再按Ctrl键或Shift键的不同情况而在窗口中输出不同的信息。
处理WM_ RBUTTONDOWN消息的程序段如下:
case WM_RBUTTONDOWN://处理鼠标右键按下的消息
iCount = 0;//重新初始化点数目
center.x=LOWORD (lParam);//保存新的中心点坐标
center.y=HIWORD (lParam);
InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口
return 0;
  这一段程序的主要作用是将鼠标右击点的坐标保存在POINT结构体变量center中,再将计数变量iCount归零,为绘制一簇射线作准备。完成以上工作后,程序调用InvalidateRect函数通知系统重画窗口,将窗口中原有的射线族擦去。处理WM_MOUSEMOVE消息的程序段如下:
case WM_MOUSEMOVE://处理鼠标移动的消息
if (wParam & MK_LBUTTON && iCount<256)//只处理鼠标拖动的消息
{
  points[iCount].x = LOWORD (lParam);//保存点的X坐标
  points[iCount++].y = HIWORD (lParam);//保存点的Y坐标
  hdc = GetDC (hwnd);//获得窗口的设备描述表句柄
  SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0L);
  //绘点
  ReleaseDC (hwnd, hdc);//释放设备描述表句柄
}
  事实上,该程序只是处理鼠标左键拖动操作的消息。在执行拖动操作的过程中,程序不断将鼠标点的坐标记录在points数组中。points数组是
WndProc函数中定义的一个静态的POINT结构体数组。在记录点坐标的同时,程序调用了SetPixel函数在窗口上的绘制点,对被记录的点进行标志。对于WM_
LBUTTONUP消息,程序只调用了IvalidateRect函数通知系统拖动操作已经结束,可以开始绘制射线族了。
处理WM _PAINT消息的程序段如下:
case WM_PAINT://处理窗口重画的消息
hdc = BeginPaint (hwnd, &ps);//获得设备描述表句柄
SetCursor (LoadCursor (NULL, IDC_WAIT));//设置新的鼠标光标
ShowCursor (TRUE);//显示鼠标光标
for (i = 0 ; i < iCount ; i++)
{
  MoveToEx(hdc, center.x, center.y,NULL);//绘制直线
  LineTo(hdc, points.x, points.y);
}
ShowCursor(FALSE);//隐藏鼠标
SetCursor(LoadCursor (NULL, IDC_ARROW));
//恢复原来的鼠标光标
EndPaint(hwnd, &ps);
return 0;
  以上程序的功能是实现射线族的绘制。程序使用了一个for循环,循环次数为iCoun变量记录的点数。在循环体中反复调用
MoveToEx函数和
LineTo函数绘制直线。MoveToEx函数使直线的起点回到有center变量记录的中点,LineTo函数实现由中点向points数组中的各点绘制直线。
  在处理WM_PAINT消息的程序中还使用了SetCursor函数和ShowCursor函数。SetCursor函数能设定一个鼠标图标,ShowCursor函数能将设定好的图标显示出来。在本实例中,调用这两个函数的目的是当程序绘制射线族的时候将鼠标图标转换成沙漏图案,表示程序正在执行某次操作。当给制完成后,又重新设置鼠标图像为箭头图标。
  程序运行后,首先生成一个窗口,等待用户执行鼠标操作。用户右击后,再按下鼠标左键并拖动,则程序会绘制出一簇美丽的射线。运行结果如图所示。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ......

    CTime t=CTime::GetCurrentTime();
    CString str=t.Format("%Y-%m-%d %H:%M:%S");
    CClientDC dc(this);
    CSize sz=dc.GetTextExtent(str);
    m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx);
    m_wndStatusBar.SetPaneText(1,str);
    
    return 0;
}

 

  此时在状态栏窗格中显示的时间是静止的,利用定时器每隔一秒就发送WM_TIMER消息,在消息响应函数OnTimer中重新获取一次系统当前时间并显示到状态栏窗格中,已达到显示动态时间的效果;

鼠标消息实例2
#include <windows.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE,LPSTR,int);
LRESULT CALLBACK WndProc(HWND,UINT, WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
lpCmdLine,int nCmdShow)
{
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
  wcex.lpfnWndProc = (WNDPROC)WndProc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = hInstance;
  wcex.hIcon = LoadIcon (NULL,(LPCTSTR)IDI_APPLICATION);
  wcex.hCursor = LoadCursor (NULL,IDC_ARROW);
  wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
  wcex.lpszMenuName = NULL;
  wcex.lpszClassName = “SeeMouseMessage”;
  wcex.hIconSm = LoadIcon(NULL,(LPCTSTR)IDI_APPLICATION);
  if(!RegisterClassEx(&wcex)) return FALSE;
  int SW_XFS = GetSystemMetrics(SM_CXSCREEN);
  int SW_YFS = GetSystemMetrics(SM_CYSCREEN);
  HWND hWnd;
  hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
             ”SeeMouseMessage”,
             ”Trace Mouse Operation”,
             WS_OVERLAPPEDWINDOW,
             0,
             0,
             SW_XFS,
             SW_YFS-25,
             NULL,
             NULL,
             hInstance,
             NULL);
  if(!hWnd) return FALSE;
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  MSG msg;

  在CMainFrame类的OnCreate函数中埋下定时器: 

  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ......
    
    SetTimer(1,1000,NULL);
    
    return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM
lParam)
{
  HDC hDC;
  PAINTSTRUCT ps;
  static char Buffer[256];
  switch(message)
  {
    case WM_MOUSEMOVE:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,40,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_MOUSEMOVE %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,40,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

  为CMainFrame类添加WM_TIMER消息,生成消息响应函数OnTimer,在该函数中重新执行一次系统时间的获取并显示到状态栏窗格中: 

    case WM_LBUTTONDOWN:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,60,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_LBUTTONDOWN %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,60,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

void CMainFrame::OnTimer(UINT nIDEvent) 
{
    // TODO: Add your message handler code here and/or call default

    CTime t=CTime::GetCurrentTime();
    CString str=t.Format("%Y-%m-%d %H:%M:%S");
    CClientDC dc(this);
    CSize sz=dc.GetTextExtent(str);
    m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx);
    m_wndStatusBar.SetPaneText(1,str);
    
    CFrameWnd::OnTimer(nIDEvent);
}

    case WM_LBUTTONUP:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,80,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_LBUTTONUP %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,80,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

  2、进度条控件

    case WM_LBUTTONDBLCLK:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,100,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_LBUTTONDBLCLK %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,100,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

  要在程序中使用进度栏,首先需要构造一个CProgressCtrl对象,然后调用CProgressCtrl类的Create函数创建进度栏控件;

    case WM_RBUTTONDOWN:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,120,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_RBUTTONDOWN %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,120,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

    BOOL Create(

    case WM_RBUTTONUP:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,140,Buffer,strlen(Buffer));
    wsprintf(Buffer,” WM_RBUTTONUP %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,140,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

      DWORD dwStyle, 
//进度栏控件的类型,窗口所有的类型+PBS_VERTICAL(进度栏垂直)/PBS_SMOOTH(进度栏连续)

    case WM_RBUTTONDBLCLK:
    hDC = GetDC(hWnd);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,160,Buffer,strlen(Buffer));
    wsprintf(Buffer,”WM_RBUTTONDBLCLK %04x %3d %3d”,
wParam,LOWORD(lParam),HIWORD(lParam));
    TextOut(hDC,20,160,Buffer,strlen(Buffer));
    ReleaseDC(hWnd,hDC);
    break;

      const RECT& rect,  //进度栏控件的大小、位置

    case WM_PAINT:
    hDC = BeginPaint(hWnd,&ps);
    wsprintf(Buffer,” “);
    TextOut(hDC,20,20,Buffer,strlen(Buffer));
    wsprintf(Buffer,” Message wParam x y”);
    TextOut(hDC,20,20,Buffer,strlen(Buffer));
    EndPaint(hWnd,&ps);
    break;

      CWnd* pParentWnd,  //进度栏的父窗口

    case WM_DESTROY:
    PostQuitMessage(0);
    break;

      UINT nID   //指定进度栏控件的ID

    default:
    return DefWindowProc(hWnd,message,wParam,lParam);
  }
  return 0;
}

    );

  在程序的CMainFrame类中添加成员变量:CProgressCtrl m_progress

    图片 6

  要在状态栏的窗格中显示进度栏,首先利用GetItemRect函数获得该窗格的区域大小:

    void GetItemRect(int nIndex, LPRECT lpRect ) const;

    nIndex:指定窗格的位置索引

    lpRect:窗格的矩形区域

  

  当程序窗口第一次显示时就会发送WM_PAINT消息,而且当程序的窗口尺寸发生变化需要重绘时也会发送WM_PAINT消息,故可在WM_PAINT消息的响应函数中判断,若程序还未创建进度栏则创建它,若已创建则将进度栏移动至目标窗格的矩形区域中;

  为CMainFrame类添加WM_PAINT消息的响应函数:

  图片 7  

void CMainFrame::OnPaint() 
{
    CPaintDC dc(this); // device context for painting
    
    // TODO: Add your message handler code here
    CRect rect;
    m_wndStatusBar.GetItemRect(2,&rect);  //获得状态栏中窗格的矩形区域
    if(!m_progress.m_hWnd){   //判断当m_progress对象的句柄为NULL时,说明该对象还未创建,则创建进度栏
        m_progress.Create(WS_CHILD|WS_VISIBLE,rect,&m_wndStatusBar,123);  //创建进度栏
    }else{
        m_progress.MoveWindow(rect);  //将进度栏移至目标矩形中
    }
    
    m_progress.SetPos(50);  //设置进度栏的当前进度
    
    // Do not call CFrameWnd::OnPaint() for painting messages
}

  若要实现进度栏进度的动态增加,需要利用定时器,在WM_TIMER消息的响应函数OnTimer函数中实现:  

void CMainFrame::OnTimer(UINT nIDEvent) 
{
    ......

    m_progress.StepIt();
    
    CFrameWnd::OnTimer(nIDEvent);
}

    CProgressCtrl::StepIt:使进度栏控件的进度按照一定的步长增加

    CProgressCtrl::SetStep:设置进度栏每次增加的步长

    CProgressCtrl::SetRange:设置进度栏的范围,默认为0~100

  3、在状态栏上显示鼠标当前的位置

    当在程序的客户区窗口移动鼠标时,将鼠标当前的坐标位置显示在状态栏的提示行上;

    首先需要在程序的CStyleView类中添加WM_MOUSEMOVE消息的响应函数:

      图片 8

    因为GetParent函数返回的为CWnd类型的指针,需要强制转换为CMainFrame类型的指针,故须在视图类的源文件中包含框架类的头文件:

      #include “MainFrm.h”

    在WM_MOUSEMOVE消息的响应函数中,实现状态栏对象的获取及在其窗格中的文本显示:      

void CStyleView::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CString str;
    str.Format("x=%d,y=%d",point.x,point.y);  //格式化鼠标坐标位置的信息
    //通过调用GetParent函数获取视图类的父窗口即框架类窗口,将返回值强制转换后,调用框架窗口对象中的状态栏成员变量
    ((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
    
    CView::OnMouseMove(nFlags, point);
}

    将鼠标当前位置信息显示到状态栏对象的提示行中的方法:

      方法一:SetWindowText

        框架类CMainFrame类的状态栏成员变量m_wndStatusBar默认在CMainFrame类的头文件中被定义为protected类型,须手动将其修改为public类型才能被调用;

      方法二:CFrameWnd类的成员函数:SetMessageText

        该函数的作用即为在ID为0的状态栏窗格(即提示行)设置一个字符串;因程序框架类CMainFrame类派生于CFrameWnd类,故这里可以直接用框架类对象调用该成员函数来设置状态栏文本;        

void CStyleView::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CString str;
    str.Format("x=%d,y=%d",point.x,point.y);
    //((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
    ((CMainFrame*)GetParent())->SetMessageText(str);
    
    CView::OnMouseMove(nFlags, point);
}

      方法三:CFrameWnd类的成员函数:GetMessageBar

        该函数返回状态栏对象的指针,故无须像方法一那样修改并调用CMainFrame类的保护成员变量m_wndStatusBar了,可直接调用SetWindowText函数设置状态栏的第一个窗格(提示行)的文本;       

void CStyleView::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CString str;
    str.Format("x=%d,y=%d",point.x,point.y);
    //((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
    //((CMainFrame*)GetParent())->SetMessageText(str);
    ((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
    
    CView::OnMouseMove(nFlags, point);
}

      方法四:CWnd类的成员函数:GetDescendantWindow

        该函数可通过指定的ID来获得当前窗口的所有子孙窗口,因状态栏属于框架类窗口,故先要得到框架类窗口的指针,然后根据状态栏的窗口ID调用该函数获得状态栏对象的指针       

void CStyleView::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CString str;
    str.Format("x=%d,y=%d",point.x,point.y);
    //((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
    //((CMainFrame*)GetParent())->SetMessageText(str);
    //((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
    GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
    
    CView::OnMouseMove(nFlags, point);
}

 

网站地图xml地图