文本输出与滚动条

#include<windows.h>
#include"SYSMET.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("SysMets1");
    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("This program requires Windows NT!"), szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,
        TEXT("Get System Metrics No.1"),
        WS_OVERLAPPEDWINDOW | WS_VSCROLL,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        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;
    static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos;
    TEXTMETRIC tm;
    int i, y;
    TCHAR szBuffer[10];

    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        GetTextMetrics(hdc, &tm);        //获取字体的各种信息
        cxChar = tm.tmAveCharWidth;
        cyChar = tm.tmHeight + tm.tmExternalLeading;
        cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
        ReleaseDC(hwnd, hdc);
        SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);
        SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
        return 0;
    case WM_SIZE:
        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;
            break;
        case SB_PAGEDOWN:
            iVscrollPos += cyClient / cyChar;
            break;
        case SB_THUMBPOSITION:
            iVscrollPos = HIWORD(wParam);
            break;
        }
        iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 1));
        if (iVscrollPos != GetScrollPos(hwnd, SB_VERT))
        {
            SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
            InvalidateRect(hwnd, NULL, TRUE);                //使客户区无效,从而发送WM_PAINT进行重绘
        }
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        for (i = 0; i < NUMLINES; i++)
        {
            y = cyChar * (i - iVscrollPos);
            TextOut(hdc, 0, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));

            TextOut(hdc, 22 * cxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
            SetTextAlign(hdc, TA_RIGHT | TA_TOP);                                                                //设置右对齐
            TextOut(hdc, 22 * cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%5d"), GetSystemMetrics(sysmetrics[i].Index)));
            SetTextAlign(hdc, TA_LEFT | TA_TOP);
        }
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
获取句柄和释放句柄只能在一跳消息中,CreateDC除外
case WM_PAINT:
    return 0;
这种代码会使windows不停发生WM_PAINT,因为没有使用BeginPaint使无效区域有效化,即一直处于无效状态,所以才会一直发送WM_PAINT。
GetWindowDC返回的是整个窗口的设备环境句柄,GetWindowDC返回的句柄可以在窗口的标题栏进行输出,相应的,程序也要处理WM_NCPAINT(非客户区绘制)消息。
GetDC返回的设备环境句柄中的裁剪矩形是整个客户区,所以可以在客户区任意部分绘制,即使没有无效矩形也没有关系。GetDC不会讲无效区域有效化,如果要使整个客户区有效化,需调用ValidateRect(hwnd, NULL)。而InvalidateRect(hwnd, NULL, TRUE)会是整个客户区无效化,并使其后调用的BeginPaint擦除原有的背景。
在设定完滚动条的坐标之后立即调用InvalidateRect会生成WM_PAINT消息以进行图形绘制,但是由于WM_PAINT消息在消息队列中处于低优先级,所以你可能不那么及时地看到绘制出来的界面,所以使用UpdateWindow函数进行
原文地址:https://www.cnblogs.com/Lu3ky-Athena/p/13626226.html