【发布时间】:2011-10-02 18:34:34
【问题描述】:
谁能指出一个使用 WINAPI(不是 MFC)在对话框中嵌入属性表的 WINAPI 示例?
【问题讨论】:
标签: winapi dialog propertysheet
谁能指出一个使用 WINAPI(不是 MFC)在对话框中嵌入属性表的 WINAPI 示例?
【问题讨论】:
标签: winapi dialog propertysheet
为了记录,以下是我在学习期间学到的一些东西 调查这个问题。我从 在这里或通过搜索网络提问。感谢大家 你的帮助!
我编写了一个类来封装我发现的所有内容; 如果有兴趣获得一份副本,请给我发送电子邮件 mdorl@wisc.edu。
为您的属性表传递父窗口的 HWND 创建属性表时 PROPSHEETHEADER 的 .hwndParent 使用 PropertySheet 页面功能。
我在对话框上使用了一个图片控件作为属性的父级 这就是我用作 hwndParent 的 HWND。
使用回调改变属性表的样式 标头中的 pfnCallback 字段,不要忘记包含 dwFlags 中的 PSH_USECALLBACK。参见 PropSheetProc 函数 MSDN。在回调的 PSCB_PRECREATE 处理程序中, 通过删除调整属性表的窗口样式 WS_POPUP 并添加 WS_CHILD。我不想要边框或标题 属性表上的栏,所以我还删除了 WS_CAPTION、WS_SYSEMNU、 & DS_MODALFRAME
LONG L = ((LPDLGTEMPLATE)lParam)->style;
L &= ~WS_POPUP;
L &= ~WS_CAPTION; // gets rid of title bar
L &= ~WS_SYSMENU;
L &= ~DS_MODALFRAME; // gets rid of border
L |= WS_CHILD; // 40000000
((LPDLGTEMPLATE)lParam)->style = L;
使用子窗口而不是弹出窗口巧妙地解决了 一些问题,例如当父级移动属性表时 移动,正确保持父窗口的蓝/灰状态 着色和剪裁问题。 IE。你不必担心这些事情。
如果您停在这里,您将面临另一个问题。对话框支持 WIN32 中的代码会进入一个 cpu 循环,试图找到属性表和 Windows 中的控件。见
What is WS_EX_CONTROLPARENT and DS_CONTROL For?
对于一个很好的解释 这个问题。解决方案是添加 WS_EX_CONTROLPARENT 扩展 属性表的样式。你可以找到这方面的信息 在 Web 上搜索 WS_EX_CONTROLPARENT。 你找不到的东西 让我很伤心。如果您使用对话框上的控件作为 父级,您还必须设置其 WS_EX_CONTROLPARENT。
我不知道 WS_EX_CONTROLPARENT 和 DS_CONTROL 之间的区别是什么,也不知道您是否可以用 DS_CONTROL 代替 WS_EX_CONTROLPARENT。从网上我收集到 DS_CONTROL 与标签有关。我的测试应用程序在有或没有 DS_CONTROL 的选项卡上的工作方式相同。
在调用了 WS_EX_CONTROLPARENT 之后,我处理了 PropertySheet 函数创建属性表。我不知道 是否可以在回调例程中执行此操作。我想 您可以在回调中使用 GetParent 来获取 控制。
LONG S;
S = GetWindowLong (hwndPS, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS, GWL_EXSTYLE, S);
S = GetWindowLong (hwndPS_Area, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS_Area, GWL_EXSTYLE, S);
使用 PropertySheet 函数创建属性表后, 你必须正确定位它:
SetWindowPos(hwndPS, HWND_TOP, 2, 2, -1, -1,
SWP_NOSIZE | SWP_NOACTIVATE);
我允许为边框设置几个像素。
如果您想摆脱所有属性表按钮及其占用的空间:
RECT rectWnd;
RECT rectButton;
GetWindowRect(hwndPS, &rectWnd);
HWND hWnd = ::GetDlgItem(hwndPS, IDOK);
if(!hWnd)
{
DebugBreak();
}
GetWindowRect(hWnd, &rectButton);
SetWindowPos (hwndPS, NULL, 0, 0,
rectWnd.right - rectWnd.left, rectButton.top - rectWnd.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
个别按钮可以去掉:
hwndOk = GetDlgItem (hwndPS,IDOK); // Hide the OK button
ShowWindow (hwndOk, SW_HIDE);
EnableWindow (hwndOk, FALSE);
您可以使用 PSH_NOAPPLYNOW 去掉 APPLY 按钮 创建属性表时在 PROPERTYSHEETHEADER 中添加 dwFlags。
直到用户首先创建属性表页面 激活它们。我希望它们都在属性时创建 工作表已创建。
unsigned int iP;
for (iP=0; iP<Header.nPages; iP++)
{
if (iP != Header.nStartPage)
SendMessage (hwndPS, PSM_SETCURSEL,iP,NULL);
}
SendMessage (hwndPS, PSM_SETCURSEL,Header.nStartPage,NULL);
有一个 PROPSHEETPAGE 标志可以做同样的事情(来自 MSDN)
PSP_PREMATURE 版本 4.71。导致页面被创建时 创建属性表。如果未指定此标志,则页面 在第一次被选中之前不会被创建。
但我担心 4.71 版的业务,所以我自己做了。
您可以更改任何按钮上的文字:
SetDlgItemText (hwndPS,ButtonID,Text);
其中 ButtonID 是 IDOK IDCANCEL IDHELP IDAPPLYNOW 之一
IDAPPLYNOW 在我的系统上未定义,所以
#define IDAPPLYNOW 0x3021
这是我打算如何使用我的属性表的方式。我将把所有的初始化和 包含属性表的对话框中的终止代码并消除所有属性表按钮。当用户按下某个 DO IT 按钮时,我可以在那里设置所有初始值并在那里检索所有最终值。我注意到设置的行为,例如,一个文本框会导致包含页面对话框获得 WM_COMMAND/EN_CHANGE。如果您使用此消息启用 APPLY 按钮,您可能需要在设置任何列表框后禁用页面的更改标志。我通过在设置初始值后清除所有更改的标志来解决这个问题。 (我怀疑在发送了所有 INITDIALOG 消息后,属性表会清除更改的标志。无论如何,如果在页面 INITDIALOG 处理程序中设置了文本框,则不会启用 APPLY。)如果我发现错误,我将选择该属性表页面并在描述错误的父对话框中放置一些红色文本。属性表对话框程序唯一需要做的就是操作它们的控件。
让我困惑的一件事是如何确定是否所有页面都从它们的 WS_NOTIFY/PSN_APPLY 消息中返回了 PSNRET_NOERROR。我已经取得了一些成功 当报告 PSNRET_INVALID 或收到 PSN_KILLACTIVE 时,连续 PSNRET_NOERROR 回复的数量将计数设置为零。如果计数达到属性表标题中的页数,我假设所有页面都返回了 PSNRET_NOERROR。但我担心诸如禁用和不可见页面之类的事情。
【讨论】: