首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Win32 開發入門(10):繪圖進階——簡易畫圖程式實作

     編輯:關於C++

填充圖形與畫刷操作

使用 SetDCBrushColor 設定裝置內容的預設畫刷顏色,但必須先將 DC_BRUSH 選入 DC:

SelectObject(hdc, GetStockObject(DC_BRUSH));
SetDCBrushColor(hdc, RGB(0, 0, 255)); // 藍色
Rectangle(hdc, 20, 18, 68, 50);

每次呼叫 SetDCBrushColor 即改變後續繪圖的填充顏色。

多種填充圖形示範

簡易畫圖程式:滑鼠繪製多條線段

核心設計

  1. 選單選擇線條樣式(實線、虛線、點線、虛點線)
  2. 滑鼠左鍵按下記錄起點,放開記錄終點並繪製
  3. WM_PAINT 重繪所有已儲存的線段
  4. 使用 std::vector 儲存每條線的起點、終點、線型

選單建立 (WM_CREATE)

case WM_CREATE: {
    HMENU menubar = CreateMenu();
    HMENU menuPop = CreatePopupMenu();
    AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_SOLID, L"實線");
    AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DASH, L"虛線");
    AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DOT, L"點線");
    AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DASHDOT, L"點虛線");
    AppendMenu(menubar, MF_STRING|MF_POPUP, (UINT_PTR)menuPop, L"選擇線型");
    SetMenu(hwnd, menubar);
} return 0;

選單 ID 直接對應 CreatePen 的線型常數,簡化程式碼。

選單命令處理 (WM_COMMAND)

case WM_COMMAND: {
    HMENU mnbar = GetMenu(hwnd);
    HMENU hmnPop = GetSubMenu(mnbar, 0);
    CheckMenuRadioItem(hmnPop, PS_SOLID, PS_DASHDOT, LOWORD(wParam), MF_BYCOMMAND);
    penStyle = (int)LOWORD(wParam); // 記錄使用者選擇
} return 0;

滑鼠事件處理

case WM_LBUTTONDOWN: {
    pCurrentData = new PAINTDATA;
    pCurrentData->penStyle = penStyle;
    pCurrentData->ptBeginX = GET_X_LPARAM(lParam);
    pCurrentData->ptBeginY = GET_Y_LPARAM(lParam);
} return 0;

case WM_LBUTTONUP: {
    if (pCurrentData) {
        pCurrentData->ptEndX = GET_X_LPARAM(lParam);
        pCurrentData->ptEndY = GET_Y_LPARAM(lParam);
        
        // 即時繪製
        HDC hdc = GetDC(hwnd);
        HPEN pen = CreatePen(pCurrentData->penStyle, 1, RGB(0,255,0));
        HPEN oldpen = (HPEN)SelectObject(hdc, pen);
        MoveToEx(hdc, pCurrentData->ptBeginX, pCurrentData->ptBeginY, NULL);
        LineTo(hdc, pCurrentData->ptEndX, pCurrentData->ptEndY);
        SelectObject(hdc, oldpen);
        DeleteObject(pen);
        ReleaseDC(hwnd, hdc);
        
        datas.push_back(*pCurrentData);
    }
} return 0;

重繪所有線段 (WM_PAINT)

case WM_PAINT: {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    for (auto& item : datas) {
        HPEN pen = CreatePen(item.penStyle, 1, RGB(0,255,0));
        SelectObject(ps.hdc, pen);
        MoveToEx(ps.hdc, item.ptBeginX, item.ptBeginY, NULL);
        LineTo(ps.hdc, item.ptEndX, item.ptEndY);
        DeleteObject(pen);
    }
    EndPaint(hwnd, &ps);
} return 0;

線型選單

畫圖程式執行畫面1

畫圖程式執行畫面2

完整程式碼清單

#include <Windows.h>
#include <WindowsX.h>
#include <vector>
using namespace std;

