Windows GDI绘图

GDI图形设备接口的英文缩写,用于处理Windows程序的图形和图像输出,程序员不需要关心硬件设备和设备驱动,就可以将应用程序的输出转换成硬件设备上的输出,实现应用程序和硬件设备的隔离,大大简化程序开发工作.

在 Windows 系统中,图形界面应用程序离不开GDI,利用GDI的众多函数可以方便在屏幕,打印机和其他输出设备上实现输出文本,图形和图像等

设备环境

设备无关性是Windows主要功能之一,应用程序可以在各种设备上进行绘制和打印输出,系统统一把所有外部设备当做文件来看看待,只要安装了它们的驱动程序``,应用程序就可以像文件一样操控,使用这些设备,GDI代表应用程序和设备驱动程序进行交互, 为了实现设备无关性,引入了逻辑设备物理设备`两个概念,在应用程序中,使用逻辑设备名称来请求使用该类设备,而系统在实际执行时,使用的是物理设备名称

设备无关性的支持包括在两个动态链接库中,第一个是GDI相关动态链接库,称为图形设备接口,第二个是设备驱动程序,设备驱动程序名称取决于应用程序绘制输出的设备, GDI 处理程序的绘图函数调用, 将这些调用传递给设备驱动程序,设备驱动程序接受来自GDI的输入, 将输入转换成设备命令, 并将这些命令传递给对应设备。

当程序在客户区中显示文本或图形时,我们通常称程序在绘制客户区,GDI在加载驱动程序后,准备设备进行绘制操作, 例如选择线条颜色和宽度,画刷颜色和图案,字体名称,裁剪区域等.这些任务是通过创建和维护设备环境(DC)来完成的, DC是定义了一组图形对象及其关联属性以及影响输出的图形模式的结构体

与 DC 有关的部分图形对象和属性如下所示

  • 画笔: 样式,宽度和颜色
  • 画刷: 样式,颜色,图案和原点
  • 字体: 字体名称,大小,字符宽度,字符高度,字符集
  • 位图: 大小(以字节为单位), 尺寸(以像素为单位),颜色格式,压缩方案
  • 路径: 形状
  • 区域: 位置和尺寸

与大多数结构体不同,应用程序不能直接访问DC,而是通过调用各种函数间接操作DC

Windows 支持5中图形模式,允许应用程序指定颜色的混合方式,输出的位置,输出的缩放模式等,下面描述这些模式

  • 背景模式: 文本的背景色与现有窗口或屏幕颜色的混合方式
  • 绘图模式: 画笔,画刷的颜色与目标显示区域颜色混合方式
  • 映射模式: 如果将图形输出从逻辑坐标映射到客户区,屏幕或打印机纸张
  • 多边形填充模式: 使用画刷填充复杂区域的内部
  • 拉伸模式: 当位图被放大或缩小时如何计算新位图

Windows 有4中类型的DC,分别是显示设备DC,打印DC,内存DC(内存兼容DC),信息DC

  • 显示设备DC: 在显示器上进行绘图操作
  • 打印DC: 打印机或绘图仪上绘图操作
  • 内存DC: 在内存的位图上进行绘图操作
  • 信息DC: 获取设备环境信息

获取显示设备 DC 句柄

DC句柄是程序使用GDI函数的通行证,几乎所有GDI绘图函数都需要一个DC句柄参数,有了DC句柄,就可以绘制窗口客户区

当客户区变成部分或全部无效且必须更新时, 例如改变窗口大小,最大化/最小化,拖动窗口时,
通过BeginPaint获取DC句柄,

hdc = BeginPaint(hwnd, &ps);
// 绘图代码
EndPaint(hwnd, &ps);

BeginPaint 函数的返回值就是需要更新区域DC句柄hdc, BeginPaint 返回的 hdc 对应的尺寸无效区域, 程序无法通过句柄绘制这个区域之外的地方, 因此这个 hdc 值只在本地WM_PAINT 消息中有效,程序不应该保存它并持有它,用在 WM_PAINT 消息之外的代码中, BeginPaitn 和 EndPaint 只能在 WM_PAINT 消息中,这时才存在无效区域,BeginPaint 函数还有一个作用就是把无效区域有效化,如果不调用BeginPaint,那么窗口的无效区域会一直不为空,系统会一直发送WM_PAINT消息

PAINTSTRCUT 结构保存着一个可以覆盖无效区域的最小矩形的坐标和一些其他信息,这个最小矩形成为无效矩形,如果这时候又出现了一个无效区域,那么window会计算出一个新的可以覆盖这两个无效区域的新的无效区域, 并更新PAINTSTRUCT结构,Windows不会在消息队列中放置多条WM_PAINT 消息

WM_PAINT 消息是一个低优先级的消息,总是在消息循环为空的时候才把WM_PAINT 消息放入消息队列, 每当消息循环为空的时候,如果Windows发现一个无效区域,就会在队列中放入一个WM_PAINT消息

如果在其他时间需要进行绘制,调用 GetDC

hdc = GetDC(hwnd);
ReleaseDC(hwnd,hdc);

GetDC函数返回hdc对应指定窗口的整个客户区,通过GetDC函数返回的hdc可以在客户区的任何位置进行绘制操作,不存在无效矩形的概念,无效矩形和BeginPaint才是原配

当使用完毕时,必须调用ReleaseDC函数释放DC, 对于用 GetDC 获取的hdc,Windows建议使用范围是单条消息内.

当程序处理某条嘻嘻阿西时,如果需要绘制客户区,可以调用GetDC获取hdc, 但在消息返回前,必须调用ReleaseDC 释放 DC, 如果在下一条消息还需要用到hdc,重新调用GetDC函数后去,如果hwnd参数设置为NULL,那么函数获取整个屏幕的DC句柄

通过 WM_LBUTTONDOWN 和 WM_MOVEEMOVE 绘制一条线,当改变窗口大小,最小化,最大化后,这条线就消息了,因为需要重绘的时候使用背景画刷擦除背景,如果希望这条线继续存在,必须在WM_PAINT消息中重新绘制(可以事先保存那些点),如果可能,在WM_PAINT消息中处理所有的绘制工作

GetWindowDC 函数可以获取整个窗口的DC句柄,包括非客户区,返回的hdc可以在窗口任意位置进行绘制,因为这个DC的原点使这个窗口的左上角,而不是窗口客户区的左上角,但是程序需要处理WM_NCPAINT(非客户区绘制)消息

HDC GetWindowDC(HWND hWnd);

绘制文本

GetSystemMetrics 函数用于获取系统度量或系统配置信息,例如可以获取屏幕分辨率,全屏窗口客户区的宽度和高度,滚动条的高端和宽度等,以像素为单位

int WINAPI GetSystemMetrics(int nIndex);

参数为索引, 这个索引有95个标识符可以使用,m_nIndex表示可用的索引值,m_pDesc表示含义

例如 GetSystemMetrics(SM_CXSCREEN) 获取屏幕的宽度(CX表示Count X,X轴像素数)
格式如下

SM_CXSCREEN 屏幕的宽度 1366

通过TextOut 函数输出 METRICS 结构数组的每个数组元素很简单,这里仅列出WM_PAINT 消息的处理

case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);
    for (int i =0; i< NUMLINES; i++)
    {
        y = 18 * i; // 行间距
        TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
        TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
        TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, Text("%d"), GetSystemMetrics(METRICS[i].m_nIndex)));
    }
    EndPaint(hwnd,&ps);
    return 0;

程序使用wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1), 把窗口背景设置为系统颜色(浅灰色)

格式化文本

文本输出是程序客户区中最常见的图形输出类型

格式函数分为3类, 获取或设置DC的文本格式属性, 获取字符宽度和高度, 获取字符串宽度和高度

文本格式属性

  1. 文本对齐方式
UINT setTextAlign(
    HDC hdc, // 设备环境句柄
    UInt fMode, // 文本对齐方式
)
#define TA_NOUPDATECP                0 // 每次文本输出函数调用后,当前位置不会更新
#define TA_UPDATECP                  1 // 使用当前位置作为起始点,当前位置在每次文本输出函数调用后会更新

#define TA_LEFT                      0 // 起始点在文本边界矩形的左边缘
#define TA_RIGHT                     2 // 右边缘
#define TA_CENTER                    6 // 边界矩形的中心(水平方向)

#define TA_TOP                       0 // 上边缘
#define TA_BOTTOM                    8 // 下边缘
#define TA_BASELINE                  24 // 基线上

默认值是 TA_LEFT | TA_TOP | TA_NOUPDATECP

调用SetTextAlign 函数可以改变TextOut, ExtTextOut, TabbedTextOut 等函数中nXStart和nYStart参数的含义

UINT GetTextAlign(HDC hdc);

获取DC的当前文本对齐设置

字符间距

SetTextCharacterExtra 设置DC中文本输出的字符间距
逻辑单位,像素

背景模式,背景颜色和文本颜色

SetTextColor 绘制的文本颜色
SetBGColor 设置每个字符后显示的颜色(背景颜色)
SetBGMode: 设置背景模式透明或不透明

iBKMode 参数指定背景模式,可用的值是OPAQUE(不透明),TRANSPARENT(透明)

COLORREF 用于指定RGB颜色值,在windef.h头文件中定义如下:
typedef DWORD COLORREF;
typedef DWORD *LPCOLORREF;

COLORREF 格式为 0x00BBGGRR, 表示透明度+BB(蓝色)+GG(绿色)+RR(红色)

#define RGB(r,g,b) 

获取字符串的宽度和高度

GetCharWidth32 函数可以获取指定DC当前字体中指定范围内的连续字符的宽度

BOOL GetCharWidth32(
    HDC hdc, // 设备环境句柄
    UINT iFirstChar, // 连续字符中的第一个字符
    UINT iLastChar, // 连续字符的最后一个字符,不得位于指定的第一个字符之前
    LPINT lpBuffer, // 接受每个字符宽度的INT数组,字符宽度是逻辑单位
)
INT arrWidth[4];
TCHAR *sz[8] = {0};
TCHAR szBuf[32] = {0};ß
GetCharWidth32(GetDC{hwnd}, TEXT('你',TEXT('佣'),arrWidth));
for (int i = TEXT('你'), j = 0;i<=TEXT('佣');i++,j++)
{
    wsprintf(sz,TEXT("%c = %d\n",i, arrWidth[j]));
    StringCChCat(szBuf,_countof(szBuf),sz);
}
MessageBox(hwnd, szBuf, TEXT("提示"),MB_OK);

你的码值是0x4f60, 佣的码值是0x4F63,输出结果如下所示

你=15
佣=15

GetTextExtentPoint32 函数用于获取指定DC中一个字符串的宽度和高度值
BOOL GetTextExtentPoint32(
HDC hdc, // 设备环境句柄
LPCTSTR lpString, // 字符串指针,不要求以零结尾,因为参数c可以指定字符串长度
int c, // 字符串长度,可以使用_tcslen
LPSIZE lpSize // 在这个SIZE结构中返回字符串的宽度和高度,逻辑单位
)

lpSize 结果是

typedef struct tagSize
{
    LONG cx;
    LONG cy;
} SIZE *PSIZE, *LPSIZE;

可以在 WM_CREATE 消息中获取字符串高度``,用于在 TextOut 函数中指定 y 坐标值`

HDC hdc;
PAINTSTRUCT ps;
TCHAR szBuf[10];
int y;
static SIZE size = {0};

switch (uMsg)
{
    case WM_CREATE:
        hdc = GetDC(hwnd);
        GetTextExtendPoint32(hdc, METRICS[0].m_pLabel, _tcslen(METRICS[0].m_pLabel, &size));
        ReleaseDC(hwnd,hdc);
        return 0;
    case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);
    for (int i = 0;i<NUMLINES;i++)
    {
        y = isze.cy * i;
        setBkMode(hdc, TRASPARENT);
        setTextColor(hdc,RGB(0,0.255));
        TextOut(hdc, 0, y , METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
        TextOut(hdc,0,y,METRICS[i].m_pLabel);
    }
    EndPaint(hwnd,&ps);
    return 0;
}

GetTextExtentPoint32 函数适用于字符串中不包含制表符的情况,如果字符串中包含制表符,则应该调用GetTabbedTextExtent 函数

DWORD GetTabbedTextExtent(
    HDC hdc, 
    LPCTSTR lpString,  // 字符串指针,不要求以零结尾,因为nCount
    int nCount, // 字符串长度,可以使用_tcslen
    int nTabPositions, // 数组中元素个数
LPINT lpnTabStopPositions) // 指向包含制表符位置的数组

如果函数执行成功,返回字符串的宽度和高度

选择字体

系统提供了6种备用字体, 使用 GetStockObject 函数获取备用画笔,画刷,字体等句柄,获取字体句柄后,通过调用SelectObject函数把字体选入DC中,以后通过GDI函数进行文本绘制就会使用新的DC属性

ANSI_FIXED_FONT: // 等宽系统字体
ANSI_VAR_FONT // 变宽系统字体
DEVICE_DEFAULT_FONT // 设备默认字体
OEM_FIXED_FONT OEM等宽字体
SYSTEM_FONT // 系统字体
SYSTEM_FIXED_FONT // 等宽系统字体
HGDIOBJ SelectObject(HDC hdc, HGDIOBJ hgdiobj)
switch (uMsg)
{
    case WM_PAINT;
        hdc = BeginPaint(hwnd, &ps);
        SetBKMode(hdc, TRANSPARENT);
        SetTextColor(hdc,RGB(0,0,255));
        hFontOld = SelectObject(hdc,GetStockObject(OEM_FIXED_FONT)); // 使用OEM_FIXED_FONT字体
        for (int i =0;i<NUMLINES;i++)
        {
            y = 18 * i;
            TextOut(hdc,760,y,szBuf, wsprintf(szBuf, TEXT("%d"), GetSystemMetrics(METRICS[i].m_nIndex)));
        }
        SelectObject(hdc,hFontOld); // 还原为原始字体
        EndPaint(hwnd,&ps);
        return 0;
}

重新编译运行,效果稍微好了一点,但是备用字体比较少,我们可以创建自己的逻辑字体
CreateFont函数创建具有指定特征的逻辑字体

HFONT CreateFont(
    int nHeight; // 字符高度, 设置为0表示使用默认的字符高度
    int nWidth; // 字符宽度,通常设置为0,表示根据字符的高度来选择合适的字体
    int nEscapment // 字符串的倾斜角度,以0.1度为单位,一般为0
    int nOrientation; // 单个字符的倾斜角度,以0.1度为单位,会被忽略
    int fnWeight; // 字体粗细,如果设置为0,使用默认粗细
    DWORD fwdItalic, //是否斜体
    DWORD fdwUnderline // 是否有下划线
    DWORD fdwStrikeOut // 是否有删除线
    DWORD fdwCharSet // 字符集
    DWORD fdwOutputPrecision // 
    DWORD fdwQuality // 如何将逻辑字体属性与实际物理字体属性匹配
    DWORD fdwPitchAndFamily // 字符间距和字体系列
    LPCTSTR lpszFace // 字体名称,字符串长度不得超过LF_FACESIZE(32)个字符
)

前两个参数nHeight和nWidth都是逻辑单位

第5个参数fnWeidht指定字体的粗细,字体的粗细在0-1000之间,400是正常粗细,700是粗体,如果该参数设置为0,则使用默认字体粗细,wingdi.h头文件中定义了如下粗细常量

FW_DONTCARE 0 
FW_THIN 100
FW_EXTRALIGHT 200
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400
FW_REGULAR 400
FW_MEDIUM 500
FW_SEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800
FW_ULTRABOLD 800
FW_HEAVY 900
FW_BLACK 900

第9个参数fdwCharSet 指定字体的字符集,

OEM_CHARSET表示OEM字符集, 
DEFAULT_CHARSET 表示基于当前系统区域的字符集
ANSI_CHARSET 0 ANSI(美国,西欧)
GB2312_CHARSET 134 (简体中文)
CHINESEBIG5_CHARSET 136 繁体中文
DEFAULT_CHARSET 1 默认字符集
OEM_CHARSET 2 设备制造商字符集
SYMBOL_CHARSET 2 标准符号
MAC_CHARSET 77 Apple字符集

第12个参数fdwQuality 指定如何将逻辑字体属性与实际物理字体属性匹配,值的含义如下

