有人能给我举一个使用WINAPI(而不是MFC)在对话框中嵌入属性表的WINAPI示例吗?
下面是我在调查这个问题。我从在这里或通过搜索网页提问。感谢所有你的帮助!
我写了一个类来封装我所发现的一切;如果有兴趣获得副本,请给我发电子邮件mdorl@wisc.edu.
在中传递属性表的父窗口的HWND创建属性表时PROPSHETHEADER的.hwindParent使用PropertySheet页面函数。
我使用对话框上的图片控件作为属性的父项表,所以这就是我用作hwndParent的HWND。
使用回调更改属性表的样式pfnCallback字段在标头中,不要忘记包含dwFlags中的PSH_USECALLBACK。请参阅中的PropSheetProc函数MSDN。在回调的PSCB_ PRECREATE处理程序中,通过删除来调整属性表的窗口样式并添加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;
使用子窗口而不是弹出窗口巧妙地解决了问题一些问题,例如当父级移动,正确保持父窗口的蓝色/灰色状态彩色和剪辑问题。也就是说,你不必担心这些事情。
如果你停在这里,你将面临另一个问题。对话框支持WIN32中的代码会进入cpu循环,试图找到属性表控件。参见
什么是WS_EX_CONTROLPARENT和DS_CONTROL?
对于这个问题。解决方案是添加扩展的WS_EX_CONTROLPARENT样式添加到属性表。您可以找到有关此的信息在web上搜索WS_EX_CONTROLPARENT你找不到的东西给我带来了很大的悲痛。如果使用对话框上的控件作为父级,还必须设置其WS_EX_CONTROLPARENT
我不知道WS_EX_CONTROLPARENT和DS_CONTROL之间的区别,也不知道是否可以用DS_CONTROL代替WS_EX_CONTROLPARENT。从网上我了解到DS_CONTROL与选项卡有关。我的测试应用程序在有或没有DS_CONTROL的选项卡上都能正常工作。
我在调用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_REMATURE 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。但我担心诸如禁用和不可见页面之类的事情。