抖音热门数字时钟设计

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(&currentTime);
            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(&currentTime);
            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基础知识之后我们来绘制一个数字时钟

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。