ANTIALIASED_QUALITY 如果字体支持,并且字体大小不太小或太大,则字体是抗锯齿或平滑的
CLEARTYPE_QUALITY 使用 ClearType 抗锯齿方法显示文本
DEFAULT_QUALITY 字体的外观并不中哟
DRAFT_QUALITY 对于GDI光栅字体启用缩放,这意味着可以使用更多的字体大小,但质量可能更低
NONANTIALIASED_QUALITY 不会抗锯齿
PROFF_QUALITY 字体的字符质量比逻辑字体属性的精确匹配更重要,对于GDI光栅字体,禁用缩放,并选择最接近大小的字体,字体质量高,外形无变形

第13个参数fdwPitchAndFamily 指定字符间距和字体系列,最大两个位表示该字体是否一个等宽字体或是一个变宽字体, wingdi.h 头文件中定义了如下常量

DEFAULT_PITCH 0 默认间距
FIXED_PITCH 1 等宽
VARIABLE_PITCH 变宽

4-7位指定字体系列,值的含义如下所示

FF_DONTCHAR 使用默认字体
FF_ROMAN 具有可变笔画宽度字体
FF_SWISS 笔画宽度可变不带衬线的字体

CreateFont 虽然参数比较多,但是除了字符高度,字符集和字符名称外,其他参数通常设置为0,HFONT是逻辑字体句柄类型,如果函数执行成功,则返回值是所创建逻辑字体的句柄,如果执行失败,返回NULL

当不需要创建字体时,需要调用DeleteObject函数将其删除,它用于删除创建的逻辑画笔,逻辑画刷,逻辑字体,位图,区域等, 释放与对象有关的所有系统资源,对象删除后,指定的句柄不在有效

 BOOL deleteObject(HGDIOBJ hObject); // GDI 对象句柄

获取字体的度量值

GetTextMetrics 函数可以获取当前选定字体的度量值, 该函数通常用于英文字体

BOOL GetTextMetrics(
    HDC hdc
    LPTEXTMETRIC lptm // 在这个TEXTMETRIC 结构中返回字体度量值,逻辑单位
) 

函数执行成功,在lptm指定的TEXTMETRIC结构中返回字体信息,在wingdi.h 头文件中结构定义如下

typedef struct tagTEXTMETRIC {
    LONG tmHeight; // 字符高度(等于tmAscent + tmDescent)
    LONG tmAscent; // 字符基线以上的高度
    LONG tmDescent; // 字符基线以下的高度
    LONG tmInternalLeading // 字符高度范围内的一部分顶部空间,用于重音符号和其他音调符号
    LONG tmExternalLeading // 在行之间添加的额外高度空间
    LONG tmAveCharWidth // 字体中字符的平均宽度
    LONG tmMaxCharWidth // 字体中最宽字符的宽度
    LONG tmWeight // 字体的粗细
    LONG tmOverhang // 额外宽度
    TCHAR tmFirstChar // 第一个字符
    TCHAR tmLastChar // 最后一个字符
    TCHAR tmDefaultChar // 没有字符的替代字符
    TCHAR tmBreakChar // 单词之间的分割字符,通常是空格
    Byte tmItalic // 是否为斜体
    Byte tmUnderlined // 下划线
    Byte tmStruckOut;
    Byte tmPitchAndFamily 字符间距
    Byte tmCharSet; // 字符集
} TEXTMETRIC, *PTEXTMETRIC;

常用的字段包括 tmHeight, tmAscent, tmDescent, tmInternalLeading

绘制文本函数

DrawText DrawTextEx, TextOut ExtTextOut TabbedTextOut

当调用一个函数时候, 操作系统会将调用传递给 GDI 图形引擎,然后传递给设备驱动程序

ExtTextOut 函数执行速度最快, 调用快速转换成设备的ExtTextOut调用

要在指定的矩形区域范围内绘制文本,可以调用DrawText函数

要创建具有对齐列的多列文本,调用TabbedTextOut

DrawText 和 DrawTextEx 函数在指定的矩形内绘制文本

