Foreword
In my previous tutorial I've tried to explain the layout of the basic framework for a custom GUI system and I assume you are familiar with solutions I've introduced. If you don't know what I'm talking about, or if you feel like you need to refresh your mind, just follow the link: Custom GUI System - Part 1: Basic Framework. (Note: As in the first part of this tutorial, all 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.)
Dialogs
Let's begin with the easier part of today's subject: the class windialog
. It is inherited from the class winbase
and it is a plain translation of this class to the language of dialogs. The main difference is that the standard creation method create()
is overridden so that it does nothing. It simply returns false. The reason for this is that the class windialog
has its own creation methods create_dialog()
and create_modal()
. The first of these creates a modeless dialog and the second one creates a modal dialog. Both methods take three parameters: the application instance handle, the parent window handle and the resource ID of the dialog. All initialization takes place within these methods so simply calling them creates a dialog object and opens the dialog window using a template designed in the resource editor (from my point of view, the visual resource editors, even the one in VC++, are very useful and relatively clean code generation tools). What follows are the differences between dialog_proc()
and window_proc()
shown in code (code that is identical for both procedures was left out - reference the updated window_proc()
in the section Controls below):
BOOL CALLBACK dialog_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
winbase* wb = 0;
if(msg == WM_INITDIALOG)
{
}
wb = winpool::get_window_from_hwnd(hwnd);
switch(msg)
{
default:
if(wb)
return wb->on_message(msg, wp, lp);
}
return false;
}
This section can be started with taking a short look at the updated window_proc()
. You may remember that this was the procedure registered for our frame window in the first part of this tutorial. It is also used by custom controls. As you will see there are new handler calls in the procedure. These handlers (defined in class winbase
) allow us to process the respective messages separately (not via the on_message()
handler):
LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
winbase* wb = 0;
if(msg == WM_CREATE)
{
wb = winpool::get_created_window();
wb->post_create_window(hwnd);
return wb->on_create(wp, lp);
}
wb = winpool::get_window_from_hwnd(hwnd);
switch(msg)
{
case WM_COMMAND:
return wb->on_command(wp, lp);
case WM_PAINT:
return wb->on_paint(wp, lp);
case WM_NCPAINT:
return wb->on_ncpaint(wp, lp);
case WM_MOUSEMOVE:
return wb->on_mousemove(wp, lp);
case WM_LBUTTONDOWN:
return wb->on_lbuttondown(wp, lp);
case WM_KILLFOCUS:
return wb->on_killfocus(wp, lp);
case WM_SETFOCUS:
return wb->on_setfocus(wp, lp);
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);
default:
if(wb)
return wb->on_message(msg, wp, lp);
else
return DefWindowProc(hwnd, msg, wp, lp);
}
}
As the next step, let us declare the class winctrl
, a base class for the custom controls. This class inherits from the class winbase
and implements access to custom control states, support for manipulating the outlook of controls, support for message routing as well as the most important part (I think), the method, attach()
, that allows attaching of class objects to any dialog controls created in the resource editor. Finally a base class for subclassed controls, the class wintruectrl
, can be introduced. This class inherits from the class winctrl
so it supports all aspects of custom controls. However subclassed controls have a single but considerable difference: instead of window_proc()
they have their own procedure subcls_proc()
, that doesn't call on_create()
since these controls get subclassed after WM_CREATE
is processed. The subclass procedure subcls_proc()
calls the on_message()
handler for all unprocessed messages. The default implementation of this handler calls the original window procedure of the subclassed control. Also note that the default implementation of all handlers in the class wintruectrl
must call the original window procedure of the subclassed control. In order to make this happen, the class winbase
contains new methods for subclassing/unsubclassing a window and a method for calling the original window procedure of the subclassed window.
For the purpose of this tutorial, we will create:
- a test dialog template with a single selection listbox (will be subclassed) and two buttons (will be custom).
- a class
MyButton
inherited from the class winctrl
, that will create a clickable, flat button.
- a class
MyListbox
inherited from the class wintruectrl
, that will subclass the generic listbox class and will allow us append strings at the end of the list.
- a class
MyDialog
, inherited from windialog
, that will encapsulate our test dialog template.
We will also use a subclass of the generic window, called MyWindow
, from the first part of the tutorial. This window will center itself on the screen and it will also load and display a cross-hair cursor. We will re-implement the on_rbuttonup()
handler so that it opens our test dialog. The last thing we do is that we add two lines of code for registering/unregistering our custom MyButton
window class:
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
{
winpool::open_session(hinst);
winpool::register_class(hinst, "MyWindow");
winpool::register_class(hinst, "MyButton");
MyWindow wnd;
int dims[] = {0, 0, 640, 480};
wnd.pre_create_window(hinst, "MyWindow");
wnd.create(NULL, "Tutorial 1 - Use mouse buttons and keyboard keys", dims);
wnd.show_window(true);
while(winpool::dispatch_message()){ }
winpool::unregister_class(hinst, "MyButton");
winpool::unregister_class(hinst, "MyWindow");
winpool::close_session();
return 1;
}
Congratulations, you've made it to the end! We have extended our little GUI system to something like this:
winbase -> MyWindow
|- windialog -> MyDialog
'- winctrl -> MyButton
'- wintruectrl -> MyListbox
Now you should be able to develop your own window types, controls, etc. For inspiration, you can have a look at the Win32 Pro package (you can find this package here - feel free to download it and experiment with it). If in doubt, don't hesitate to drop me a mail: