您的当前位置:首页正文

对话框处理

2023-06-03 来源:好走旅游网
Some handy dialog box tricks, tips and workarounds

By Nishant Sivakumar | 18 Sep 2002

Hidden modal dialogs, stealing focus, always on top dialogs, going full-screen, expanding and contracting dialogs, removing task bar icon, context sensitive help and many other useful tips and tricks.

Introduction

These are some tricks I've discovered along the way when I've been using dialog boxes both in non-dialog based apps and in dialog-based apps. Some of the might seem naive but yet are pretty handy. I thought I'd share some of these tricks with you.

Starting a modal dialog hidden

You often hear people complain that despite putting a ShowWindow(SW_HIDE) in their OnInitDialog their modal dialog still starts up in a shown state. The problem here is that when CDialog::OnInitDialog() finishes it will call ShowWindow(SW_SHOW). Thus your dialog box is again made visisble. But then as, is to be expected, people have worked around this. Here is what you need to do.

Add a BOOL member to your dialog class and call it something, say visible. Now in your dialog constructor set visible to false. Collapse

visible = false;

Now you need to override WM_WINDOWPOSCHANGING. You might have to change your message filtering options to have this message show up in the Class Wizard.

Collapse

void CTest_deleteDlg::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) {

if(!visible)

lpwndpos->flags &= ~SWP_SHOWWINDOW;

CDialog::OnWindowPosChanging(lpwndpos); }

That's it. Now your modal dialog actually starts up in a hidden state. And when you want to make it visible this is what you need to do. Collapse

visible = true;

ShowWindow(SW_SHOW);

Full screen dialogs

Sometimes you might feel the need to make a full screen dialog - means a dialog that fills up the entire monitor. Well it's rather easy to achieve this. You need to remove the caption and the border which we do by removing the WS_CAPTION and the WS_BORDER styles. Then we call SetWindowPos with HWND_TOPMOST and resize the dialog to fill up the entire screen. Just put the following code into your OnInitDialog function. Collapse

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; }

How to steal focus on 2K/XP

I bet that sometimes you long for the old days when a simple

SetForegroundWindow brought your dialog into focus. Sigh! Now with 2K/XP things have sorta changed so that if you try a simple SetForegroundWindow you end up flashing the taskbar icon a few times (I never counted but something tells me it flashes thrice). Not exactly what you wanted to do, eh? Luckily there are ways to bring your dialog into the foreground. The trick is to use AttachThreadInput to attach the thread that owns the current foreground window to our thread, then call SetForegroundWindow and then detach the attached thread, again using AttachThreadInput. Cool, huh?

Collapse

//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);

Making your dialog stay on top

Haven't you seen programs which have an \"always-stay-on-top\" option? Well the unbelievable thing is that you can make your dialog stay on top with just one line of code. Simply put the following line in your dialog class's OnInitDialog() function.

Collapse

SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); Basically what we are doing is to use the SetWindowPos function to change the Z-order of our dialog window. We make our dialog stay on top of all other windows by moving it to the top of the Z-order. Now even when you activate some other window, our window will stay on top. But I'd advise you to make sure you know exactly what you are doing when you do this, for it might annoy people if they can't get your window out of the way when they want to do that.

Expanding and Contracting your dialog boxes

I bet you have seen programs with dialog boxes that start off at one size. They will have a button called \"expanded view\" or maybe something called \"advanced\" and when you click on that button the dialog box expands beautifully revealing some so-far hidden child controls. They might also have some button called \"hide details\and when you click on that it will contract back to the original smaller size hiding the extra controls that were revealed on expansion. This can be easily achieved using SetWindowPos, which as you can see is quite an useful function.

Assume that you have two buttons, \"MakeSmall\" and \"Expand\". Then this is what you need to put in their click-handler functions. Of course, you need to replace the cx and cy parameters with your own values. Collapse

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); }

And also remember to put a call to OnMakeSmall() in your dialog's OnInitDialog() function so that your dialog window starts off in the contracted size. On the contrary you may start it off expanded with a call to OnExpand() in the OnInitDialog(). Sometimes you want to use the same button, changing the button caption accordingly and using a boolean flag to determine when to expand and when to contract.

By the way here is an additional tip from Thomas Freudenberg. It's regarding the fact that even after contraction, the hidden controls are still accesible via the keyboard, thus you might want to disable those controls using EnableWindow. I'd like to thank Thomas for this suggestion:

You have missed something regarding \"Expanding and Contracting your dialog boxes\". It seems as you prefer using the mouse than use the keyboard (You're a so-called Mausschubser (German for mouse hustler or so)) If a dialog is contracted, you can still use the tab key to go to controls which are outside the dialog. I suggest to call EnableWindow (fExtracted) for all appropriate controls.

Getting your dialog box into the desktop

Sometimes the user might move the dialog box around screen till it is partly outside the desktop. And you might want to bring the dialog box back into full view. There might also be a a situation where you did the development on a higher resolution and on your machine it comes up nice and full, but the final user might be using a lower screen resolution and thus part of the dialog will be out of the screen. Again you'd really want to make sure the dialog box is fully visible. Well, believe it or not, this can be accomplished with just one line of code. Collapse

SendMessage(DM_REPOSITION);

Smooth eh? Remember that this message only works for top level dialog boxes and will not work for child dialogs.

Adding minimize/maximize buttons to your dialog box

If you want to do this at design time, all you need to do is to set the properties accordingly. But if for some reason you need to do this at run-time then this is what you need to do. Override OnCreate() and add this code to it. Please note that putting this code in OnInitDialog() has a weird side-effect. The buttons do get shown, both maximize and minimize; but they are dummy, meaning they don't function correctly. My guess is that OnInitDialog() is too late a place to change the style of the dialog. Collapse

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; }

Also note than lpCreateStruct is only a copy of the original CREATESTRUCT passed. Thus changing the style bits there has no effect. And for some puzzling (at least to me) reasons, PreCreateWindow never gets called for a modal dialog and thus we can't change the style bits there too, as we might do for a view window or a frame window.

Changing the mouse cursor - from Andrew Peace

Well, I'd like to thank Andrew Peace for this suggestion. Sometimes you might feel a need to change the default mouse cursor in a dialog. What you need to do is override OnSetCursor, set a new cursor and then return without calling the base class function as show below. I had tried this and it failed because I kept calling the base class. Thanks again to Andrew Peace for pointing me in the right direction.

Collapse

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); }

Changing a dialog's background and control text color

There is an often over-looked CWinApp member function, SetDialogBkColor, that allows us to do just this. The function takes two COLORREFs as its arguments. The first COLORREF is the dialog's background color. The second COLORREF will be the color for static, check and radio controls. It will not affect edit and button controls. All dialog boxes and message boxes that are brought up by the application will globally use these colors. Put this code in your CWinApp derived class's InitInstance(). Remember to call the function just before you instantiate your CDialog-derived object.

Collapse

//Red background with Green colored controls

SetDialogBkColor(RGB(255,0,0),RGB(0,255,0));

This function is now obsolete! It may not even work! I'll update this tip with the correct method in the next update!

Removing the task bar icon of your dialog based App

Sometimes you might want to create stealth dialog applications for whatever reasons. Well they won't be stealth in the proper sense since the dialog will be visible. But lets assume you don't want them to have taskbar icons for whatever reasons. Here is what you do to achieve this. We first create an invisible top level frame window. Here is the code to put into your CWinApp-derived class.

Collapse

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;

Now we need to modify the dialog window's style. So put this code in your CDialog-derived class's OnInitDialog. We need to remove the WS_EX_APPWINDOW style.

Collapse

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 }

Context sensitive help - P J Arends

This tip from Pete Arends shows you how to get context sensitive help in your dialog boxes. The first thing you need to do is to make sure that the dialog box has the question mark on it's title bar. For that you need to do this in the OnInitDialog as shown below. Collapse

BOOL HelpDialog::OnInitDialog() {

//blah blah blah

//blah blah blah

ModifyStyleEx(0, WS_EX_CONTEXTHELP); return CDialog::OnInitDialog(); }

OnHelpInfo(...) can get called in two different situations. The first situation is when the user presses the F1 key. This should normally bring up the main help window for the dialog. The other situation is when the user clicks on the question mark and then clicks on an individual control in the dialog. Alternatively the user might also right click a control and selects the \"What's this?\" option from the popup menu. Of course you'll have to handle popping up of the menu yourself. Thus we'll need to handle these two cases as shown below.

