Freitag, 25. März 2011

Dialog Controls explained - Part 1

Hello

In this series of articles i will describe you the usage of several common controls that are used together with windows. Here it actually doesn't matter if you are using a dialog resource and DialogBox to create the window or if you are doing it the manual way using CreateWindow for the main window and all its controls. All you need is just a HWND to that control you are interacting with. Tough in this article for now you can only see the source code that uses a dialog template to create the window. So if you are not using a dialog resource just stick to the part we are actually acting with the controls.

The base part of this project is an empty dialog with no controls on it and the following code that displays it:
#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)
{
 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  return TRUE;
 }

 return FALSE;
}

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

Now lets start adding controls to it!


The common button control

Buttons are represented by the following window class: "Button" (pretty unexpected...)

We can drag & drop button from the toolbox onto our dialog:


The property bar in Visual Studio already gives you a lot of customization options. As every of them is described pretty well inside the property view i think its not really necessary to talk about them. If you are unsure about one just leave a comment!

Well, the most important thing buttons are for is handling clicks. Thus we should know what happens if we click on a button. This is rather simple. The button sends a WM_COMMAND message to its parent window setting wParam and lParam to appropriate values. Its like that:
HIWORD(wParam) = notification code = BN_CLICKED
LOWORD(wParam) = Control ID = IDC_BUTTON1 (for this button in my case)
lParam = Control handle = some value we dont know yet

So to handle a click from a button all you need to do is catching WM_COMMAND in the DialogProc (or WndProc if you are using a regular window) and interpret the values!

Thats an example code that handles the click on our button:
#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)
{
 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  return TRUE;

 case WM_COMMAND:
  {
   if(LOWORD(wParam) == IDC_BUTTON1)
   {
    if(HIWORD(wParam) == BN_CLICKED)
     MessageBox(0, "Button clicked!", "", MB_OK);
   }
  }
  return TRUE;
 }

 return FALSE;
}

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

All we do is catching WM_COMMAND and then we compare if the ID is our button and if the code is BN_CLICKED. Thats pretty easy!

As a side note there are interesting styles for buttons defined in CommCtrl.h! For example the following code:
#include <windows.h>
#include "resource.h"
#include <commctrl.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)
{
 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  {
   HWND hButton = GetDlgItem(hWindow, IDC_BUTTON1);
   // do something if hButton == NULL
   LONG oldStyle = GetWindowLongPtr(hButton, GWL_STYLE);
   oldStyle |= BS_SPLITBUTTON;
   SetWindowLongPtr(hButton, GWL_STYLE, oldStyle);
  }
  return TRUE;

 case WM_COMMAND:
  {
   if(LOWORD(wParam) == IDC_BUTTON1)
   {
    if(HIWORD(wParam) == BN_CLICKED)
     MessageBox(0, "Button clicked!", "", MB_OK);
   }
  }
  return TRUE;
 }

 return FALSE;
}

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

Lets the button look like that:


but be aware: This is only available from Windows Vista on!

Another neat feature of buttons is that they can display images! This is done pretty easy. In the property page you can set the "Bitmap" property to true which will instruct the button to display a bitmap (of course only after we send BM_SETIMAGE). With that in mind the following code can be used to add the image to the button:
#include <windows.h>
#include "resource.h"
#include <commctrl.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 HBITMAP hButtonBackg = (HBITMAP)LoadImage(0, "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  {
   HWND hButton = GetDlgItem(hWindow, IDC_BUTTON1);
   // do something if hButton == NULL
   SendMessage(hButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hButtonBackg);
  }
  return TRUE;

 case WM_COMMAND:
  {
   if(LOWORD(wParam) == IDC_BUTTON1)
   {
    if(HIWORD(wParam) == BN_CLICKED)
     MessageBox(0, "Button clicked!", "", MB_OK);
   }
  }
  return TRUE;
 }

 return FALSE;
}

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

And instead of text now there is an image being displayed (of course only if image.bmp exists in the current folder!). One might expect that setting the bitmap property inside the property page to false will hide the image on the button. But thats not true. That property more says "Only bitmap and no text". If you set Bitmap to false and send BM_SETIMAGE the text and the image will displayed organized on the button!

And a last nice feature of buttons (starting at Windows Vista) is that they can be used to elevate the current user level (pushing the application into administrator mode).

If you have windows vista the following code will put an UAC-Shield on the button (this wont handle putting the application into a higher state! It only displays the shield!)
#include <Windows.h>
#include "resource.h"
#include <commctrl.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 HBITMAP hButtonBackg = (HBITMAP)LoadImage(0, "image2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
 switch(uMsg)
 {
 case WM_CLOSE:
  EndDialog(hWindow, 0);
  DestroyWindow(hWindow);
  return TRUE;

 case WM_INITDIALOG:
  {
   HWND hButton = GetDlgItem(hWindow, IDC_BUTTON1);
   // do something if hButton == NULL
   SendMessage(hButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hButtonBackg);
   Button_SetElevationRequiredState(hButton, TRUE);
  }
  return TRUE;

 case WM_COMMAND:
  {
   if(LOWORD(wParam) == IDC_BUTTON1)
   {
    if(HIWORD(wParam) == BN_CLICKED)
     MessageBox(0, "Button clicked!", "", MB_OK);
   }
  }
  return TRUE;
 }

 return FALSE;
}

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

Note that the other image we sent isnt displayed anymore!

More to come later...

Thanks for reading and happy commenting!
Yanick

1 Kommentar: