Выбрать главу

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {

 switch(Message) {

 case WM_CREATE:

  {

   HMENU hMenu, hSubMenu;

   hMenu = CreateMenu();

   hSubMenu = CreatePopupMenu();

   AppendMenu(hSubMenu, MF_STRING, ID_FILE_EXIT, "E&xit");

   AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&File");

   hSubMenu = CreatePopupMenu();

   AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");

   AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");

   SetMenu(hwnd, hMenu);

   hIcon = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);

   if (hIcon) SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);

   else MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);

   hIconSm = LoadImage(NULL, "menu_two.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);

   if (hIconSm) SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);

   else MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);

  }

  break;

 case WM_COMMAND:

  switch(LOWORD(wParam)) {

  case ID_FILE_EXIT:

   break;

  case ID_STUFF_GO:

   break;

  }

  break;

 case WM_CLOSE:

  DestroyWindow(hwnd);

  break;

 case WM_DESTROY:

  PostQuitMessage(0);

  break;

 default:

  return DefWindowProc(hwnd, Message, wParam, lParam);

 }

 return 0;

}

As you can see we've got our WM_COMMAND all set up, and it even has another switch() in it. This switch()'s on the value of the low word of wParam , which in the case of WM_COMMAND contains the control or menu id that sent the message.

We obviously want the Exit menu item to close the program. So in the WM_COMMAND, ID_FILE_EXIT handler you can use the following code to do just that.

PostMessage(hwnd, WM_CLOSE, 0, 0);

Your WM_COMMAND handler should now look like this:

case WM_COMMAND:

 switch(LOWORD(wParam)) {

  case ID_FILE_EXIT:

   PostMessage(hwnd, WM_CLOSE, 0, 0);

   break;

  case ID_STUFF_GO:

   break;

 }

 break;

I leave it up to you to make the other menu command ID_STUFF_GO do something.

The program file icon

You may have noticed that the menu_one.exe file now shows up as the custom icon we added as a resource, whereas the menu_two.exe

file does not, since we are loading an external file. Windows Explorer simply displays the first icon (numerically by ID) in the program files resources, so since we only have one icon, that's what it is displaying. If you want to be sure that a certain icon is displayed with your program file, simply add it as a resource and assign it a very low ID… like 1. You don't even need to refer to the file in your program, and you can load completely different icons for your windows if you choose.

Dialogs, GUI coders best friend

Example: dlg_one

There's hardly a windows program out there that doesn't use dialog boxes. Just go File→Open in any text editor or any other kind of editor for that matter and voila, you are presented with a dialog box, one that probably allows you to select a file to be opened.

Dialogs aren't limited to the standard open file ones, they can look like and do whatever you choose. The attractive point of dialogs is that they provide a quick way to arrange and create a GUI (Graphic User Interface) and even some default processing, cutting down on the amount of code you must write.

One thing to remember is that dialogs are just windows. the difference between a dialog and a "normal" window is that the system does some additional default processing for dialogs, such as creating and initialising controls, and handling tab order. Nearly all APIs that are applicable to "normal" windows will work just as well on dialogs, and vice versa!

The first step is to create the dialog resource. As with any resource how you do this will depend on your compiler/IDE. Here I will show you the plain text of the dilaog in the .rc file and let you incorporate it into your project.

IDD_ABOUT DIALOG DISCARDABLE 0, 0, 239, 66

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "My About Box"

FONT 8, "MS Sans Serif"

BEGIN

 DEFPUSHBUTTON "&OK",IDOK,174,18,50,14

 PUSHBUTTON    "&Cancel",IDCANCEL,174,35,50,14

 GROUPBOX      "About this program…",IDC_STATIC,7,7,225,52

 CTEXT         "An example program showing how to use Dialog Boxes\r\n\r\nby theForger",

               IDC_STATIC,16,18,144,33

END

On this first line, IDD_ABOUTDLG is the id of the resource. DIALOG is the resource type, and the four number are the Left, Top, Width and Height co-ordinates. These ARE NOT PIXELS, they are in Dialog Units, which are based on the size of the font used by the system (and chosen by the user). If you have a large font selected, the dialog will be large, if you use a smaller font, the dialog will be that much smaller. This is important as it makes sure that all of the controls are the proper size to display their text in the current font. You can convert dialog units to pixels at runtime using MapDialogRect(). DISCARDABLE tells the system it may swap the resource memory to disk when it's not being used in order to conserve system resources (essentially pointless).

The second line starts with STYLE and follows with the window styles that will be used to create the dialog. These should be explained under CreateWindow() in your help files. In order to use the predefined constants you may need to add #include "windows.h" to your .rc file, or in the case of VC++, winres.h or afxres.h will do. If you use the resource editor these files will certainly be included automatically if needed.

The CAPTION line should be self explanitory.

The FONT line specifies the size and name of the font you wish to use for this dialog box. This might not end up exactly the same on each computer as different people will have different fonts and may have specified different font sizes. You usually don't need to worry about that though.