Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

A Custom GUI System

2.11/5 (7 votes)
31 May 20063 min read 1   655  
If you demand the most possible control of your code, if you like to force Windows looking your way, and if you are lazy enough, this tutorial is exactly for you.

Motivation

If you don't like tools that automatically create loads of useless code you mostly won't understand it because the authors didn't bother to document it. If you demand the most possible control of your code, if you like to force Windows looking your way, and if you are lazy enough, this tutorial is exactly for you.

Well, some months ago these were the reasons that made me think of an ultimate solution of a simple, expandable and easy-to-use GUI system. Actually I liked the ideas behind MFC like: separate class for each window type, handlers for messages instead of huge window procedures, but I wanted them in combination with the "low-level" nature of pure Win32 API.

Description

When you look at the source code, you will notice that the basic framework consists of two classes. (Note: Both classes have more or less detailed documentation in the source files. They were extracted from a larger project, and for the purpose of this tutorial, they were stripped down to substantial elements needed to present the basic principles behind them.)

The first class is winbase, a class that represents a generic window (like CWnd in MFC). Its core functionality relies on the standard creation method create(), pre and post create initialization methods and message handlers. Message handlers are implemented as message specific, one handler for one type of message, except for the generic message handler on_message() that provides a way of extending the functionality of the system without recompiling the whole API. The generic handler has also the ability to catch unprocessed messages. All functions in this class are virtual.

The second class is winpool that is a placeholder for all application windows and also acts as an application manager. This class has only static methods as there is no need for multiple instances of this class per application.

Now comes 'the magic' - the common window procedure passed to CreateWindowEx() that gets called in create():

LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
   winbase* wb = 0;

   //special case: at the moment we don't have 
   //the hwnd associated with our window object
   if(msg == WM_CREATE)
   {
      //find the window we are creating - this window has WDS_INITIAL flag set
      wb = winpool::get_created_window();

      //init windows`s hwnd and data members that need to be obtained 
      //through this hwnd (hdc,...) and set window`s display state to WDS_OPEN
      wb->post_create_window(hwnd);

      //bail with calling the user defined (or default) on_create() handler
      return wb->on_create(wp, lp);
   }

   //standard case: we already have the hwnd assigned 
   //to our window object, window has WDS_OPEN flag set
   //get the window this message belongs to
   wb = winpool::get_window_from_hwnd(hwnd);

   //call message specific handlers
   switch(msg)
   {
   case WM_LBUTTONUP:
      return wb->on_lbuttonup(wp, lp);
   case WM_RBUTTONUP:
      return wb->on_rbuttonup(wp, lp);
   case WM_CLOSE:
      return wb->on_close(wp, lp);
   case WM_DESTROY:
      return wb->on_destroy(wp, lp);
   case WM_NCDESTROY:
      return wb->on_ncdestroy(wp, lp);
   //call generic handlers
   default:
      if(wb)
      //we have the pointer to the window
         return wb->on_message(msg, wp, lp);
      else
      //for the sake of security when we for any reason
      // don't have the pointer to the window
         return DefWindowProc(hwnd, msg, wp, lp);
   }
}

Using the system

Well, the system is created, it should do something useful. For the purpose of this tutorial we will make a simple window that will react on mouse clicks and key strokes. So we create a subclass of the generic window, called MyWindow. This window will center itself on the screen and it will also load and display the cross-hair cursor. Then we override the handlers so that when the user left-clicks the window, a message box pops up asking whether the user likes to close the window. When the user right-clicks the window a message box pops up showing the coordinates of the click, and when the user strokes a key a message box pops up showing the ASCII value of the key being stroked. Making the application run is then a piece of cake:

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
{
   //initialize application manager
   winpool::open_session(hinst);

   //register our window class
   winpool::register_class(hinst, "MyWindow");

   //window instance
   MyWindow wnd;

   //position and dimensions array (dims={x, y, w, h})
   //we don`t need to specify position since the window centers itself
   int dims[] = {0, 0, 640, 480};

   //initialize window data members needed for creating the window
   //(e.g hinstance, classname and display state to WDS_INITIAL)
   wnd.pre_create_window(hinst, "MyWindow");

   //create the window - we specify only the 
   //owner (NULL=desktop), caption and dimensions
   wnd.create(NULL, "Tutorial 1 - Use mouse buttons" 
                    " and keyboard keys", dims);

   //finally show the window on the screen
   wnd.show_window(true);

   //run message loop until WM_QUIT is encountered
   while(winpool::dispatch_message()){ /* run */}

   //clean up
   winpool::unregister_class(hinst, "MyWindow");
   winpool::close_session();

   return 1;
}

Last Words

As you can see we ended up with a running GUI system that is easy-to-use and can be further expanded. Now you should understand how the "wheels are turning" in the Win32 Pro package (you can find this package here - feel free to download it and experiment with it). See the next tutorial, where I try to explain how I implemented the support for dialogs, custom and subclassed controls.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here