使用 gdi 在 winapi 窗口中的运行时更新颜色

Posted

技术标签:

【中文标题】使用 gdi 在 winapi 窗口中的运行时更新颜色【英文标题】:update color in runtime in winapi window using gdi 【发布时间】:2021-08-18 16:14:41 【问题描述】:

我试图在我的 ui 窗口内的一个特殊矩形中显示用户输入的颜色。 到目前为止,我能够读出用户颜色输入,但不知何故,我绘制的矩形的颜色没有改变。 我尝试使用 InvalidateRect(wnd, NULL, TRUE) 导致窗口重绘,但它不起作用。

似乎我不了解有关 winapi 运行时功能的基本知识。这是我的 cmets 代码:

#include <windows.h>
#include <shlobj.h>
#include <string>
#include <iostream>

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

void createEditProc(HWND);

HWND hColorInput;
WNDPROC ColorEditProc;

struct color 
    float r;
    float g;
    float b;
;
color current_color =  1, 0.5, 1 ;


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) 
    WNDCLASSW wc =  0 ;
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"myWindowClass";
    wc.lpfnWndProc = WindowProcedure;

    if (!RegisterClassW(&wc))
        return -1;

    HWND hWnd = CreateWindowW(L"myWindowClass", L"My Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 570, 330, NULL, NULL, NULL, NULL);

    MSG msg =  0 ;
    while (GetMessage(&msg, NULL, NULL, NULL))
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);

    
    

    return 0;



LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)

    switch (msg)
    
    case WM_CREATE:
        createEditProc(hWnd);
        break;

    case WM_PAINT:
        
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        HBRUSH newBrush = CreateSolidBrush(RGB(floor(current_color.r * 255), floor(current_color.g * 255), floor(current_color.b * 255)));
        HGDIOBJ oldBrush = SelectObject(hdc, newBrush);
        Rectangle(hdc, 10,  10, 200, 200);
        SelectObject(hdc, oldBrush);
        EndPaint(hWnd, &ps);
        
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProcW(hWnd, msg, wp, lp);
    



std::string read_textInput(HWND hEdit) 
    wchar_t colorcode0[1000];
    GetWindowTextW(hEdit, colorcode0, 1000);
    std::wstring ws(colorcode0);
    std::string colorcode(ws.begin(), ws.end());
    return colorcode;



LRESULT CALLBACK subEditProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)

    switch (msg)
    
    case WM_KEYDOWN:
        switch (wParam)
        
        case VK_RETURN:
        
            std::string color_text = read_textInput(hColorInput);
            char* cstr = new char[color_text.length() + 1];
            strcpy_s(cstr, color_text.length() + 1, color_text.c_str());
            int r, g, b;
            sscanf_s(cstr, "%02x%02x%02x", &r, &g, &b);
            current_color.r = r / 255.;
            current_color.g = g / 255.;
            current_color.b = b / 255.;
            InvalidateRect(wnd, NULL, TRUE); // is it correct? 
        
        break;  //or return 0; if you don't want to pass it further to def proc. If not your key, skip to default:
        
    default:
        return CallWindowProc(ColorEditProc, wnd, msg, wParam, lParam);
    
    return 0;



void createEditProc(HWND hWnd)

    hColorInput = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL, 465, 30, 65, 25, hWnd, NULL, NULL, NULL);
    ColorEditProc = (WNDPROC)SetWindowLongPtr(hColorInput, GWLP_WNDPROC, (LONG_PTR)subEditProc);

函数subEditProc在用户按下回车后读取用户输入的十六进制颜色代码并将其放入current_color结构中。

在我的WM_PAINT 函数中,Rectangle 是用这种颜色的newBrush 绘制的。不知何故,尽管当用户点击 enter 时我的 current_color 确实正确更改,但它从未在 newBrush 中更新。

据我了解,我应该在某处添加重绘功能。可以借助 redrawwindow 函数。但奇怪的是,我从未遇到过使用此功能的工作示例。

我在处理消息WM_KEYDOWN 时添加了InvalidateRect(wnd, NULL, TRUE)。但我不认为这是正确的。此外,当我更改窗口大小并调用消息WM_PAINT 时,它不会更改颜色。

【问题讨论】:

InvalidateRect 的第一个参数应该是传递编辑窗口句柄时要重绘的窗口的句柄。 另外,每次执行 WM_PAINT 处理程序时都会泄漏一个画笔对象。使用完画笔后调用DeleteObject,或者使用GetStockObject(DC_BRUSH)SetDCBrushColor 而不是创建实心画笔对象。 感谢 cmets。我纠正了舔。关于 InvalidateRect 中的正确句柄...我认为它得到了wnd 句柄。然后这个wnd 句柄参与createEditProc 函数,该函数与hWnd 句柄(主窗口的句柄)一起使用? 传入子类控件的自定义窗口过程的wnd与传入常规窗口过程的hWnd无关。您将不得不以一种或另一种方式获取编辑控件的父窗口句柄。然而,更容易的是,甚至不要子类化编辑控件,并在父窗口中处理 EN_CHANGE 通知。 我听从了你的第一个建议,它奏效了!非常喜欢。我发布了一个带有更新的工作代码的答案。 【参考方案1】:

刚刚理解了我的错误,在这里我为需要这种功能的人发布了一个工作示例:

#include <windows.h>
#include <shlobj.h>
#include <string>
#include <iostream>

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

void createEditProc(HWND);

HWND hColorInput, hWnd, hMainWindow;
WNDPROC ColorEditProc;

struct color 
    float r;
    float g;
    float b;
;
color current_color =  1, 0.5, 1 ; // We set initial color of the window.

// Here is the main window. Here I define the handle hMainWindow to repaint it later.

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE PrevInst, LPSTR args, int ncmdshow) 
    WNDCLASSW wc =  0 ;
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInst;
    wc.lpszClassName = L"myWindowClass";
    wc.lpfnWndProc = WindowProcedure;

    if (!RegisterClassW(&wc))
        return -1;

    hMainWindow = CreateWindowW(L"myWindowClass", L"My Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 570, 330, NULL, NULL, NULL, NULL);

    MSG msg =  0 ;
    while (GetMessage(&msg, NULL, NULL, NULL))
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);

    
    

    return 0;



// WinMain call back function

LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)

    switch (msg)
    
    case WM_CREATE:
        createEditProc(hWnd);
        break;

    case WM_PAINT:
        
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        HBRUSH newBrush = CreateSolidBrush(RGB(floor(current_color.r * 255), floor(current_color.g * 255), floor(current_color.b * 255)));
        HGDIOBJ oldBrush = SelectObject(hdc, newBrush);
        Rectangle(hdc, 10,  10, 200, 200);
        SelectObject(hdc, oldBrush);
        DeleteObject(newBrush);
        EndPaint(hWnd, &ps);
        
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProcW(hWnd, msg, wp, lp);
    



std::string read_textInput(HWND hEdit) 
    wchar_t colorcode0[1000];
    GetWindowTextW(hEdit, colorcode0, 1000);
    std::wstring ws(colorcode0);
    std::string colorcode(ws.begin(), ws.end());
    return colorcode;



LRESULT CALLBACK subEditProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)

    switch (msg)
    
    case WM_KEYDOWN:
        switch (wParam)
        
        case VK_RETURN:
        
            std::string color_text = read_textInput(hColorInput);
            char* cstr = new char[color_text.length() + 1];
            strcpy_s(cstr, color_text.length() + 1, color_text.c_str());
            int r, g, b;
            sscanf_s(cstr, "%02x%02x%02x", &r, &g, &b);
            current_color.r = r / 255.;
            current_color.g = g / 255.;
            current_color.b = b / 255.;
            InvalidateRect(hMainWindow, NULL, TRUE); // here I pass hMainWindow handle causing it to repaint.
        
        break; 
        
    default:
        return CallWindowProc(ColorEditProc, wnd, msg, wParam, lParam);
    
    return 0;



void createEditProc(HWND hWnd)

    hColorInput = CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL, 465, 30, 65, 25, hWnd, NULL, NULL, NULL);
    ColorEditProc = (WNDPROC)SetWindowLongPtr(hColorInput, GWLP_WNDPROC, (LONG_PTR)subEditProc);



【讨论】:

以上是关于使用 gdi 在 winapi 窗口中的运行时更新颜色的主要内容,如果未能解决你的问题,请参考以下文章

WinAPI/GDI:如何使用 GetDIBits() 为位图合成颜色表?

是否可以使用 WINAPI 或 GDI+ 在 C++ 中创建 CommandLink 对话框?

如何在纯 C++ 中高效快速地清理我的 GDI 对象 - winapi(不是 .net,c#)?

如何在 WINAPI GDI 中制作淡入淡出效果?

在运行时更新 PNG 图像会引发 GDI+ 溢出异常

GDI 中的多边形函数