Donnerstag, 24. März 2011

Using a dialog - Using buttons & co.

Hi everyone

This article continues where the first article about dialogs ended. If you didn't read that so far you can do it here.

Important notice for users which do not use the paid version of Visual Studio:
Depending on the free editor you are using you maybe need to convert the source code a bit or move files. For example ResEdit creates a resource.h but you need to move it to your projects folder first
.

In the article linked above we have seen how we can show and destroy a dialog using a resource inside the executable and the function DialogBox. Now we want to let the user interact with controls on the dialog. In this example we will make a dialog that has a two buttons and an edit box where the user can enter text.

First we need to design our dialog. Please have a look at the properties tab in visual studio for the dialog and the children to customize them. Here is how i made mine look:


Now in order to identify controls on a dialog every control becomes its own ID. You can view this ID if you select the control and go to the properties tab. There you'll find the column "ID". It contains the name of the control. For my Flash Window button it looks like that:


This name will be important later. The Request text button has the ID IDB_REQTEXT and the edit control is named IDC_EDIT1. Thats all for the design. Now we can switch back to the code!

As all logic will be performed inside the DialogProc function so long all variables will be static ones inside that function. To retrieve handles (accessors) for the controls windows exposes the function GetDlgItem (which means "get item inside dialog") which searches all controls and looks if it matches the given ID. The usage of GetDlgItem is very simple. The first parameter is the dialog that should be searched and the second parameter is the ID (see above) of the control to search.

This results in the following code:
#include <windows.h>
#include "resource.h"

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

BOOL WINAPI DialogProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 static HWND hButtonFlash = NULL;
 static HWND hButtonReq = NULL;
 static HWND hEdit1 = NULL;

 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  {
   hButtonFlash = GetDlgItem(hWindow, IDB_FLASH);
   hButtonReq = GetDlgItem(hWindow, IDB_REQTEXT);
   hEdit1 = GetDlgItem(hWindow, IDC_EDIT1);
   if(hButtonFlash == NULL || hButtonReq == NULL || hEdit1 == NULL)
   {
    MessageBox(hWindow, "Unable to retrieve controls! Closing....", "Error!", MB_OK);
    EndDialog(hWindow, 0);
    DestroyWindow(hWindow);
    return TRUE;
   }

   break;
  }
  return TRUE;
 }

 return FALSE;
}

int main()
{
 DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), GetDesktopWindow(), DialogProc);
}

We are searching the codes as soon as the dialog is ready which is inside WM_INITDIALOG. We also check if we could get handles to all controls we need and if not we sadly need to terminate the application because they are essential!

Well, now, what happens if the user clicks on the flash button? In fact a message to its owner (which is the dialog) is sent indicating what happened. The ID of that message is WM_COMMAND. For controls of a dialog it has a special layout. The high word of wParam holds the type of command that is sent. The low word of wParam holds the ID of the control that sent the message and lParam holds a handle to that control. Using that we can easily determine if one of our buttons was pressed and also which one as you can see in this code:
#include <Windows.h>
#include "resource.h"

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

BOOL WINAPI DialogProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 static HWND hButtonFlash = NULL;
 static HWND hButtonReq = NULL;
 static HWND hEdit1 = NULL;

 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  {
   hButtonFlash = GetDlgItem(hWindow, IDB_FLASH);
   hButtonReq = GetDlgItem(hWindow, IDB_REQTEXT);
   hEdit1 = GetDlgItem(hWindow, IDC_EDIT1);
   if(hButtonFlash == NULL || hButtonReq == NULL || hEdit1 == NULL)
   {
    MessageBox(hWindow, "Unable to retrieve controls! Closing....", "Error!", MB_OK);
    EndDialog(hWindow, 0);
    DestroyWindow(hWindow);
    return TRUE;
   }

   break;
  }
  return TRUE;

 case WM_COMMAND:
  {
   if(HIWORD(wParam) != BN_CLICKED) // we are only interested in clicks
    break;

   switch(LOWORD(wParam))
   {
   case IDB_FLASH:
    {
     FlashWindow(hWindow, TRUE);
     break;
    }
    break;
   case IDB_REQTEXT:
    {
     int textLen = GetWindowTextLength(hEdit1);
     if(textLen == 0)
      break;

     // We need to include the terminating 0, GetWindowTextLength does not count it!
     TCHAR* wndText = new TCHAR[textLen + 1];
     GetWindowText(hEdit1, wndText, textLen + 1);
     MessageBox(hWindow, wndText, "Info", MB_OK);
     delete [] wndText;
    }
    break;
   }
  }
  return TRUE;
 }

 return FALSE;
}

int main()
{
 DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), GetDesktopWindow(), DialogProc);
}

There is one other important thing to say about WM_COMMAND. The high word of wParam which specifies the notification code is not different for every single type of notification that exists. Its only unique for each type of control. For example BN_CLICKED is defined as 0. There are other types of controls which also have the notification code defined which not really means that they were clicked. So always take the type of control that sent the notification into account, thats very important!

Thanks for reading and cheers
Yanick

Keine Kommentare:

Kommentar veröffentlichen