int DrawText(
    HDC hdc,
    LPCTSTR lpchText, 字符串指针
    int cchText, 字符串长度,以字符为单位
    LPRECT lpRect, // 限定在这个矩形范围内
}
int DrawTextEx(
    HDC hdc,
    LPCTSTR lpchText, 字符串指针
    int cchText, 字符串长度,以字符为单位
    LPRECT lpRect, // 限定在这个矩形范围内
    UINT uFormat // 绘制格式选项
    LPDRAWTEXTPARMS lpDTParams
}

uFormat 指定格式化文本的方法,常见的值如下

DT_TOP: 文本对齐到矩形的顶部
DT_BOTTOM 文本对齐到矩形底部,与DT_SINGLELINE单行文本一起使用
DT_VCENTER 垂直居中
DT_LEFT 左对齐
DT_RIGHT
DT_CENTER
DT_SINGLELINE
DT_WORDBREAK // 如果一个单词超过矩形的边界,自动断开到下一行
DT_EXPANDTABS // 

GetClientRect 函数

用于获取客户区的矩形坐标

BOOL WINAPI GetClientRect(
    HWND hWnd,
    LPRECT lpRect // 在这个RECT中返回客户区的坐标,以像素为单位
)

参数lpRect指向的RECT结构返回客户区的左上角和右下角的坐标,因为客户区坐标相对于窗口客户区左上角的,所以获取到的左上角坐标是(0,0)

加入标准滚动条

在窗口中加入一个标准滚动条, 只需要在CreateWindowEx 的dwStyle指定WS_HSCROLL / WS_VSCROLL样式即可加入水平/垂直滚动条

每个滚动条都有相应的范围和位置, 滚动条范围是一对整数,分别代表滚动条的最小值和最大值,位置是指滑块在范围中所处的值,当滑块在滚动条最顶端(或最左),滑块的值是范围的最小值,在最底部(或最右), 范围的最大值

标准滚动条的默认范围是 0 -100, 通过 SetScrollRange 函数设置范围

BOOL setScrollRange(
HWND hwnd, // 滚动条所在的窗口的句柄
int nBar,  // 要滚动的滚动条
int nMinPos // 最小滚动范围
int nMaxPos,  // 最大滚动范围
BOOL bRedraw // 是否应该重绘滚动条以反映更改
}

nBar 的参数如下所示

SB_HORZ // 设置标准水平滚动条的范围
SB_VERT 垂直滚动条的范围
SB_CTL 设置滚动条控件的范围

SetScrollPos 设置滚动条的位置

