Posts mit dem Label Dialog werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Dialog werden angezeigt. Alle Posts anzeigen

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

Donnerstag, 24. März 2011

Getting static - Tips with static controls

Aloha

This article covers handling of static controls. Those controls are a bit nasty and need to be tweaked so they work correctly. Also it sometimes is not obvious that a control is a static control which we will see later!

The following code is based on the project created in this article. If you dont remember how interacting with controls works check out this article.

Our final application will load an image when a button is pressed and display it inside a picturebox. You maybe are wondering now where there is a static control involved because neither a button nor a picturebox is really static. But in fact a picturebox is considered a static control. To be clearer a picturebox is nothing more than a simple static control with a style that indicates that this control accepts bitmaps as background.

To start we drag a picturebox control on the dialog and we add a button. I renamed the button to IDB_LOADIMG and left the picturebox as it is. If you save now and open the resource.h you may see that there is only the IDB_LOADIMG and the ID from the picturebox is not present! Why?

Static controls by default are not listed and cannot be accessed. To change that you have to change the ID of every static control you create. This will immediatly add it to the resource.h. Ok, i renamed my picturebox control to IDC_PICBOX. To set the background of a static control you have to send a message to that control. This is done using the function SendMessage. The message to use is STM_SETIMAGE. So the code looks like that:
#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 hButtonLoad = NULL;
 static HWND hPicBox = NULL;
 static HBITMAP hBmp = (HBITMAP)LoadImage(NULL, "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

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

 case WM_INITDIALOG:
  {
   hButtonLoad = GetDlgItem(hWindow, IDB_LOADIMG);
   hPicBox = GetDlgItem(hWindow, IDC_PICBOX);
   if(hButtonLoad == NULL || hPicBox == 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_LOADIMG:
    {
     SendMessage(hPicBox, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
     break;
    }
    break;
   }
  }
  return TRUE;
 }

 return FALSE;
}

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

If you run this you need to make sure that an image called image.bmp is in the folder of the executable. LoadImage is used to load that image as a bitmap. The WPARAM of STM_SETIMAGE indicates which type of background we are sending and the LPARAM is in this case a handle to the bitmap.

When you compile this and run the application and press the button to load the image most likely you will see something like that:


Hey, where is our image?! The answer: Its not displayed at all! Why? Pretty simple: Have a look at the properties page of the picturebox control inside the dialog editor. You can find the column "type" which currently contains "Frame" (unless you have changed it). This tells the system that the control is just a frame. If you send an image to that control Windows will think "Wow, its nice he sends me an image, but actually im just a frame, i dont display images!". So we need to instruct the operating system that our control should accept bitmaps. We change the "Type" to "Bitmap". You may notice that the control now isnt scalable anymore. This is because it will automatically scale to fit the size of the image that gets loaded.

If we compile now and rerun the application after pressing the button the image gets displayed, in my case it looks like that:


Cool, isnt it? :)

Thanks for reading and cya
Yanick

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

Opening the Window - Creating a dialog!

Hello everyone!

In this article we will focus on how to easily create a basic layout for a window. It is using dialog templates stored as a resource inside an executable or DLL. It is often a lot of work to create a layout for a window just using the
CreateWindow
function is pretty a mess also because you cannot view directly how it will finally look. Having a WYSIWYG editor would be way easier! Interestingly there exists a simple way to code using such an editor. In order to achieve that you need to create the window from a resource which is embedded into the executable. This resource contains text that describes how the window should be led out. And the best thing is: There are free and paid editors for those files. Ill describe both, one with a free and one with a paid editor.

Users with a paid version of Visual Studio 
To add a dialog resource you do the following



This will show up a new dialog window that lets us select which type of resource to add. Obviously we will create a dialog resource!



The simplest is to just use Dialog. It will create a nice window and show it up in a new UI. There you can freely add controls, change properties of the window or the controls added.

When you are finished designing the window you can return to your project and you see that there are 2 new files. One is the resource.h which defines symbolic names for the resources and ProjectName.rc. This is the file which describes how resources are called inside the executable.


Users which have no paid version of Visual Studio 
There are free editors available to create a resource script (.rc) file and edit the contents. I think the best one is ResEdit. Its freely available and pretty easy to handle. When finished you will have a .rc file with a dialog resource included. You can drag that .rc file into your project and compile. It will be included into the executable. Thats it!

From now on everything is the same for the paid and the free version. 

Simply displaying the dialog is a very easy task. Windows offers us a function called DialogBox. This function expects parameters that specify where to find the dialog resource. Then it shows the dialog and returns when the dialog is destroyed. As usual also dialogs send messages to the application telling it that something interesting may have happened. So the code to show our dialog is pretty simple:
#include <Windows.h>
#include "resource.h" // users with ResEdit do not need that because they dont have it

// make sure visual styles are enabled
#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()
{
 // Users with ResEdit specify "Name of the resource" for the second param
 DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), GetDesktopWindow(), DialogProc);
}

The most complex is the DialogProc function. In opposite to regular message handlers it only returns a boolean. This boolean tells the operation system if our application has handled the message or not. The two messages that are important are WM_CLOSE and WM_INITDIALOG. The first message is sent when the user presses the close icon, the latter is sent when the dialog is loaded. In WM_INITDIALOG the return value indicates if the keyboard focus should be set to the control the operating system has chosen or not. This actually the recommended way.

In WM_CLOSE we first end the dialog using EndDialog and then destroy the window using DestroyWindow. The second parameter of EndDialog will be the value that DialogBox will return!

Thats it, we have made a nice window without much code!

Next step: here

Thanks for reading and greetings
Yanick