zoukankan      html  css  js  c++  java
  • Windows程序设计--(四)文本输出

    4.1 绘制和重绘

    4.1.2 有效矩阵和无效矩阵

    在擦除对话框之后,需要重画的被对话框遮住的矩形区域,这个区域称为「无效区域」或「更新区域」。正是显示区域内无效区域的存在,才会让Windows将一个WM_PAINT消息放在应用程序的消息队列中。只有在显示区域的某一部分失效时,窗口才会接受WM_PAINT消息。

    Windows内部为每个窗口保存一个「绘图信息结构」,这个结构包含了包围无效区域的最小矩形的坐标以及其它信息,这个矩形就叫做「无效矩形」,有时也称为「无效区域」。如果在窗口消息处理程序处理WM_PAINT消息之前显示区域中的另一个区域变为无效,则Windows计算出一个包围两个区域的新的无效区域(以及一个新的无效矩形),并将这种变化后的信息放在绘制信息结构中。Windows不会将多个WM_PAINT消息都放在消息队列中。

    4.2 GDI 简介

    4.2.2 获取设备环境句柄:方法一

    HDC hdc;//设备环境句柄
    PAINTSTRUCT ps;//绘制结构
    RECT rect;//矩形结构
    case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);//标明窗口绘制开始,设备环境句柄
            GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸
            DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
            EndPaint(hwnd, &ps);//结束窗口绘制
            return 0;

    4.2.3 绘制信息结构

    PAINTSTRUCT结构包含了应用程序用来绘制它所拥有的窗口客户区所需要的信息。PAINTSTRUCT的结构定义如下:

    typedef struct tagPAINTSTRUCT {
      HDC hdc;
      BOOL fErase; 
      RECT rcPaint;
      BOOL fRestore;
      BOOL fIncUpdate;
      BYTE rgbReserved[32]; 
    } PAINTSTRUCT, *PPAINTSTRUCT;

      当调用BeginPaint函数时, Windows将自动填充这个结构体中的成员相关属性, 程序仅能使用前三个成员, 其他为Windows内部使用。 参数一HDC hdc即为设备环境句柄, BeginPaint函数的返回值也就是这里的设备环境句柄, 简单来说就是先填充再返回; 参数二fErase决定是否擦出客户区背景, 如果为非零值则擦除背景,否则不擦除背景; 参数三rcPaint 通过指定客户区左上角和右下角的坐标确定一个要绘制的矩形范围, 即使需要更新的无效区域不是一个矩形, Windows也会把需要重绘的部分裁剪为一个矩形。

      如果你仍想重绘整个客户区, 可以在BeginPaint函数之前调用InvalidateRect( hwnd, NULL, TRUE );使整个客户区无效化。

     

    4.2.4 获取设备环境句柄:方法二

    #include <Windows.h>
    #include <iostream>
    
    using namespace std;
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数
    {
        static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称
        HWND hwnd;//句柄
        MSG msg;//结构体
        WNDCLASS wndclass;//窗口类
    
        //窗口类属性
        wndclass.style = CS_HREDRAW | CS_VREDRAW;//样式
        wndclass.lpfnWndProc = WndProc;//窗口处理函数
        wndclass.cbClsExtra = 0;//窗口实例扩展
        wndclass.cbWndExtra = 0;//窗口类扩展
        wndclass.hInstance = hInstance;//窗口实例句柄
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载图标
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//鼠标,移入内容区域变成箭头
        wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);//主窗口背景色
        wndclass.lpszMenuName = NULL;//窗口菜单
        wndclass.lpszClassName = szAppName;//窗口类名
    
        if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口
            MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口
            
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,                //Windows类名
                            TEXT("窗口绘制成功!"),        //窗口标题
                            WS_OVERLAPPEDWINDOW,    //窗口风格
                            CW_USEDEFAULT,            //初始化窗口位置的X坐标
                            CW_USEDEFAULT,            //初始化窗口位置的Y坐标
                            CW_USEDEFAULT,            //初始化窗口宽度大小
                            CW_USEDEFAULT,            //初始化窗口长度大小
                            NULL,                    //父类窗口句柄
                            NULL,                    //窗口菜单句柄
                            hInstance,                //程序实例句柄
                            NULL);                    //创建参数
        ShowWindow(hwnd, iCmdShow);//显示窗口
        UpdateWindow(hwnd);//更新窗口
        
        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;//绘制结构
        /*
        typedef struct tagRECT
        {
            LONG    left;
            LONG    top;
            LONG    right;
            LONG    bottom;
        } RECT
        其中left,top赋为0,因此right和bottom表示客户区的宽度和高度(像素)
        */
        RECT rect;//矩形结构
    
        switch (message) {//处理得到的消息
        case WM_CREATE://窗口创建发来消息
            MessageBox(hwnd, TEXT("创建成功,音乐播放"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);
            /*
            1.波形文件的名称
            2.只有当声音文件是一个资源时才有用,此处NULL表示不使用
            3.指定一组选项,表示指定了第一个参数为文件名且该段声音是以异步方式播放
            */
            PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
            return 0;
        case WM_PAINT://处理窗口绘制
            hdc = GetDC(hwnd);
            GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸
            DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
            ReleaseDC(hwnd, hdc);
            ValidateRect(hwnd, NULL);//因为会不断更新整个客户区,不断刷新,要使其他消息能接收,需要使客户区有效
            return 0;
        case WM_LBUTTONDOWN:
            MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_RBUTTONDOWN:
            MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_DESTROY://处理窗口关闭时的消息
            MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
            PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理
    }

    4.2.5 TEXTOUT函数详解

    TextOut (hdc, x, y, psText, iLength) ;

    以下将详细地讨论这个函数。

    第一个参数是设备内容句柄,它既可以是GetDC的传回值,也可以是在处理WM_PAINT消息时BeginPaint的传回值。

    psText参数是指向字符串的指针,iLength是字符串中字符的个数。如果psText指向Unicode字符串,则字符串中的字节数就是iLength值的两倍。字符串中不能包含任何ASCII控制字符(如回车、换行、制表或退格),Windows会将这些控制字符显示为实心块。Text0ut不识别作为字符串结束标志的内容为零的字节(对于Unicode,是一个短整数型态的0),而需要由nLength参数指明长度。

    TextOut中的x和y定义显示区域内字符串的开始位置,x是水平位置,y是垂直位置。字符串中第一个字符的左上角位于坐标点(x,y)。在内定的设备内容中,原点(x和y均为0的点)是显示区域的左上角。如果在TextOut中将x和y设为0,则将从显示区域左上角开始输出字符串。

    4.2.6 系统字体

    设备内容还定义了在您呼叫TextOut显示文字时Windows使用的字体。内定字体为「系统字体」,或用Windows表头文件中的标识符,即SYSTEM_FONT。系统字体是Windows用来在标题列、菜单和对话框中显示字符串的内定字体。

    4.2.7 字符大小

    GetTextMetrics需要的TEXTMETRIC型态的结构:

    typedef struct tagTEXTMETRIC {
      LONG tmHeight;              //字符高度
      LONG tmAscent;              //字符上部高度(基线以上)
      LONG tmDescent;             //字符下部高度(基线以下)
      LONG tmInternalLeading,     //由tmHeight定义的字符高度的顶部空间数目
      LONG tmExternalLeading,     //夹在两行之间的空间数目
      LONG tmAveCharWidth,        //平均字符宽度
      LONG tmMaxCharWidth,        //最宽字符的宽度
      LONG tmWeight;              //字体的粗细轻重程度
      LONG tmOverhang,            //加入某些拼接字体上的附加高度
      LONG tmDigitizedAspectX,    //字体设计所针对的设备水平方向
      LONG tmDigitizedAspectY,    //字体设计所针对的设备垂直方向
      BCHAR tmFirstChar;          //为字体定义的第一个字符
      BCHAR tmLastChar;           //为字体定义的最后一个字符
      BCHAR tmDefaultChar;        //字体中所没有字符的替代字符
      BCHAR tmBreakChar;          //用于拆字的字符
      BYTE tmItalic,              //字体为斜体时非零
      BYTE tmUnderlined,          //字体为下划线时非零
      BYTE tmStruckOut,           //字体被删去时非零
      BYTE tmPitchAndFamily,      //字体间距(低4位)和族(高4位)
      BYTE tmCharSet;             //字体的字符集
    } TEXTMETRIC;

    总共20个字段,我们只关心前7个。默认的映射模式为MM_TEXT

    TextOut的两种运用,将上面代码的WM_PAINT部分替换即可。

    第一种

    TEXTMETRIC tm;
    TCHAR szBuffer[100];
    int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
    case WM_PAINT://处理窗口绘制
        hdc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &rect);
        TextOut(hdc, 400, 200, szBuffer, iLength);
        EndPaint(hwnd, &ps);
        return 0;

    第二种:

    在switch前面定义一个结构变量tm

    TEXTMETRIC tm;
    TCHAR szBuffer[100];
    int iLength = wsprintf(szBuffer, TEXT("Unicode形式输出!"));
    case WM_PAINT://处理窗口绘制
        hdc = GetDC(hwnd);
        GetTextMetrics(hdc, &tm);
        TextOut(hdc, 400, 200, szBuffer, iLength);
        ReleaseDC(hwnd, hdc);
        ValidateRect(hwnd, NULL);
        return 0;

    4.2.8 文本尺寸大小

    tmHeight,它是tmAscent和tmDescent的和。这两个值表示了基准在线下字符的最大纵向高度。「间距」(leading)指打印机在两行文字间插入的空间。在TEXTMETRIC结构中,内部的间距包括在tmAscent中(因此也在tmHeight中),并且它经常是重音符号出现的地方。tmInternalLeading字段可被设成0,在这种情况下,加重音的字母会稍稍缩短以便容纳重音符号。

    字段tmExternalLeading,它是字体设计者建议加在横向字符之间的空间大小。

    TEXTMETRICS结构包含有描述字符宽度的两个字段,即tmAveCharWidth(小写字母加权平均宽度)和tmMaxCharWidth(字体中最宽字符的宽度)。

    4.2.9 文本的格式化

    4.2.10 综合使用

    SYSMETS.h
    #include <Windows.h>
    
    #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0])))
    
    struct
    {
        const TCHAR* szName;
        int iAge;
        const TCHAR* szMajor;
    }
    sysmetrics[]={
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程")
    };
    #include <Windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include "SYSMETS.h"
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数
    {
        static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称
        HWND hwnd;//句柄
        MSG msg;//结构体
        WNDCLASS wndclass;//窗口类
    
        //窗口类属性
        wndclass.style = CS_HREDRAW | CS_VREDRAW;//样式
        wndclass.lpfnWndProc = WndProc;//窗口处理函数
        wndclass.cbClsExtra = 0;//窗口实例扩展
        wndclass.cbWndExtra = 0;//窗口类扩展
        wndclass.hInstance = hInstance;//窗口实例句柄
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载图标
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//鼠标,移入内容区域变成箭头
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//主窗口背景色
        wndclass.lpszMenuName = NULL;//窗口菜单
        wndclass.lpszClassName = szAppName;//窗口类名
    
        if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口
            MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口
    
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,    //Windows类名
            TEXT("窗口绘制成功!"),        //窗口标题
            WS_OVERLAPPEDWINDOW,        //窗口风格
            CW_USEDEFAULT,                //初始化窗口位置的X坐标
            CW_USEDEFAULT,                //初始化窗口位置的Y坐标
            500,                        //初始化窗口长度大小
            300,                        //初始化窗口宽度大小
            NULL,                        //父类窗口句柄
            NULL,                        //窗口菜单句柄
            hInstance,                    //程序实例句柄
            NULL);                        //创建参数
        ShowWindow(hwnd, iCmdShow);//显示窗口
        UpdateWindow(hwnd);//更新窗口
    
        while (GetMessage(&msg, NULL, 0, 0)) {//从消息队列中获取消息
            TranslateMessage(&msg);//将虚拟键消息转换为字符消息
            DispatchMessage(&msg);//分发到回调函数
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
        static int cxChar, cxCaps, cyChar;
        int i = 0;
        TCHAR szBuffer[100];
        HDC hdc;//设备环境句柄
        PAINTSTRUCT ps;//绘制结构
        TEXTMETRIC tm;
    
        switch (message) {//处理得到的消息
        case WM_CREATE://窗口创建发来消息
            hdc = GetDC(hwnd);
            GetTextMetrics(hdc, &tm);
            cxChar = tm.tmAveCharWidth;        //得到字体平均宽度
            //cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//判断字体是等宽字体还是变宽字体,变宽字体是cxChar的1.5倍
            cyChar = tm.tmHeight + tm.tmExternalLeading;// //字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading
            ReleaseDC(hwnd, hdc);
            return 0;
        case WM_PAINT://处理窗口绘制
            hdc = BeginPaint(hwnd, &ps);
            for (i = 0; i < NUMLINES; ++i) {
                //SetTextAlign(hdc, TA_RIGHT | TA_TOP);//设置TextOut使用的坐标将从右上角开始
                TextOut(hdc, 0, cyChar * i, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName));
                //TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), GetSystemMetrics(sysmetrics[i].iAge)));
                TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
                TextOut(hdc, 200, cyChar * i, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
            }
         EndPaint(hwnd,&ps);
    return 0; case WM_LBUTTONDOWN: MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_RBUTTONDOWN: MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION); return 0; case WM_DESTROY://处理窗口关闭时的消息 MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串 PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam return 0; } return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理 }

    SYSMETS.h
    #include <Windows.h>
    
    #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0])))
    
    struct
    {
        const TCHAR* szName;
        int iAge;
        const TCHAR* szMajor;
    }
    sysmetrics[]={
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程")
    };
    #include <Windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include "SYSMETS.h"
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数
    {
        static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称
        HWND hwnd;//句柄
        MSG msg;//结构体
        WNDCLASS wndclass;//窗口类
    
        //窗口类属性
        wndclass.style = CS_HREDRAW | CS_VREDRAW;//样式
        wndclass.lpfnWndProc = WndProc;//窗口处理函数
        wndclass.cbClsExtra = 0;//窗口实例扩展
        wndclass.cbWndExtra = 0;//窗口类扩展
        wndclass.hInstance = hInstance;//窗口实例句柄
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载图标
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//鼠标,移入内容区域变成箭头
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//主窗口背景色
        wndclass.lpszMenuName = NULL;//窗口菜单
        wndclass.lpszClassName = szAppName;//窗口类名
    
        if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口
            MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口
    
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,    //Windows类名
            TEXT("窗口绘制成功!"),        //窗口标题
            WS_OVERLAPPEDWINDOW,        //窗口风格
            CW_USEDEFAULT,                //初始化窗口位置的X坐标
            CW_USEDEFAULT,                //初始化窗口位置的Y坐标
            500,                        //初始化窗口长度大小
            300,                        //初始化窗口宽度大小
            NULL,                        //父类窗口句柄
            NULL,                        //窗口菜单句柄
            hInstance,                    //程序实例句柄
            NULL);                        //创建参数
        ShowWindow(hwnd, iCmdShow);//显示窗口
        UpdateWindow(hwnd);//更新窗口
    
        while (GetMessage(&msg, NULL, 0, 0)) {//从消息队列中获取消息
            TranslateMessage(&msg);//将虚拟键消息转换为字符消息
            DispatchMessage(&msg);//分发到回调函数
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
        static int cxChar, cxCaps, cyChar;
        static int cxClient, cyClient,iVscrollPos;
        int i = 0, y;
        TCHAR szBuffer[100];
        HDC hdc;//设备环境句柄
        PAINTSTRUCT ps;//绘制结构
        TEXTMETRIC tm;
    
        switch (message) {//处理得到的消息
        case WM_CREATE://窗口创建发来消息
            hdc = GetDC(hwnd);
            GetTextMetrics(hdc, &tm);
            cxChar = tm.tmAveCharWidth;        //得到字体平均宽度
            //cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
            cyChar = tm.tmHeight + tm.tmExternalLeading;// //字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading
            ReleaseDC(hwnd, hdc);
            SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);//设置垂直滚动条的范围
            SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置垂直滚动条的位置
            return 0;
        case WM_PAINT://处理窗口绘制
            hdc = BeginPaint(hwnd, &ps);
            for (i = 0; i < NUMLINES; ++i) {
                y = cyChar * (i - iVscrollPos);//刚开始为0,小于iVscrollPos都被显示到了内容区域之外
                //SetTextAlign(hdc, TA_RIGHT | TA_TOP);
                TextOut(hdc, 0, y, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName));
                //TextOut(hdc, 400, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%2d"), GetSystemMetrics(sysmetrics[i].iAge)));
                TextOut(hdc, 400, y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
                TextOut(hdc, 200, y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
            }
            return 0;
        case WM_LBUTTONDOWN:
            MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_RBUTTONDOWN:
            MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
            return 0;
        case WM_SIZE:
            //cxClient = LOWORD(lParam);//客户区宽度
            cyClient = HIWORD(lParam);//客户区的高度
            return 0;
        case WM_VSCROLL:
            switch (LOWORD(wParam)) {
            case SB_LINEUP:
                iVscrollPos -= 1;//表示向上移动一行
                break;
            case SB_LINEDOWN:
                iVscrollPos += 1;
                break;
            case SB_PAGEUP:
                iVscrollPos -= cyClient / cyChar;//cyClient / cyChar表示客户区一列能显示的字符数,因此这里表示向上一屏
                break;
            case SB_PAGEDOWN:
                iVscrollPos += cyClient / cyChar;
                break;
            case SB_THUMBPOSITION:
                iVscrollPos = HIWORD(wParam);
                break;
            default:
                break;
            }
            /*
            min中判断是否到达最后一组数据,
            max如果在第一行向上移动,iVscrollPos为负,所以使用max表示向上还是显示第一组数据。
            */
            iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 1));
            if (iVscrollPos != GetScrollPos(hwnd, SB_VERT)) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据
                SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//设置显示位置
                InvalidateRect(hwnd, NULL, TRUE);//将要绘制区域无效
            }
            return 0;
        case WM_DESTROY://处理窗口关闭时的消息
            MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
            PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理
    }

     但是上面的最后一个信息,显示在最后一页,可以将最后一个信息显示在最后一页的最后一行。

    #include <Windows.h>
    
    #define NUMLINES ((int)(sizeof(sysmetrics) / sizeof(sysmetrics [0])))
    
    struct
    {
        const TCHAR* szName;
        int iAge;
        const TCHAR* szMajor;
    }
    sysmetrics[]={
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("刘然"), 20, TEXT("软件工程"),
        TEXT("黄鹤"), 21, TEXT("计算机科学"),
        TEXT("马平"), 20, TEXT("网络工程"),
        TEXT("评然"), 20, TEXT("软件工程"),
        TEXT("里鹤"), 21, TEXT("计算机科学"),
        TEXT("高平"), 20, TEXT("网络工程"),
        TEXT("偶然"), 20, TEXT("软件工程"),
        TEXT("炮打"), 21, TEXT("计算机科学"),
        TEXT("马结束"), 20, TEXT("网络工程")
    };
    View Code
    #include <Windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include "SYSMETS.h"
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//消息函数声明
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)//主函数
    {
        static TCHAR szAppName[] = TEXT("WNDCLASS NAME");//窗口类名称
        HWND hwnd;//句柄
        MSG msg;//结构体
        WNDCLASS wndclass;//窗口类
    
        //窗口类属性
        wndclass.style = CS_HREDRAW | CS_VREDRAW;//样式
        wndclass.lpfnWndProc = WndProc;//窗口处理函数
        wndclass.cbClsExtra = 0;//窗口实例扩展
        wndclass.cbWndExtra = 0;//窗口类扩展
        wndclass.hInstance = hInstance;//窗口实例句柄
        wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//加载图标
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//鼠标,移入内容区域变成箭头
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//主窗口背景色
        wndclass.lpszMenuName = NULL;//窗口菜单
        wndclass.lpszClassName = szAppName;//窗口类名
    
        if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口
            MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口
    
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,    //Windows类名
            TEXT("窗口绘制成功!"),        //窗口标题
            WS_OVERLAPPEDWINDOW,        //窗口风格
            CW_USEDEFAULT,                //初始化窗口位置的X坐标
            CW_USEDEFAULT,                //初始化窗口位置的Y坐标
            500,                        //初始化窗口长度大小
            300,                        //初始化窗口宽度大小
            NULL,                        //父类窗口句柄
            NULL,                        //窗口菜单句柄
            hInstance,                    //程序实例句柄
            NULL);                        //创建参数
        ShowWindow(hwnd, iCmdShow);//显示窗口
        UpdateWindow(hwnd);//更新窗口
    
        while (GetMessage(&msg, NULL, 0, 0)) {//从消息队列中获取消息
            TranslateMessage(&msg);//将虚拟键消息转换为字符消息
            DispatchMessage(&msg);//分发到回调函数
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
        static int cxChar, cxCaps, cyChar;
        static int cxClient, cyClient,iMaxWidth, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;
        int i = 0, x, y;
        SCROLLINFO si;
        TCHAR szBuffer[100];
        HDC hdc;//设备环境句柄
        PAINTSTRUCT ps;//绘制结构
        TEXTMETRIC tm;
    
        switch (message) {//处理得到的消息
        case WM_CREATE://窗口创建发来消息
            hdc = GetDC(hwnd);
            GetTextMetrics(hdc, &tm);
            cxChar = tm.tmAveCharWidth;        //得到字体平均宽度
            cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//判断字符类型,Unicode是字符型的1.5倍大小
            cyChar = tm.tmHeight + tm.tmExternalLeading;//字体高度, 总高度tmHeight + 两行文字之间的建议间距大小tmExternalLeading
            ReleaseDC(hwnd, hdc);
            iMaxWidth = 40 * cxChar + 22 * cxCaps;//最大宽度
            return 0;
        case WM_PAINT://处理窗口绘制
            hdc = BeginPaint(hwnd, &ps);
    
            si.cbSize = sizeof(si);//必须设置cbSize为结构体大小
            si.fMask = SIF_POS;//指定滚动条位置
            GetScrollInfo(hwnd, SB_VERT, &si);//获取当前纵向滚动条位置
            iVertPos = si.nPos;//设置iVertPos为当前纵向滚动条位置
    
            GetScrollInfo(hwnd, SB_HORZ, &si);//获取当前横向滚动条位置
            iHorzPos = si.nPos;//设置iHorzPos为当前横向滚动条位置
    
            iPaintBeg = max(0, iVertPos + ps.rcPaint.top / cyChar);//ps.rcPaint.top这里为0,处理UP操作
            //ps.rcPaint.bottom这里为cyClient,处理DOWN操作,因为...top为0,所以不考虑Unicode字符影响
            //而...bottom表示cyClient大小,实际上的Unicode字符是原来的1.5倍
            iPaintEnd = min(NUMLINES, iVertPos + 3 * ps.rcPaint.bottom / cyChar / 2);
            for (i = iPaintBeg; i < iPaintEnd; ++i) {
                //小于当前横iHorzPos,纵iVertPos滚动条位置的数据都被隐藏
                //0和i相当于输出的起始位置
                x = cxChar * (0 - iHorzPos);
                y = cyChar * (i - iVertPos);
                //SetTextAlign(hdc, TA_LEFT | TA_TOP);
                TextOut(hdc, x, y, sysmetrics[i].szName, lstrlen(sysmetrics[i].szName));
                TextOut(hdc, x+ 22 * cxCaps, y, sysmetrics[i].szMajor, lstrlen(sysmetrics[i].szMajor));
                //SetTextAlign(hdc, TA_RIGHT | TA_TOP);
                TextOut(hdc, x+ 22 * cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%2d"), sysmetrics[i].iAge));
            }
            return 0;
        case WM_SIZE:
            cxClient = LOWORD(lParam);//客户区宽度
            cyClient = HIWORD(lParam);//客户区的高度
    
            si.cbSize = sizeof(si);
            si.fMask = SIF_RANGE | SIF_PAGE;//指定滚动范围和页面大小
            
            si.nMin = 0;//纵向范围最小为0
            si.nMax = NUMLINES - 1;//纵向范围最大值为NUMLINES-1
            si.nPage = cyClient / cyChar;//页面行数
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);//设置滚动范围和滚动页码到si中
    
            si.cbSize = sizeof(si);
            si.fMask = SIF_RANGE | SIF_PAGE;
            si.nMin = 0;//横向范围最小为0
            si.nMax = 2 + iMaxWidth / cxChar;//横向范围最大值
            si.nPage = cxClient / cxChar;//每页列数
            SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
            return 0;
        case WM_VSCROLL:
            si.cbSize = sizeof(si);
            si.fMask = SIF_ALL;
            GetScrollInfo(hwnd, SB_VERT, &si);
            iVertPos = si.nPos;
            switch (LOWORD(wParam)) {
            case SB_TOP:
                si.nPos = si.nMin;
                break;
            case SB_BOTTOM:
                si.nPos = si.nMax;
                break;
            case SB_LINEUP:
                si.nPos -= 1;//表示向上移动一行
                break;
            case SB_LINEDOWN:
                si.nPos += 1;
                break;
            case SB_PAGEUP:
                si.nPos -= si.nPage;//cyClient / cyChar表示客户区一列能显示的字符数,因此这里表示向上一屏
                break;
            case SB_PAGEDOWN:
                si.nPos += si.nPage;
                break;
            case SB_THUMBTRACK:
                si.nPos = si.nTrackPos;
                break;
            default:
                break;
            }
            si.fMask = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            GetScrollInfo(hwnd, SB_VERT, &si);
            if (si.nPos != iVertPos) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据
                ScrollWindow(hwnd, 0, cyChar * (iVertPos - si.nPos), NULL, NULL);
                UpdateWindow(hwnd);
            }
            return 0;
        case WM_HSCROLL:
            si.cbSize = sizeof(si);
            si.fMask = SIF_ALL;
    
            GetScrollInfo(hwnd, SB_HORZ, &si);
            iHorzPos = si.nPos;
            switch (LOWORD(wParam)) {
            case SB_LINELEFT:
                si.nPos -= 1;
                break;
            case SB_LINERIGHT:
                si.nPos += 1;
                break;
            case SB_PAGELEFT:
                si.nPos -= si.nPage;
                break;
            case SB_PAGERIGHT:
                si.nPos += si.nPage;
                break;
            case SB_THUMBPOSITION:
                si.nPos = si.nTrackPos;//截获SB_THUMBPOSITION通知码
                break;
            default:
                break;
            }
            si.fMask = SIF_POS;
            SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
            GetScrollInfo(hwnd, SB_HORZ, &si);
            if (si.nPos != iHorzPos) {//如果滚动跳当前位置和我更新之后的iVscrollPos位置不同,就需要设置新的滚动条位置,并绘制数据
                ScrollWindow(hwnd, cxChar * (iHorzPos - si.nPos), 0, NULL, NULL);
            }
            return 0;
        case WM_DESTROY://处理窗口关闭时的消息
            PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理
    }

    iPaintBeg和iPaintEnd那两行删了,下面使用for(i = 0; i < NUMLINES; ++i)也能够正常输出
  • 相关阅读:
    面向对象设计与实用的思考(主动对象与被动对象)
    改进弧长法 判断点是否在多边形内 c#代码
    PaintCode begin
    kiwivm putty lnmp
    初心已变,我也不是当时的我
    spfa
    tree dp
    开心就好之修行ing
    MIME 参考手册
    TypeScript学习和参考手册
  • 原文地址:https://www.cnblogs.com/Mayfly-nymph/p/11300555.html
Copyright © 2011-2022 走看看