Have you ever been frustrated with not being able to use full C++ while adhering to the MFC model?
MFC is based on C with a “little bit of inheritance,” which is quite far from the expressivity in modern C++ use. A C++ expert expects RAII (resource acquisition is resource initialization) and to be able to gather orthogonal logic by the use of templates.
Even though there are attempts at that semi-holy grail even from Microsoft – in the form of WTL (Windows Template Library) – it is far from the type of model that a C++ expert would enjoy. Welcome to Win32GUI, a library developed by John Torjo: http://www.torjo.com/win32gui.
He is strongly influenced by the de facto Modern C++ library suite: Boost, and makes use of some of the Boost libraries. Unfortunately, he does not go all the way, such as using Boost.Signal for GUI events, but he goes quite far. In fact, I should assist that effort myself instead of just whining about it (
A new top-level window is created with
-
wnd<sdi_frame> root = create_wnd<sdi_frame>("test", null_wnd);
The create_wnd template function takes the type of window – in this case an SDI frame – as the template argument and expects two parameters (you can provide further options): the caption and the parent window, where null_wnd is a special window corresponding to NULL, or “no window at all.” The reason for not using bare pointers to structures is to allow for RAII.
If you then want to start the event loop, you simply do
-
root->wait();
You create dialogs, menus and other (visual or not) resources via regular Win32 means, and Win32GUI provides a tool called Resource Splitter that creates C++ structures from regular Win32 resources.
What is a Windows application without an event handler?
The event handler class and object is distinct from the window class, so if we want to create a test dialog and some event handling, we do
-
struct test_dlg : wnd_extend<dialog, test_dlg> {
-
static int dialog_id();
-
};
-
-
int test_dlg::dialog_id()
-
{
-
return dialog_id_;
-
}
-
-
// Handles events for the test dialog
-
struct test_dlg_handler : event_handler<
-
test_dlg_handler, test_dlg> {
-
handle_event on_ok() {
-
msg_box(self, "You clicked OK...");
-
return command<IDOK>().
-
HANDLED_BY(&me::on_ok);
-
}
That creates a dialog class, with the actual layout given by a Win32 resource – in this case a simple dialog with an OK and Cancel button. It also associates an event handler class with that dialog class, which in runtime will create analogous coupling between a dialog object and an event handler object. There are ways to manually add event handlers to individual windows as well.
The magic is within the return statement, and, more precisely, in HANDLED_BY. Note that this – awkwardly enough – declares the event handling mapping within the event handler itself; although this might seem like a chicken or egg problem, it Just Works.
The static method dialog_id is what associated a C++ class with a Win32 resource. I leave the internals of this for a later post.
I have much more to say about Win32GUI, but will end by giving you a short sample of a complete Win32 application, with a root menu and a free-floating dialog, with event handlers for both the menu commands and the buttons in the dialog.
Enjoy!
/* File: test_wingui.cpp Author: David Bergman A simple test of the win32gui framework. */ #include <win32gui/pch/pch.hpp> // The following two files are generated by // Resource Splitter, a code generator that // takes a Win32 resource as input #include "win32gui_res/menus.hpp" #include "win32gui_res/mydlg.hpp" // This namespace is where Win32Gui resides using namespace win32::gui; // This namespace is where Resource Splitter puts // the C++ types corresponding to the Win32 // resources using namespace win32::gui::res_id; // Extend the dialog widget, with the layout // given by a Win32 resource struct test_dlg : wnd_extend<dialog, test_dlg> { static int dialog_id(); }; int test_dlg::dialog_id() { return dialog_id_; } // Handles events for the test dialog struct test_dlg_handler : event_handler<test_dlg_handler, test_dlg> { // Event handler for ANY command for the dialog, // which is specified by the 0 command handle_event on_anything() { msg_box(self, "Something happened..."); return command<0>(). HANDLED_BY(&me::on_anything); } // Event handler for the Cancel button, using // the default resource ID of IDCANCEL handle_event on_cancel() { msg_box(self, "You clicked Cancel :-("); return command<IDCANCEL>(). HANDLED_BY(&me::on_cancel); } }; // Act on a menu command "open file" struct test_menu_handler : event_handler<test_menu_handler, sdi_frame> { handle_event on_open() { msg_box(self, "Opening file..."); return command<menu_::arch_open>(). HANDLED_BY(&me::on_open); } handle_event on_save() { msg_box(self, "Saving file..."); return command<menu_::arch_save>(). HANDLED_BY(&me::on_save); } }; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // Create an SDI frame with a menu attached, // and also a dialog on //top of that just to show off wnd<sdi_frame> root = create_wnd<sdi_frame> ("Testing Win32Gui", null_wnd, create_info().menu(menu_::main_menu)); create_dlg<test_dlg>(root); create_wnd<status_bar>(root); // Wait for the event loop to end root->wait(); }
Download this code: test_wingui.cpp
C++ expert developer generic gui mfc templates Tools Reviews win32 win32gui windows