使用 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 对话框?