Dialogs

Win32++

Dialogs

Description

A dialog or dialog box is a specialised window, designed to host other controls, such as buttons, toolbars, tree-views, list-views and so forth. A dialog is typically used to gather information from or present information to the user.

In Win32++, the CDialog class is used to create and control dialogs. We inherit a class from CDialog and override functions to control the way the dialog interacts with its controls. Refer to the description of CDialog in the Class Library section of this documentation for a description of the functions which can be overridden.

Dialog Components

The following picture shows a dialog with several controls, namely: a slider control; a scrollbar control; a progress bar control, a static control (for text); and a button control. These controls are actually specialised windows, and they are child windows of the dialog.

 

Modal and Modeless Dialogs

The behaviour of dialog boxes vary, depending on whether the dialog box is modal or modeless. A modal dialog box requires the user to close the dialog box before activating another window in the application. A modeless dialog box does not require an immediate response from the user. It is similar to a main window containing controls.

Working with resources

The resources used in an application are defined in a resource script. In the Win32++ samples this file is called Resource.rc. The resource script can be used to define the following types of resources.

  • Accelerator
  • Bitmap
  • Cursor
  • Dialog
  • Icon
  • Manifest
  • Menu
  • String table
  • User defined
  • Version Information

Typically a resource editor is used to create the resource script. The commercial compilers generally include a resource a resource editor, but the free ones don't. Fortunately there are some free resource editors available. One such resource editor is ResEdit, available for download at: http://www.resedit.net/.

When resources are defined, they will have an associated resource ID. These resource IDs are defined in a header file (typically resource.h). The resource script and its associated header file should be added to your project. When building your project, the resource script is compiled and linked to your executable or dll.

Its important to note that the resources defined in the resource script are actually embedded in your program. A very large resource, (a large bitmap for example) can increase the size of the executable considerably. If the size of the executable is an important consideration, large bitmaps and icons could be loaded from files during runtime rather than defined in the resource script. 

Creating the dialog

The first step in creating the dialog is to define the dialog resource using a resource editor. The resource ID defined in this process is then used to construct the object inherited from CDialog.

This is an example of how a dialog would be constructed for a simple dialog application. The CDialogApp is inherited from CWinApp, and would be instanciated in WinMain. CDialogApp's constructor initialization list provides the resource ID of the dialog to CMyDialog's constructor. When the application runs, InitInstance is called which creates a modal dialog.

// Declaration of the CDialogApp class
class CDialogApp : public CWinApp
{
public:
  CDialogApp() : m_MyDialog(IDD_DIALOG1) {}
  virtual ~CDialogApp() {}
  virtual BOOL InitInstance();
  {
    //Display the Modal Dialog
    m_MyDialog.DoModal();

    //End the program
    ::PostQuitMessage(0);

    return TRUE;	
  }

  CMyDialog& GetDialog() {return m_MyDialog;}

private:
  CMyDialog m_MyDialog;
};

Working with controls

The OnInitDialog function is called when the dialog is created. We can override this function to determine what happens when the dialog starts. In this example the dialog's icon is set, and one of the dialog's controls (a button) is attached to a class inherited from CButton.

BOOL CMyDialog::OnInitDialog()
{
  // Set the Icon
  SetIconLarge(IDW_MAIN);
  SetIconSmall(IDW_MAIN);

  // Attach the dialog controls
  AttachItem(IDC_BUTTON1, m_Button);
    
  return true;
}

Attaching the button control to m_Button like this allows us to intercept the messages for the button, and handle them in m_Button's WndProc function.

LRESULT CMyButton::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_MOUSEMOVE:      OnMouseMove(wParam, lParam);	break;
    case WM_NCHITTEST:      OnNCHitTest(wParam, lParam);	break;
    case WM_SETCURSOR:      OnSetCursor(wParam, lParam);	break;
    case WM_LBUTTONDOWN:    OnLButtonDown(wParam, lParam);	break;
    case WM_LBUTTONUP:      OnLButtonUp(wParam, lParam);	break;
    case WM_RBUTTONDOWN:    OnRButtonDown(wParam, lParam);	break;
    default:
      TRACE("CMyButton::WndProc - Unspecified Message\n");
      break;
  }

  // Pass all other messages on for default processing	
  return WndProcDefault(uMsg, wParam, lParam);
}

Responding to Events

When a button is pressed on a dialog, a WM_COMMAND message is sent to the dialog window. Override OnCommand to respond to these commands as demonstrated in the following code.

BOOL CMyDialog::OnCommand(WPARAM wParam, LPARAM /*lParam*/)
{
  switch (LOWORD(wParam))
  {
    case IDC_BUTTON1:    OnButton();    return TRUE;
    case IDC_RADIO1:     OnRadio1();    return TRUE;
    case IDC_RADIO2:     OnRadio2();    return TRUE;
    case IDC_RADIO3:     OnRadio3();    return TRUE;
    case IDC_CHECK1:     OnCheck1();    return TRUE;
    case IDC_CHECK2:     OnCheck2();    return TRUE;
    case IDC_CHECK3:     OnCheck3();    return TRUE;
  }

  return FALSE;
}

The Ok and Cancel buttons call the OnOK and OnCancel functions when pressed. The default behaviour of these functions is to close the dialog. Override these functions to perform other tasks.

Handling Notifications

Common controls (for example an Edit control) send notification messages to their parent window to notify them of events, usually by way of a WM_NOTIFY message.  The CDialog's OnNotify function is called in response to a WM_NOTIFY message.  It can be overridden to handle these notification events. 

OnNotify is used to handle notification messages in the class inherited from CDialog. However, we can use the OnNotifyReflect to handle the notification in the CWnd of the child window that generated the notification instead. It is a matter of personal preference whether to use OnNotify to handle the notification in the parent (dialog) or OnNotifyReflect to handle it in the child control that created it. If we are handling notifications from several controls, OnNotifyReflect can prove simpler.

When overriding the OnNotifyReflect function, it could look something like this:

LRESULT CMyTreeView::OnNotifyReflect(WPARAM, LPARAM lParam)
{
  LPNMHDR  lpnmh = (LPNMHDR) lParam;

  switch(lpnmh->code)
  {
  case NM_RCLICK:
    // Handle the mouse right button click notification
    {
      CPoint ptScreen = GetCursorPos();
      DoContextMenu(ptScreen);
    }
    break;
	
  } //switch LPNMHDR

  return 0L;
}

Likewise, OnMessage and OnMessageReflect can also be used to handle a small set of older messages that behave like notifications. The set of messages which are handled by OnMessage and OnMessageReflect are as follows:

  WM_CTLCOLORBTN
  WM_CTLCOLOREDIT
  WM_CTLCOLORDLG 
  WM_CTLCOLORLISTBOX
  WM_CTLCOLORSCROLLBAR
  WM_CTLCOLORSTATIC
  WM_DRAWITEM
  WM_MEASUREITEM
  WM_DELETEITEM
  WM_COMPAREITEM
  WM_CHARTOITEM
  WM_VKEYTOITEM
  WM_HSCROLL
  WM_VSCROLL
  WM_PARENTNOTIFY

When overriding OnMessageReflect, it might look something like this:

LRESULT CMySlider::OnMessageReflect(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  UNREFERENCED_PARAMETER(wParam);
  UNREFERENCED_PARAMETER(lParam);

  switch (uMsg)
  {
  case WM_HSCROLL:
    {
      // Get the slider bar position
      int nPos = GetPos();

      // Get a pointer to the MyDialog object
      CMyDialog* pDialog = (CMyDialog*)GetParent();

      pDialog->SetProgress(nPos);		// Set the progress bar position
      pDialog->SetScroll(nPos);		// Set the scroll bar position
      pDialog->SetStatic(TRUE, nPos);	// Set the static text
      
      break;
    }
  }

  return 0;
}

Handling the Dialog's Messages

The dialog itself is a window. It is the parent window for each of the controls. Sometimes we need to handle the dialog's messages too. When we need to handle the dialog window's messages, we override DialogProc to specify how the messages should be handled.  Any unhandled messages are passed on to the default dialog procedure.

When overriding the DialogProc function, it could look something like this:

BOOL CMyDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//  switch (uMsg)
//  {
//      //Additional messages to be handled go here
//  }

    // Pass unhandled messages on to parent DialogProc
    return DialogProcDefault(uMsg, wParam, lParam); 
}