📄 dialog.html.primary
字号:
<html>
<head>
<title>Controls</title>
<meta name="description" content="Reliable Software Win32 Tutorial: Dialog Box">
<meta name="keywords" content="reliable, software, windows, cplusplus, source code, example, tutorial, controls, dialog box, object oriented">
</head>
<body background="../images/grid.gif" bgcolor="white" text="black">
<table cellpadding=10 width="100%">
<tr>
<td width=100 align=center valign=middle>
<a href="../index.htm">
<img src="../images/rsbullet.gif" alt="RS" border=0 width=39 height=39>
<br>Home</a>
<td><font face="arial" color="#009966">
<h1 align=center>Dialog Box</h1>
</font>
</table>
<p>
<table width="100%">
<tr>
<td width=80> <!-- Left margin -->
<td> <!-- Middle column, there is also the right margin at the end -->
<table cellpadding=10 cellspacing=0 width="100%">
<tr>
<td bgcolor="#ffffff">
<p><a href="source/dialog.zip">Download <img src="images/generic.gif" alt="generic icon" width=32 height=32>eneric</a> program with a dialog box: full sources (zipped project 14k).
<hr>
<font size="+1"><b>Dialog box</b></font> is for a Windows program what a function call is for a C program. First, a Windows programs passes some data to the dialog box in order to initialize it. Next, the dialog box solicits information from the user. When the user decides that the program's curiosity has been satisfied, he or she clicks the OK button. The new data is then returned back to the program.
<p>A dialog box is usually spiked with all kinds of controls (widgets, gadgets, gizmos, whatever you call them). Dialog box code interacts with these gizmos by sending and receiving messages in its <i>Dialog Box Procedure</i>. There is a lot of common code in the implementation of various dialog boxes. We would like to write and test this code only once and then keep reusing it.
<p>What varies from dialog to dialog is:
<ul>
<li>The kind of data that is passed to and retrieved from the dialog box
<li>The kind of gizmos within the dialog box and
<li>The interactions between them.
</ul>
This variability can be encapsulated in two custom client classes: the argument list class and the dialog controller class.
<p>The argument list class encapsulates the in and out arguments and provides access for retrieving and changing them. The definition and the implementation of this class is under complete control of the client (the programmer who reuses our code).
<p>The dialog controller class contains control objects for all the gizmos in the dialog and implements interactions between them. The client is supposed to derive this class from the abstract <b>DlgController</b> class and implement all its pure virtual methods.
<p>The rest is just glue. We need to instantiate a template <b>CtrlFactory</b> class that is used by our generic implementation of the <b>ModalDialog</b> class. The controller factory creates the appropriate client-defined controller and returns a polymorphic pointer to it. Fig 1. shows the relationships between various classes involved in the implementation of a dialog box.
<p><img src="images/dialog.gif" alt="Class hierarchy" border=0 width=450 height=130>
<p>Fig 1. Classes involved in the dialog box design pattern.
<p>Let's start with the client code. In our generic program we need a dialog box that lets the user edit a string. Using a resource editor, we create a dialog template that contains an edit control and two buttons, OK and CANCEL. The resource id of this dialog is IDD_EDITDIALOG. Next we define our own argument list class called <b>EditorData</b> and a special controller class called <b>EditorCtrl</b>. When the user selects the Edit menu item, the following code is executed:
<hr>
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width=100%>
<tr>
<td width=20> </td>
<td bgcolor="#e0e080"><font color="#000000">
<pre>void <font color="#cc0066"><b>Controller::Edit</b></font> (HWND hwnd)
{
EditorData data (_model.GetText ());
ControllerFactory <EditorCtrl, EditorData> factory (& data);
ModalDialog dialog (_hInst, hwnd, IDD_EDITDIALOG, & factory);
if (dialog.IsOk ())
{
_model.SetText (data.GetName ());
// Force repaint
InvalidateRect (hwnd, 0, TRUE);
}
}</pre>
<!--End Code--></font>
</td>
<td width=20> </td>
</tr>
</table>
<!--End of yellow background-->
<hr>
First, the <b>EditorData</b> object is created and initialized with a string. Next, the <b>ControllerFactory</b> template is instantiated. We parametrize it with the two client classes, <b>EditorCtrl</b> and <b>EditorData</b>. The factory object is initialized with a pointer to our data. Next, the <b>ModalDialog</b> object is created. It takes the pointer to our factory as an argument. It uses it to create the controller object and to retrieve data from the argument list. After the interaction with the user is done, we check whether the user blessed the results by clicking the OK button, and if so, we retrieve the result from the editor data object and incorporate it in our program. This is pretty much the standard way to set up a dialog box.
<p>The <b>EditorData</b> class in our example is pretty simple.
<hr>
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width=100%>
<tr>
<td width=20> </td>
<td bgcolor="#e0e080"><font color="#000000">
<pre>class <font color="#cc0066"><b>EditorData</b></font>
{
public:
enum { maxLen = 128 };
EditorData (char const * name)
{
SetName (name);
}
BOOL IsNameOK () { return (_name[0] != '\0'); }
void SetName (char const *name)
{
strcpy (_name, name);
}
char const *GetName () { return _name; }
private:
char _name [maxLen];
};</pre>
<!--End Code--></font>
</td>
<td width=20> </td>
</tr>
</table>
<!--End of yellow background-->
<hr>
The controller class, <b>EditorCtrl</b>, is where all the action is. First of all, it has the Edit control embedded in it. This object is responsible for the interaction with the edit gizmo embedded in the dialog box. The gizmo has the id IDC_NAME_EDIT that we gave it in the resource editor. Second of all, the controller stores a pointer to <b>EditorData</b>. This pointer is retrieved from the base class <b>DlgController</b>. Three virtual methods of <b>DlgController</b> have to be overridden in our <b>EditorControl</b>. These are <b>OnInitDialog</b>, which is called immediately after the dialog has been initialized, <b>OnCommand</b>, which is called whenever any dialog box gizmo sends us a command and, finally, <b>OnNotify</b>, which is used by the new Windows95 controls.
<hr>
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width=100%>
<tr>
<td width=20> </td>
<td bgcolor="#e0e080"><font color="#000000">
<pre>class <font color="#cc0066"><b>EditorCtrl</b></font> : public <font color="#cc0066"><b>DlgController</b></font>
{
public:
EditorCtrl (HWND hwndDlg, void *argList)
: DlgController (argList),
_nameEdit (hwndDlg, IDC_NAME_EDIT)
{
_dlgData = (EditorData *) GetArgList ();
}
void OnInitDialog (HWND hwnd);
bool OnCommand (HWND hwnd, int ctrlID, int notifyCode);
bool OnNotify (HWND hwnd, int idCtrl, NMHDR *hdr);
private:
Edit _nameEdit;
EditorData *_dlgData;
};</pre>
<!--End Code--></font>
</td>
<td width=20> </td>
</tr>
</table>
<!--End of yellow background-->
<hr>
In the <b>OnInitDialog</b> method we retrieve the string that was passed in <b>EditorData</b> and use it to initialize the edit control.
<hr>
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width=100%>
<tr>
<td width=20> </td>
<td bgcolor="#e0e080"><font color="#000000">
<pre>void <font color="#cc0066"><b>EditorCtrl::OnInitDialog</b></font> (HWND hwnd)
{
char const * name = _dlgData->GetName ();
_nameEdit.SetString (name);
}</pre>
<!--End Code--></font>
</td>
<td width=20> </td>
</tr>
</table>
<!--End of yellow background-->
<hr>
<b>OnCommand</b> receives commands from various gizmos. The gizmos are identified by their control id. For instance, if the control id is IDC_NAME_EDIT, it means that something happend to the edit control. In our implementation we are a little overzelous and we react to every change by copying the whole string into <b>EditorData</b> object. There are cases though, when you need to keep validating the string or displaying it in some other control and then you really have to react to every change message.
<p>When the user clicks the OK button, we get a command with the IDOK control id. We verify the string and, if it's okay, we end the dialog passing TRUE as the return code. When the control id is IDCANCEL (from the Cancel button) we end the dialog with the FALSE return code.
<p>The <b>OnNotify</b> method does nothing in the case of pre-Widnows95 controls, such as the edit control and the buttons.
<hr>
<!--Yellow background-->
<table cellpadding=10 cellspacing=0 width=100%>
<tr>
<td width=20> </td>
<td bgcolor="#e0e080"><font color="#000000">
<pre>bool <font color="#cc0066"><b>EditorCtrl::OnCommand</b></font> (HWND hwnd, int ctrlID, int notifyCode)
{
switch (ctrlID)
{
case IDC_NAME_EDIT:
if (_nameEdit.IsChanged (notifyCode))
{
char nameBuf [EditorData::maxLen];
int len = _nameEdit.GetLen ();
if (len < EditorData::maxLen)
{
_nameEdit.GetString (nameBuf, sizeof (nameBuf));
_dlgData->SetName (nameBuf);
}
return true;
}
break;
case IDOK:
if (_dlgData->IsNameOK ())
{
EndDialog(hwnd, TRUE);
}
else
{
MessageBox (hwnd, "Please, enter valid name", "Name Editor",
MB_ICONINFORMATION | MB_OK);
}
return true;
case IDCANCEL:
EndDialog(hwnd, FALSE);
return true;
}
return false;
}
bool <font color="#cc0066"><b>EditorCtrl::OnNotify</b></font> (HWND hwnd, int idCtrl, NMHDR *hdr)
{
return false;
}</pre>
<!--End Code--></font>
</td>
<td width=20> </td>
</tr>
</table>
<!--End of yellow background-->
<hr>
<font size="+1"><b>Now that you know</b></font> how to use it, let's have a look at the implementation of the whole dialog box pattern. The controller factory is a very simple template class. All it does it to accept the generic argument list and store it as a void pointer. Only the client-defined controller knows what the actual class of the argument list is and it will perform a cast (see the constructor of <b>EditorCtrl</b>).
<p>The code common to all controller factories is isolated in the class <b>CtrlFactory</b> from which the actual template is derived. The template overrides the <b>MakeController</b> method to create a new controller of the client-defined class <b>ActualCtrl</b>. Notice however that the method returns <b>ActualCtrl</b> as a pointer to its base class <b>DlgController</b> and that's what the rest of the implementation sees.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -