为什么在更改 WinAPI 控件的大小后不调用WM_NCCALSIZE?
Why is WM_NCCALSIZE is not called after the size of WinAPI control is changed?
我有一个丰富的编辑控件,创建后其大小会更改。
我想自定义控件的外观,因此每次更改控件的大小时,都需要重新计算其工作区的大小。想法是在每次组件收到消息时更改工作区的大小WM_NCCALCSIZE
因为 MSDN 文档WM_NCCALCSIZE
指出:
在必须计算窗口工作区的大小和位置时发送。通过处理此消息,应用程序可以在窗口的大小或位置更改时控制窗口工作区的内容。
问题是在更改控件大小后不调用WM_NCCALCSIZE
(仅在创建控件时调用一次(。
此行为并非特定于丰富的编辑控件。同样的情况也是如此,例如按钮控件。在组件显示在屏幕上之前还是之后更改大小也无关紧要。
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <string>
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker,""/manifestdependency:type='win32'
name='Microsoft.Windows.Common-Controls' version='6.0.0.0'
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"")
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK RichEditProc(HWND, UINT, WPARAM, LPARAM);
WNDPROC richEditOrigProc;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
LoadLibrary(TEXT("msftedit.dll"));
WNDCLASSEX mainwcex;
mainwcex.cbSize = sizeof(WNDCLASSEX);
mainwcex.style = CS_HREDRAW | CS_VREDRAW;
mainwcex.lpfnWndProc = WindowProc;
mainwcex.cbClsExtra = 0;
mainwcex.cbWndExtra = 0;
mainwcex.hInstance = hInstance;
mainwcex.hIcon = NULL;
mainwcex.hCursor = (HICON)LoadCursor(NULL, IDC_ARROW);
mainwcex.hbrBackground = GetSysColorBrush(COLOR_MENU);
mainwcex.lpszMenuName = NULL;
mainwcex.lpszClassName = "mainwindow";
mainwcex.hIconSm = NULL;
RegisterClassEx(&mainwcex);
HWND mainWindow = CreateWindowEx(
NULL,
"mainwindow",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
HWND richEditControl = CreateWindowEx(
NULL,
"RICHEDIT50W",
"Rich Edit",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP,
50,
50,
100,
25,
mainWindow,
NULL,
hInstance,
NULL);
richEditOrigProc = (WNDPROC) SetWindowLongPtr(richEditControl, GWLP_WNDPROC, (LONG_PTR) RichEditProc);
// Changes the width of rich edit control from 100 px to 400 px.
SetWindowPos(richEditControl, NULL, 0, 0, 400, 25,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
ShowWindow(mainWindow, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(mainWindow, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK RichEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_NCCALCSIZE:
{
RECT newClientRect;
GetWindowRect(hWnd, &newClientRect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), reinterpret_cast<POINT*>(&newClientRect), 2);
InflateRect(&newClientRect, -3, -3);
int width = newClientRect.right - newClientRect.left;
int height = newClientRect.bottom - newClientRect.top;
if (wParam) {
NCCALCSIZE_PARAMS* ncParams = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
ncParams->rgrc[0].left = newClientRect.left;
ncParams->rgrc[0].top = newClientRect.top;
ncParams->rgrc[0].right = newClientRect.right;
ncParams->rgrc[0].bottom = newClientRect.bottom;
return WVR_HREDRAW;
}
return 0;
}
}
return CallWindowProc(richEditOrigProc, hWnd, uMsg, wParam, lParam);
}
在此示例中,很明显,虽然丰富编辑控件的总大小发生了更改,但它的工作区不会重新计算,而是对应于其初始大小。
调整自定义控件工作区大小以反映其总大小变化的正确方法是什么。
如果要在调整控件大小时执行操作,则应捕获WM_SIZE
,而不是WM_NCCALCSIZE
。
WM_NCCALCSIZE
的目的是允许(例如(标题窗口在标题栏的高度更改时重新计算其工作区的大小。(用户可以通过控制面板进行调整,或者至少他们以前这样做过。 我希望在创建控件时发送WM_NCCALCSIZE
,这有点神秘。
WM_SIZE在 MSDN 中完整记录。
另外:我没有看到您实际上在代码中的任何位置调整该控件的大小......
如果您坚持使用WM_NCCALCSIZE您应该能够通过指定 SWP_FRAMECHANGED 标志来强制使用 SetWindowPos 来强制使用它。
WM_NCCALSIZE
消息处理方式错误。不应将新客户端矩形的坐标设置为NCCALCSIZE_PARAMS
的rgrc[0]
矩形,而应仅设置相对于控件总边界的递增/递减。这些递增/递减值指定非工作区的大小,即工作区的边距。
更改控件的大小后,工作区的大小将相对于新控件的边界自动调整。
例如,此代码将工作区的上/下边距设置为3 px
,左/右边距设置为50 px
。
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <string>
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker,""/manifestdependency:type='win32'
name='Microsoft.Windows.Common-Controls' version='6.0.0.0'
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"")
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK RichEditProc(HWND, UINT, WPARAM, LPARAM);
WNDPROC richEditOrigProc;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
LoadLibrary(TEXT("msftedit.dll"));
WNDCLASSEX mainwcex;
mainwcex.cbSize = sizeof(WNDCLASSEX);
mainwcex.style = CS_HREDRAW | CS_VREDRAW;
mainwcex.lpfnWndProc = WindowProc;
mainwcex.cbClsExtra = 0;
mainwcex.cbWndExtra = 0;
mainwcex.hInstance = hInstance;
mainwcex.hIcon = NULL;
mainwcex.hCursor = (HICON)LoadCursor(NULL, IDC_ARROW);
mainwcex.hbrBackground = GetSysColorBrush(COLOR_MENU);
mainwcex.lpszMenuName = NULL;
mainwcex.lpszClassName = "mainwindow";
mainwcex.hIconSm = NULL;
RegisterClassEx(&mainwcex);
HWND mainWindow = CreateWindowEx(
NULL,
"mainwindow",
NULL,
WS_OVERLAPPEDWINDOW,
100,
100,
600,
400,
NULL,
NULL,
hInstance,
NULL);
HWND richEditControl = CreateWindowEx(
NULL,
"RICHEDIT50W",
"Rich Edit",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP,
50,
50,
100,
25,
mainWindow,
NULL,
hInstance,
NULL);
richEditOrigProc = (WNDPROC) SetWindowLongPtr(richEditControl, GWLP_WNDPROC, (LONG_PTR) RichEditProc);
// Changes the width of rich edit control from 100 px to 400 px.
SetWindowPos(richEditControl, NULL, 0, 0, 400, 25,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
ShowWindow(mainWindow, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!IsDialogMessage(mainWindow, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK RichEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_NCCALCSIZE:
{
if (wParam) {
NCCALCSIZE_PARAMS* ncParams = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
ncParams->rgrc[0].left += 50;
ncParams->rgrc[0].top += 3;
ncParams->rgrc[0].right -= 50;
ncParams->rgrc[0].bottom -= 3;
return WVR_HREDRAW;
}
return 0;
}
}
return CallWindowProc(richEditOrigProc, hWnd, uMsg, wParam, lParam);
}
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 函数调用中参数的顺序重要吗
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在c++类上调用void函数
- 为什么 std::unique 不调用 std::sort?
- 调用专用模板时出错"no matching function for call to [...]"
- 选择要调用的构造函数
- C++为什么尽管我调用了void函数,它却不起作用
- 构造函数正在调用一个使用当前类类型的函数
- 变量没有改变?通过向量的函数调用
- 没有为自己的结构调用列表推回方法
- 调用'begin(int [n])'没有匹配函数
- 什么时候调用析构函数
- 如何用参数值调用函数(仅在运行时已知)
- std::cout.imbue()多重调用
- 函数何时会在c++中包含stack_Unwind_Resume调用