隐藏模态对话框,窃取焦点,始终在顶部对话框,全屏,展开和收缩对话框,删除任务栏图标,上下文敏感的帮助和许多其他有用的提示和技巧。
介绍
这些是我在非对话框应用程序和基于对话框的应用程序中使用对话框时发现的一些技巧。其中一些可能看起来很幼稚,但却非常方便。我想我应该和你分享一下这些技巧。
启动一个隐藏的模态对话框
你经常听到人们抱怨,尽管在他们的OnInitDialog中放置了一个ShowWindow(SW_HIDE),但他们的模态对话框仍然以显示状态启动。这里的问题是,当CDialog::OnInitDialog()结束时,它将调用ShowWindow(SW_SHOW)。因此,您的对话框再次可见。但正如人们所预料的那样,人们已经解决了这个问题。这是你需要做的。
将BOOL成员添加到对话框类中,并对其进行命名,比如visible。
现在在你的对话框构造函数中设置visible为false。
visible = false;
现在你需要覆盖WM_WINDOWPOSCHANGING。您可能必须更改消息筛选选项,以使该消息显示在“类向导”中。
void CTest_deleteDlg::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) { if(!visible) lpwndpos->flags &= ~SWP_SHOWWINDOW; CDialog::OnWindowPosChanging(lpwndpos); }
就是这样。现在,您的模态对话框实际上是以隐藏状态启动的。当你想让它可见时,这就是你需要做的。
visible = true; ShowWindow(SW_SHOW);
使用HWND_TOPMOST,并调整对话框的大小以填满整个屏幕。只需将以下代码放入OnInitDialog函数中。
BOOL CFullScrDlgDlg::OnInitDialog() { CDialog::OnInitDialog(); //... int cx, cy; HDC dc = ::GetDC(NULL); cx = GetDeviceCaps(dc,HORZRES) + GetSystemMetrics(SM_CXBORDER); cy = GetDeviceCaps(dc,VERTRES) + GetSystemMetrics(SM_CYBORDER); ::ReleaseDC(0,dc); // Remove caption and border SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) & (~(WS_CAPTION | WS_BORDER))); // Put window on top and expand it to fill screen ::SetWindowPos(m_hWnd, HWND_TOPMOST, -(GetSystemMetrics(SM_CXBORDER)+1), -(GetSystemMetrics(SM_CYBORDER)+1), cx+1,cy+1, SWP_NOZORDER); //... return TRUE; }
如何窃取关注2K/XP
我敢打赌,有时你会怀念过去的日子,当一个简单的SetForegroundWindow让你的对话框进入焦点。叹息!现在有了2K/XP,情况有所改变,所以如果你尝试一个简单的SetForegroundWindow,你最终会闪烁任务栏图标几次(我从来没有数过,但有些东西告诉我它闪烁了三次)。这不是你想做的,对吧?幸运的是,有一些方法可以将对话框带到前台。
诀窍是使用AttachThreadInput将拥有当前前台窗口的线程附加到我们的线程,然后调用
SetForegroundWindow
然后再使用AttachThreadInput分离附加的线程。酷,不是吗?
//Attach foreground window thread //to our thread AttachThreadInput( GetWindowThreadProcessId( ::GetForegroundWindow(),NULL), GetCurrentThreadId(),TRUE); //Do our stuff here ;-) SetForegroundWindow(); SetFocus(); //Just playing safe //Detach the attached thread AttachThreadInput( GetWindowThreadProcessId( ::GetForegroundWindow(),NULL), GetCurrentThreadId(),FALSE);
让你的对话框保持在顶层
你没见过有“永远保持领先”选项的程序吗?令人难以置信的是,只需一行代码,你就可以让你的对话框保持在顶部。只需在对话框类的OnInitDialog()函数中输入以下代码。
SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
基本上,我们正在做的是使用SetWindowPos函数来改变我们对话框窗口的z轴次序。我们通过将对话框移动到z轴顺序的顶部,使其保持在所有其他窗口的顶部。即使你激活了其他窗口,我们的窗口也会在顶部。但我建议你在做这件事的时候,一定要清楚自己在做什么,因为如果人们想要把你的窗户挪开却不能,可能会惹恼他们。
展开和收缩对话框
我打赌你见过对话框大小相同的程序。它们会有一个叫做“扩展视图”的按钮,或者叫做“高级”的按钮,当你点击这个按钮时,对话框会漂亮地展开,显示出一些迄今为止隐藏的子控件。它们可能也有一些叫做“隐藏细节”的按钮,当你点击它时,它会收缩回原来的小尺寸,隐藏在扩展中显示的额外控制。这可以通过使用SetWindowPos轻松实现,正如您所看到的,这是一个非常有用的函数。
假设您有两个按钮,“makemall”和“Expand”。这就是你需要在他们的点击处理函数中放入的东西。当然,您需要用自己的值替换cx和cy参数。
void CTest_deleteDlg::OnMakeSmall() { SetWindowPos(NULL,0,0,200,200,SWP_NOZORDER|SWP_NOMOVE); } void CTest_deleteDlg::OnExpand() { SetWindowPos(NULL,0,0,500,300,SWP_NOZORDER|SWP_NOMOVE); }
还记得在你的对话框的OnInitDialog()函数中调用OnMakeSmall(),这样你的对话框窗口就会以缩小的尺寸开始。相反,你可以在OnInitDialog()中调用OnExpand()来开始扩展。有时您希望使用相同的按钮,相应地更改按钮标题,并使用布尔标志来确定何时展开何时收缩。
顺便说一下,这是Thomas Freudenberg的另一个提示。事实上,即使在收缩之后,隐藏的控件仍然可以通过键盘访问,因此您可能想要使用EnableWindow禁用这些控件。我要感谢Thomas的建议:
您错过了关于“扩展和收缩对话框”的内容。似乎你更喜欢使用鼠标而不是键盘(你是一个所谓的Mausschubser(德语为鼠标贩子)),如果一个对话框被压缩了,你仍然可以使用tab键去控制对话框之外的控件。我建议为所有适当的控件调用EnableWindow (fExtracted)。
将对话框放入桌面
有时,用户可能会将对话框在屏幕周围移动,直到它部分位于桌面之外。您可能希望将对话框恢复到完整的视图中。也有可能出现这样一种情况,即你在更高分辨率上进行开发,在你的机器上显示得很好,很完整,但最终用户可能使用较低的屏幕分辨率,因此部分对话框将出现在屏幕之外。同样,您需要确保对话框是完全可见的。不管你信不信,这只需要一行代码就可以完成。
SendMessage(DM_REPOSITION);
顺利是吗?请记住,此消息仅适用于顶级对话框,而不适用于子对话框。
在对话框中添加最小化/最大化按钮
如果您想在设计时这样做,您所需要做的就是相应地设置属性。但如果出于某种原因,你需要在运行时这样做,那么这就是你需要做的。覆盖OnCreate()并将此代码添加到它中。请注意,将这段代码放在OnInitDialog()中有一个奇怪的副作用。按钮确实显示了,最大化和最小化;但是它们是假的,这意味着它们不能正常工作。我猜OnInitDialog()已经太迟了,无法改变对话框的风格。
int CTest_deleteDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here SetWindowLong(this->m_hWnd,GWL_STYLE, GetWindowLong(this->m_hWnd,GWL_STYLE) | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); return 0; }
还要注意lpCreateStruct只是传递的原始CREATESTRUCT的副本。因此,改变样式位没有影响。由于一些令人费解的原因(至少对我来说),PreCreateWindow从来没有被调用一个模态对话框,因此我们不能改变那里的样式位,就像我们可能做一个视图窗口或框架窗口。
更改鼠标光标
有时您可能觉得需要更改对话框中的默认鼠标光标。您需要做的是覆盖OnSetCursor,设置一个新的游标,然后返回,而不调用基类函数,如下所示。我尝试过这个,但是失败了,因为我一直在调用基类。
BOOL CTest_deleteDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // TODO: Add your message handler code here and/or call default SetCursor(AfxGetApp()->LoadStandardCursor(IDC_UPARROW)); // Now we return instead of calling the base class return 0; // return CDialog::OnSetCursor(pWnd, nHitTest, message); }
更改对话框的背景和控件文本颜色
有一个经常被忽略的CWinApp成员函数,SetDialogBkColor,它允许我们这样做。该函数接受两个colorref作为参数。第一个COLORREF是对话框的背景色。第二个COLORREF将是静态、检查和无线电控件的颜色。它不会影响编辑和按钮控件。应用程序打开的所有对话框和消息框都将全局使用这些颜色。把这段代码放到你的CWinApp派生类的InitInstance()中。记住在实例化cdialog派生的对象之前调用该函数。
//Red background with Green colored controls SetDialogBkColor(RGB(255,0,0),RGB(0,255,0));
这个函数现在已经过时了!它甚至可能不起作用!我将在下次更新中使用正确的方法更新这个技巧!
删除基于对话框的应用程序的任务栏图标
有时,您可能出于某种原因想要创建隐藏对话框应用程序。因为对话框是可见的,所以他们不是潜行。但让我们假设你不希望它们有任务栏图标,无论什么原因。以下是你要做的事情。我们首先创建一个不可见的顶层框架窗口。下面是要放入cwinapp派生类中的代码。
CFrameWnd *abc=new CFrameWnd(); abc->Create(0,0,WS_OVERLAPPEDWINDOW); CNoTaskBarIconDlg dlg(abc); m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { } delete abc;
现在我们需要修改对话框窗口的样式。所以把这段代码放到你的cdialog派生类的OnInitDialog中。我们需要删除WS_EX_APPWINDOW样式。
BOOL CNoTaskBarIconDlg::OnInitDialog() { CDialog::OnInitDialog(); ModifyStyleEx(WS_EX_APPWINDOW,0); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control }
上下文敏感的帮助- P J Arends
Pete Arends的这个提示向您展示了如何在对话框中获得上下文相关的帮助。您需要做的第一件事是确保对话框的标题栏上有问号。为此,您需要在OnInitDialog中这样做,如下所示。
BOOL HelpDialog::OnInitDialog() { //blah blah blah //blah blah blah ModifyStyleEx(0, WS_EX_CONTEXTHELP); return CDialog::OnInitDialog(); }
OnHelpInfo(…)可以在两种不同的情况下被调用。第一种情况是用户按下F1键。这通常会打开对话框的主帮助窗口。另一种情况是用户单击问号,然后单击对话框中的单个控件。或者用户也可以右键单击控件,从弹出菜单中选择“这是什么?”选项。当然,你得自己处理菜单弹出的问题。因此,我们需要处理这两种情况,如下所示。
BOOL HelpDialog::OnHelpInfo(HELPINFO* pHelpInfo) { short state = GetKeyState (VK_F1); if (state < 0) // F1 key is down, get help for the dialog return CDialog::OnHelpInfo(pHelpInfo); else { // F1 key not down, get help for specific control if (pHelpInfo->dwContextId) WinHelp (pHelpInfo->dwContextId, HELP_CONTEXTPOPUP); return TRUE; } }
请记住,控件必须有一个与之关联的帮助ID。这可以通过[属性-常规选项卡-帮助ID复选框]来完成。当然,您还需要为您的程序编写帮助文件。谢谢。
在主对话框被解散后显示MessageBox
有时,在基于对话框的应用程序的主对话框被取消后,您需要显示某种类型的消息框。但是您会注意到消息框从未显示出来。有趣的是,如果在这里设置断点,程序会在调试时中断。这里的问题是,在基于对话框的应用程序中,CWinThread::m_pMainWnd是对话框本身,当对话框被取消时,主窗口被销毁,程序退出。解决方案是注释掉m_pMainWnd被设置为对话框窗口的行。
BOOL CTestApp::InitInstance() { // .... CTestDlg dlg; /* Comment out the following line */ //m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle // when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle // when the dialog is // dismissed with Cancel } MessageBox(NULL,"Some message","Title",0); return FALSE; }
当然,你不能从应用程序的任何地方调用AfxGetMainWnd。因为AfxGetMainWnd盲目返回
m_pMainWnd
CWinApp派生类的成员。否则,你可以做的是保存你的CDialog*到一些其他的成员变量,比如m_pMainWnd2,然后写一个函数AfxGetMainWnd2,它只是返回m_pMainWnd2,如果在所有你想使用AfxGetMainWnd。