int SetScrollPos(
HWND hwnd,
int nBar
int nPos
BOOL bRedraw

WM_SIZE 消息

在WinMain中调用ShowWindow函数时, 在窗口大小更改后,窗口最小化, 最大化的时候,Windows都会发送WM_SIZE 消息到窗口过程

wParam 参数表示请求的大小调整类型, 值如下

SIZE_RESTORED // 窗口的大小已发生变化
Size_minimized // 窗口已最小化
Size_maximized // 窗口已最大化

WM_SIZE 消息的 lParam 参数表示窗口客户区新尺寸的段度和高度

cxClient = LOWORD(lParam); // 客户区的新宽度
cyClient = HIWORD(lParam); // 客户区的新高度

随着窗口大小的改变, 子窗口或子窗口控件通常也需要随之改变位置和大小,以适应新的客户区大小,例如, notepad 程序客户区中用于编辑文本的控件就是一个编辑控件,如果窗口大小改变,就需要响应WM_SIZE消息,重新计算客户区大小,对编辑控件的大小作出改变.

如果不在 WM_SIZE 消息中,可以通过调用 GetClientRect 函数获取客户区大小

WM_HSCROLL 消息

当窗口的标准水平滚动条发生滚动事件, 窗口过程会收到 WM_HSCROLL消息,垂直滚动条会收到WM_VSCROLL消息

WM_HSCROLL 消息的 wParam 参数表示滑块当前的位置和用户的滚动请求, 滚动请求值如下:

SB_LINELEFT: 向左滚动一个单位
SB_LINERIGHT 向右
SB_PAGELEFT 向左滚动一页
SB_PAGERIGHT
SB_THUMBPOSITION 用户拖动滑块并释放了鼠标, wParam的高位表示操作结束后滑块的位置

SB_THUMBTRACK 用户正在拖动滑块, 该消息会不断发送,直到用户释放鼠标, wParam的高位表示滑块被拖动的新位置
SB_LEFT 最左侧

SB_NEDSCROLL 滚动已结束

WM_VSCROLL 消息

滚动请求值如下

SB_LINEDOWN 向下滚动一个单位
SB_LINEUP 向上滚动一个单位
SB_PAGEDOWN 向下滚动一页
SB_PAGEUP 向上滚动一页

当用户按住水平或垂直滚动滑块进行滑动时, 程序通常处理的是 SB_THUMBTRACK 请求,而不是 SB_THUMBPOSITION 请求,以便用户拖动过程中, 客户区的内容可以实时改变

InvalidateRect 函数

向指定窗口的更新区域添加一个无效矩形

BOOL InvalidateRect(
    HWND hWnd,
    RECT *lpRect, //无效矩形,如果设置为NULL,则整个客户区都是无效去腥
    BOOL bErase, // 是否擦除更新区域的背景
)

InavlidateRect 函数会导致客户区出现一个无效矩形,如果客户区存在无效矩形,Windows会发送WM_PAINT消息到窗口过程

配合UpdateWindows,把WM_PAINT消息直接发送到窗口过程,绕过应用程序的消息队列,来立即刷新客户区

ValidateRect,从指定窗口的更新区域删除一个矩形区域

SetScrollInfo 和 GetScrollInfo 函数

SetScrollInfo 设置滚动条的参数

int SetScrollInfo(
    HWND hwnd,
    int fnBar, // 要设置的滚动条
    LPCSCROLLINFO losi, 指定滚动条的参数
    BOOL fRedraw // 是否要重绘滚动条以反映滚动条的更改
)

GetScrollInfo 获取滚动条的参数,包括最大,最小滚动范围,页面大小,滑块位置以及滑块即是位置

根据客户区内容调整窗口大小

// 设置窗口的属性
LONG_PTR SetWindowLongPtr(
    HWND hwnd,
    int nIndex // 要设置哪一项
    LONG_PTR dwNewLong // 新值
)

属性列表

GWLP_WINPROC // 设置窗口过程的指针
GWLP_USERDATA // 用户数据
GWLP_ID // 窗口的ID
GWLP_HINSTANCE // 实例句柄
GWL_STYLE // 窗口样式
// 获取窗口的属性
GetWindowLongPtr(
    HWND hwnd,
    int nIndex
)
//根据客户区大小计算所需窗口的大小
BOOL AdjustWindowRectEx(
    LPRECt lpRect, 
    DWORD dwStyle // 窗口样式
    BOOL bMenu // 窗口是否有菜单
    DWROD dwExStyle // 扩展样式
)
// 设置窗口的大小,位置和Z顺序
SetWindowPos(
    HWND hwnd,
    HWND hWndInsertAfter, // 指定窗口句柄或一些预定义值
    int x
    int y
    int cx
    int cy
    UINT uFlags
)
// 更改窗口的尺寸和位置
BOOL MoveWindow(
    HWND hwnd,
    int x,
    int y
    int nWidth
    int nHeight
    BOOL bRepaint
)

保存设备环境

int SaveDC(HDC hdc);
BOOL RestoreDC(
    HDC hdc,
    int nSavedDC
)

绘制像素点

COLORREF SetPixel(HDC hdc, int X, int y, COLORREF crColor)
COLORREF GetPixel(HDC hdc, int x, int y)

COLORREF: RGB(255,0,0)

绘制直线

LineTo, Polyline, PolylineTo PolyPolyLine

绘制曲线

Arc ArcTo PolyBezier PolyBezierTo

填充图形

// 直角矩形
BOOL Rectangle(
HDC hdc,
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect
)
//圆角矩形
BOOL RoundRect(
HDC hdc
int nLeftRect
int nTopRect
int nRightRect
int nBottomRect
int nWidth
int nHeight
)
// 椭圆
BOOL Ellipse(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)

原文链接:https://juejin.cn/post/7321922279557546020 作者:Bowen_J

(0)
上一篇 2024年1月10日 上午10:48
下一篇 2024年1月10日 上午10:58

相关推荐

发表回复

登录后才能评论