typedef struct tagData {
    int ptBeginX, ptBeginY;
    int ptEndX, ptEndY;
    int penStyle;
} PAINTDATA;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hThisApp, HINSTANCE hPrevApp, LPSTR lpsCmd, int nShow) {
    WNDCLASS wc = {};
    wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
    wc.hInstance = hThisApp;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = L"My";
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);
    
    HWND hwnd = CreateWindow(L"My", L"應用程式", WS_OVERLAPPEDWINDOW,
                             50, 20, 600, 480, NULL, NULL, hThisApp, NULL);
    if (hwnd == NULL) return -1;
    ShowWindow(hwnd, nShow);
    UpdateWindow(hwnd);
    
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static vector<PAINTDATA> datas;
    static int penStyle = PS_SOLID;
    static PAINTDATA* pCurrentData = nullptr;
    
    switch (msg) {
        case WM_CREATE: {
            HMENU menubar = CreateMenu();
            HMENU menuPop = CreatePopupMenu();
            AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_SOLID, L"實線");
            AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DASH, L"虛線");
            AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DOT, L"點線");
            AppendMenu(menuPop, MF_STRING, (UINT_PTR)PS_DASHDOT, L"點虛線");
            AppendMenu(menubar, MF_STRING|MF_POPUP, (UINT_PTR)menuPop, L"選擇線型");
            SetMenu(hwnd, menubar);
        } return 0;
        
        case WM_COMMAND: {
            HMENU mnbar = GetMenu(hwnd);
            HMENU hmnPop = GetSubMenu(mnbar, 0);
            CheckMenuRadioItem(hmnPop, PS_SOLID, PS_DASHDOT, LOWORD(wParam), MF_BYCOMMAND);
            penStyle = (int)LOWORD(wParam);
        } return 0;
        
        case WM_LBUTTONDOWN: {
            pCurrentData = new PAINTDATA;
            pCurrentData->penStyle = penStyle;
            pCurrentData->ptBeginX = GET_X_LPARAM(lParam);
            pCurrentData->ptBeginY = GET_Y_LPARAM(lParam);
        } return 0;
        
        case WM_LBUTTONUP: {
            if (pCurrentData) {
                pCurrentData->ptEndX = GET_X_LPARAM(lParam);
                pCurrentData->ptEndY = GET_Y_LPARAM(lParam);
                
                HDC hdc = GetDC(hwnd);
                HPEN pen = CreatePen(pCurrentData->penStyle, 1, RGB(0,255,0));
                HPEN oldpen = (HPEN)SelectObject(hdc, pen);
                MoveToEx(hdc, pCurrentData->ptBeginX, pCurrentData->ptBeginY, NULL);
                LineTo(hdc, pCurrentData->ptEndX, pCurrentData->ptEndY);
                SelectObject(hdc, oldpen);
                DeleteObject(pen);
                ReleaseDC(hwnd, hdc);
                
                datas.push_back(*pCurrentData);
            }
        } return 0;
        
        case WM_PAINT: {
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            for (auto& item : datas) {
                HPEN pen = CreatePen(item.penStyle, 1, RGB(0,255,0));
                SelectObject(ps.hdc, pen);
                MoveToEx(ps.hdc, item.ptBeginX, item.ptBeginY, NULL);
                LineTo(ps.hdc, item.ptEndX, item.ptEndY);
                DeleteObject(pen);
            }
            EndPaint(hwnd, &ps);
        } return 0;
        
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
            
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

重點整理

  • DC_BRUSH + SetDCBrushColor 快速變更填充顏色
  • 滑鼠繪圖三步驟:按下記錄起點 → 放開記錄終點並繪製 → 重繪時遍歷向量重畫
  • 選單 ID 對應 PS_* 常數,簡化畫筆建立邏輯
  • static 變數延長生命週期至程式結束
  • GDI 物件用完必須 DeleteObject 釋放
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved