为了在处理WM_PAINT
消息时更新窗口中显示的文本,您需要有一些源来显示文本字符串。
由于您的原始帖子有些陈旧,Windows API 已随着新版本的 Windows 发生变化,当前版本的 Windows 10 和 Windows 11 已经处于测试阶段。
Windows 因为 Windows XP 是 WinAPI 的 16 位 UNICODE,所以人们大多使用wchar_t
文本字符。这要求文本字符串常量需要L
修饰符,如L"wchar_t text"
.
使用 Visual Studio 2019,我整理了一个在 Windows 10 上运行的简单示例。这是一个简单的 Windows WinAPI 桌面 GUI 应用程序。我从 Visual Studio 中的一个新项目开始,并让 IDE 为带有wWinMain()
、MyRegisterClass()
、InitInstance()
和WndProc()
.
然后我修改了生成的源代码以执行以下操作:
- 在主窗口中出现四个按钮以允许数据更改
- 显示两个文本字符串,这些字符串使用按钮点击次数进行更新
我选择使用默认字体,所以没有做任何修改用于显示文本的字体。如果您需要修改字体,您需要添加代码来创建您想要的字体,选择新字体到HDC
用于绘制文本,然后使用TextOut()
新字体绘制文本。使用字体后,您需要将其换回,然后将其删除。
第一步是创建一个用于管理按钮和按钮点击的数据区域。我选择在InitInstance()
.
static struct {
const wchar_t* txt; // pointer to text to display on button face
int iCount; // count of number of times button clicked
HWND hwnd; // button window handle which identifies the button
} myButtons[] = {
{L"Points up", 0, 0},
{L"Points dwn", 0, 0},
{L"Level up", 0, 0},
{L"Level dwn", 0, 0}
};
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
// create the displayed window along with the buttons.
// the buttons are in a single row at the top of the window.
POINT myPoint = { 10, 10 }; // x, y
for (auto &a : myButtons) {
a.hwnd = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
a.txt, // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
myPoint.x, // x position
myPoint.y, // y position
100, // Button width
50, // Button height
hWnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
myPoint.x += 100 + 20; // button width plus a separation distance
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
更新显示窗口的源代码如下。我们有两个函数,一个按钮单击处理程序来确定单击了哪个按钮,以及一个修改显示窗口WndProc()
的消息处理程序。WM_PAINT
// process a button click event and return an indication
// whether the button handle matches one we are managing (1)
// or not managing (0).
int buttonClick(HWND hWnd, HWND hButton)
{
// look through the list of buttons to see if the window handle
// of the button event matches one of our buttons.
for (auto &a : myButtons) {
if (a.hwnd == hButton) {
// this is one of our buttons so we increment button click count.
// then invalidate the window area and update to trigger WM_PAINT message.
a.iCount++;
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
return 1; // indicate we processed this event.
}
}
return 0; // indicate we did not process this event
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmCode = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
// not a menu event so see if it is a button click or not.
if (wmCode == BN_CLICKED) {
// if we are managing this button then we skip
// the DefWindowProc() otherwise it is called.
if (buttonClick(hWnd, (HWND)lParam))
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
// create the text strings we are going to display/update
wchar_t myText[2][64];
// following swprintf_s() works because template
// generates the proper call with the additional buffer
// size argument.
swprintf_s(myText[0], L"Points: %d", myButtons[0].iCount - myButtons[1].iCount);
swprintf_s(myText[1], L"Level: %d", myButtons[2].iCount - myButtons[3].iCount);
// get the text metrics of the font we are using to draw the text so
// that we can find out how tall the letters are and can adjust the
// distance for each line of text properly.
TEXTMETRIC myTextMetric = { 0 };
GetTextMetrics(hdc , &myTextMetric);
// we will use a POINT struct for maintaining the point at which
// the text output will start. x coordinate is horizontal position
// and y coordinate is the vertical position.
POINT myPoint = { 10, 150 }; // x, y
int myMargin = 5;
// iterate over the list of strings we are displaying and
// display each one on a separate line.
for (auto &a : myText) {
TextOut(hdc, myPoint.x, myPoint.y, a, wcslen(a));
myPoint.y += myTextMetric.tmHeight + myMargin;
}
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}