Collapse

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; } }

Remember that the control must have a help ID associated with it. This can be done by taking [Properties --- General Tab --- Help ID check box]. And of course you'll also need to write the help file for your program. Thanks.

Show MessageBox after main dialog is dismissed

Sometimes, it is required that you need to show a message box of some kind after the main dialog in a dialog based application has been dismissed. But you'll notice that your message box is never shown. Funnily if you put a break point there, the program does break while debugging. The problem here is that, in dialog based applications the

CWinThread::m_pMainWnd is the dialog window itself and when the dialog box is dismissed the main window is destroyed and the program exits. The solution is to comment out the line where m_pMainWnd is set to the dialog window.

Collapse

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\

return FALSE; }

Of course you must not call AfxGetMainWnd from anywhere in your

application. Because AfxGetMainWnd blindly returns the m_pMainWnd member of your CWinApp derived class. Otherwise what you can do is to save your CDialog* into some other member variable, say m_pMainWnd2 and then write a function AfxGetMainWnd2 which simply returns m_pMainWnd2, if at all you badly want to use AfxGetMainWnd.

Last Update

Since I update this article very regularly I am not maintaining a complete history, but I shall mention the last update's information here. The following tip or tips were added in the last update which was on September 18th 2002.

Full screen dialogs

 How to steal focus on 2K/XP

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Tabbed Dialog Box Class

By Christophe Mestrallet | 28 Feb 2000

A docking dialog that auto-expands when the mouse passes over it

 Download demo project - 27 Kb  Download source files - 8 Kb

Introduction

This class implements a brand new kind of dialog box that can be used with dialog based aplications.

This class contains a Tab instead of the Dialog Title Bar. What is so cool about this dialog is that the dialog actually docks to side of the desktop. Then when the dialog loses focus, the main body of the dialog disappears with only the tab remaining visible! This is great for admin type

applications that you always want your user to have available, yet not in the way.

Then when you want to reactivate the dialog, instead of searching either the Task Bar or the Task Manager for your dialog, you simply move your mouse over the dialog's tab and the complete dialog reappears!

Implementing the Tab Dialog

1. Create your dialog resource as normal and get Class Wizard to

generate a dialog class and data members for you. The dialog

should'nt have the \"Title Bar\" and \"System Menu\" styles enabled. 2. Insert TabDialog.cpp file into your project.

3. include \"TabDialog.h\" file in your Dialog header File.

4. Change the base class of the dialog to CTabDialog. Make sure you

change all occurrence of CDialog to CTabDialog.

5. On your Dialog constructor, you can modify the TabDialog

properties :

o

m_hTitleIcon : handle to an icon to use as the Tab Icon

o o o

m_sTitle : Title to display in the Tab

m_bCloseBox : TRUE if you want a close bbox to appear in the Tab

m_bAutoTabSize : TRUE if you want the Tab Dialog to

automatically compute the Tab size, depending of the Icon, the title and the close box.

o m_nBorderPos : indicate the desktop border you want your tab

dialog to be attached on. follwing values are possibles: TAB_BORDER_LEFT TAB_BORDER_RIGHT TAB_BORDER_TOP TAB_BORDER_BOTTOM

o m_nXpos or m_nYpos : indicate the position in pixels of the

tab Dialog, on the border you chosed.

o m_bTopMost : TRUE if you want the Tab Dialog to be topMost o m_bAutoHide : TRUE if you want the Tab Dialog to auto

show/hide when the mouse move on and out of it o m_nTabHeight : height of the tab in pixels.

o m_nTabWidth : width of the tab in pixels (used if

m_bAutoTabSize = FALSE)

6. At runtime you can call the following methods:

void SetTopMost(BOOL bTopMost) : specify the new TopMost status of the Window

void SetAutoHide(BOOL bAutoHide): specify the new AutoHide status of the Window

The Sample Project

I have included a sample Dialog based project which illustrates how the CTabDialog class can be used. It is a very simple dialog-based application which displays some basic controls.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below. A list of licenses authors might use can be found here

About the Author

因篇幅问题不能全部显示,请点此查看更多更全内容