win32GDI项目数字时钟
前言:基于win32GDI绘图制作抖音热门数字时钟
定时器
开启定时器
//定义定时器ID
#define IDT_TIME 1001
//设置定时器
SetTimer(hWnd
, IDT_TIME //定时器ID
, 1000 //定时间隔 单位毫秒ms
, NULL); //参数为NULL时,定时向程序投递WM_TIMER消息
匹配定时器消息
case WM_TIMER:
switch (wParam)
{
case IDT_TIME:
{
HDC hdc = GetDC(hWnd);
static int y = 0;
TextOut(hdc, 0, y += 20, L"定时器开启第",6);
ReleaseDC(hWnd, hdc);
}
break;
default:
break;
}
break;
关闭定时器
KillTimer(hWnd, IDT_TIME);
系统时间
系统时间结构体 SYSTEMTIME
获取系统时间
wchar_t szTime[100];
SYSTEMTIME currentTime;
GetLocalTime(¤tTime);
wsprintf(szTime, L"%.2d %.2d %.2d %.2d:%.2d:%.2d\n", currentTime.wYear,
currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond);
TextOut(hdc, 0, 0, szTime, wcslen(szTime) - 1);
定时器+时钟 = 简陋的数字时钟
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
wchar_t szTime[100];
SYSTEMTIME currentTime;
switch (message)
{
case WM_CREATE:
SetTimer(hWnd, IDT_TIME, 1000, NULL);
break;
case WM_RBUTTONDOWN:
KillTimer(hWnd, IDT_TIME);
break;
case WM_TIMER:
switch (wParam)
{
case IDT_TIME:
{
HDC hdc = GetDC(hWnd);
GetLocalTime(¤tTime);
wsprintf(szTime, L"%.2d %.2d %.2d %.2d:%.2d:%.2d\n", currentTime.wYear,
currentTime.wMonth, currentTime.wDay, currentTime.wHour, currentTime.wMinute, currentTime.wSecond);
TextOut(hdc, 0, 0, szTime, wcslen(szTime) - 1);
ReleaseDC(hWnd, hdc);
}
break;
default:
break;
}
break;
case WM_COMMAND:
break;
case WM_PAINT:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
效果显示
去标题栏
创建窗口的时候自定义窗口风格
// 创建主窗口
HWND hwnd = CreateWindowEx(
0, // dwExStyle,扩展样式
szClassName, // lpClassName,类名
L"时钟", // lpWindowName,标题
WS_POPUP|WS_SYSMENU|WS_SIZEBOX, // dwStyle,窗口风格 WS_POPUP|WS_SYSMENU去标题栏所需宏
100, // X,初始 X 坐标
100, // Y,初始 Y 坐标
300, // nWidth,宽度
300, // nHeight,高度
NULL, // hWndParent,父窗口句柄
NULL, // hMenu,菜单句柄
hInstance, // hlnstance,程序实例句柄
NULL); // lpParam,用户数据
移动窗口
截取DefWindowProc消息
case WM_NCHITTEST:
{
UINT nHitTest;
//截取DefWindowProc消息
nHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
//如果鼠标左键按下 GetAsyncKeyState函数的返回值小于0
if (nHitTest == HTCLIENT && GetAsyncKeyState(MK_LBUTTON) < 0)
{
//改变DefWindowProc消息
nHitTest = HTCAPTION;
}
return nHitTest;
}
break;
给系统菜单追加菜单项
定义菜单ID
#define IDM_HELP 1001
#define IDM_TOPMOST 1002
追加菜单
case WM_CREATE:
{
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE); //获取系统菜单
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL); //添加分割线
AppendMenu(hSysMenu, MF_STRING, IDM_TOPMOST, L"总是在前");
AppendMenu(hSysMenu, MF_STRING, IDM_HELP, L"帮助");
}
右键菜单
这个不能算是右键菜单,因为截取了DefWindowProc消息,多以对客户区的右键相当于对标题栏的右键弹出的是系统菜单
case WM_CONTEXTMENU:
{
POINT pt;
//lParam包含了鼠标的位置消息 低位x,高位y
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
//获取系统菜单
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
//弹出系统菜单
int nId = TrackPopupMenu(hSysMenu, TPM_LEFTALIGN | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL);
if (nId > 0)
SendMessage(hwnd, WM_SYSCOMMAND, nId, 0);
return 0;
}
break;
对菜单操作进行响应
全局添加是否顶置标识符
static int s_bTopMost = 0;
响应菜单
注册窗口的时候窗口风格必须带上WS_SYSMENU
case WM_SYSCOMMAND:
{
int nId = wParam;
switch (nId)
{
case IDM_HELP:
MessageBox(hwnd, L"一个小时钟程序", L"帮助", 0);
break;
case IDM_TOPMOST://窗口顶置
{
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
if (s_bTopMost)
{
//取消选中
CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_UNCHECKED);
//取消顶置窗口
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
s_bTopMost = FALSE;
}
else
{
//选中
CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_CHECKED);
//顶置
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
s_bTopMost = TRUE;
}
return DefWindowProc(hwnd, WM_SYSCOMMAND, nId, 0);
}
break;
完整代码显示
#include <windows.h>
#define IDM_HELP 1001
#define IDM_TOPMOST 1002
// 窗口函数的函数原形
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WCHAR szClassName[] = L"MainWClass";//窗口类的名称
WNDCLASSEX wndclass;
// 用描述主窗口的参数填充WNDCLASSEX结构
wndclass.cbSize = sizeof(wndclass); // 结构的大小
wndclass.style = CS_HREDRAW | CS_VREDRAW; // 指定如果大小改变就重画
wndclass.lpfnWndProc = MainWndProc; // 窗口函数指针
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 = szClassName; // 窗口类的名称
wndclass.hIconSm = NULL; // 没有类的小图标
// 注册这个窗口类
RegisterClassEx(&wndclass);
// 创建主窗口
HWND hwnd = CreateWindowEx(
0, // dwExStyle,扩展样式
szClassName, // lpClassName,类名
L"时钟", // lpWindowName,标题
WS_POPUP|WS_SYSMENU|WS_SIZEBOX, // dwStyle,窗口风格
100, // X,初始 X 坐标
100, // Y,初始 Y 坐标
300, // nWidth,宽度
300, // nHeight,高度
NULL, // hWndParent,父窗口句柄
NULL, // hMenu,菜单句柄
hInstance, // hlnstance,程序实例句柄
NULL); // lpParam,用户数据
if (hwnd == NULL)
{
MessageBox(NULL, L"创建窗口出错!", L"error", MB_OK);
return -1;
}
// 显示窗口,刷新窗口客户区
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 从消息堆中取出消息
MSG msg;
while (::GetMessage(&msg, NULL, 0, 0))
{
// 转化键盘消息
TranslateMessage(&msg);
// 将消息发送到相应的窗口函数
DispatchMessage(&msg);
}
// 当GetMessage返回0时程序结束
return msg.wParam;
}
static int s_bTopMost = 0;
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
TCHAR szText[] = L"第一个win32程序!";
switch (message)
{
case WM_CREATE:
{
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL); //添加分割线
AppendMenu(hSysMenu, MF_STRING, IDM_TOPMOST, L"总是在前");
AppendMenu(hSysMenu, MF_STRING, IDM_HELP, L"帮助");
}
break;
case WM_NCHITTEST:
{
UINT nHitTest;
//截取DefWindowProc消息
nHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
//如果鼠标左键按下 GetAsyncKeyState函数的返回值小于0
if (nHitTest == HTCLIENT && GetAsyncKeyState(MK_LBUTTON) < 0)
{
//改变DefWindowProc消息
nHitTest = HTCAPTION;
}
return nHitTest;
}
break;
case WM_CONTEXTMENU:
{
POINT pt;
//lParam包含了鼠标的位置消息 低位x,高位y
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
//获取系统菜单
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
//弹出系统菜单
int nId = TrackPopupMenu(hSysMenu, TPM_LEFTALIGN | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL);
if (nId > 0)
SendMessage(hwnd, WM_SYSCOMMAND, nId, 0);
return 0;
}
break;
case WM_SYSCOMMAND:
{
int nId = wParam;
switch (nId)
{
case IDM_HELP:
MessageBox(hwnd, L"一个小时钟程序", L"帮助", 0);
break;
case IDM_TOPMOST://窗口顶置
{
HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
if (s_bTopMost)
{
//取消选中
CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_UNCHECKED);
//取消顶置窗口
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
s_bTopMost = FALSE;
}
else
{
//选中
CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_CHECKED);
//顶置
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
s_bTopMost = TRUE;
}
return DefWindowProc(hwnd, WM_SYSCOMMAND, nId, 0);
}
break;
default:
break;
}
}
break;
case WM_PAINT: // 窗口客户区需要重画
{
HDC hdc;
PAINTSTRUCT ps;
// 使无效的客户区变的有效,并取得设备环境句柄
hdc = BeginPaint(hwnd, &ps);
// 显示文字
//TextOut(hdc, 10, 10, szText, wcslen(szText));
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY: // 正在销毁窗口
// 向消息队列投递一个WM_QUIT消息,促使GetMessage函数返回0,结束消息循环
PostQuitMessage(0);
return 0;
}
// 将我们不处理的消息交给系统做默认处理
return DefWindowProc(hwnd, message, wParam, lParam);
}
设置坐标
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
//获取客户区大小
RECT rt;
GetClientRect(hWnd, &rt);
int cx = rt.right;
int cy = rt.bottom;
SetViewportExtEx(hdc, cx, cy, NULL); //设置x,y坐标轴
SetViewportOrgEx(hdc, 0, 0, NULL); //设置原点
EndPaint(hWnd, &ps);
}
了解这些win32基础知识之后我们来绘制一个数字时钟