Introduction
In this article, I am going to walk you through creating a simple pure WIN32 SDI application with a split main window. The window will be split into a left pane, right pane and main window to act as a splitter bar. It has button at the bottom to change the color of the window dynamically. It also uses the XP theme for the controls used in the application, which is enabled through a manifest file.
Most controls in this window were created using the Win32 APIs except a dialog box, which was created with the resource editor used to change the color of the window dynamically. Let's start creating the project. First of all, start Visual Studio and create a new project. Select Win32Project, give your project a name and click OK.
Click Next...
Select "Windows application" and click the Finish button.
Usage
Code used to create a splitter window
The way to create a splitter window is by creating three windows:
- The main window
- The left window
- The right window
Make the left and the right window as child windows to the main window. Use the WS_EX_CLIENTEDGE
extended window style while creating the window with the API CreateWindowEx
, as shown below:
case WM_CREATE : GetClientRect(hWnd, &rect);
g_hleftwnd = CreateWindowEx(WS_EX_CLIENTEDGE, LEFT_WINDOW_CLASS, "",
WS_CHILD | WS_VISIBLE, rect.left, rect.top + TOP_POS, LEFT_WND_WIDTH,
(rect.bottom - rect.top) – (TOP_POS + BOTTOM_POS), hWnd, NULL,
hInst, NULL);
if(NULL != g_hleftwnd)
{
ShowWindow(g_hleftwnd, SW_SHOW);
UpdateWindow(g_hleftwnd);
}
g_hrightwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RIGHT_WINDOW_CLASS, "",
WS_CHILD | WS_VISIBLE | SS_SUNKEN,
rect.left + LEFT_WINDOW_WIDTH + SPLITTER_BAR_WIDTH,
rect.top + TOP_POS,
rect.right - (rect.left + LEFT_WINDOW_WIDTH +SPLITTER_BAR_WIDTH),
(rect.bottom - rect.top) - (TOP_POS + BOTTOM_POS),
hWnd, NULL, hInst, NULL);
if(NULL != g_hrightwnd)
{
ShowWindow(g_hrightwnd, SW_SHOW);
UpdateWindow(g_hrightwnd);
}
Adjust the width of the right and left windows so that the main window will act as a splitter bar. We can adjust the width of the splitter bar through MACRO SPLITTER_BAR_WIDTH
, which is defined in the file macro.h. Handle the window messages...
WM_LBUTTONDOWN
WM_MOVE
WM_LBUTTONUP
WM_SIZE
...as shown in the code below:
case WM_LBUTTONDOWN :
{
int xPos;
int yPos;
xPos = (int)LOWORD(lParam);
yPos = (int)HIWORD(lParam);
xSizing = (xPos > nleftWnd_width - SPLITTER_BAR_WIDTH &&
xPos < nleftWnd_width + SPLITTER_BAR_WIDTH );
if(xSizing)
{
SetCapture(hWnd);
if(xSizing)
{
SetCursor(hcSizeEW);
}
}
}
break;
case WM_MOUSEMOVE :
{
int xPos;
int yPos;
xPos = (int)LOWORD(lParam);
yPos = (int)HIWORD(lParam);
if(wParam == MK_LBUTTON)
{
if(xSizing)
{
RECT focusrect;
HDC hdc;
hdc = GetDC(hWnd);
GetClientRect(hWnd, &rect);
if(xSizing)
{
SetRect(&focusrect, nleftWnd_width - (WIDTH_ADJUST * 2),
rect.top + TOP_POS, nleftWnd_width + WIDTH_ADJUST,
rect.bottom - BOTTOM_POS);
DrawFocusRect(hdc, &focusrect);
nleftWnd_width = xPos;
SetRect(&focusrect, nleftWnd_width - (SPLITTER_BAR_WIDTH * 2),
rect.top + TOP_POS, nleftWnd_width + SPLITTER_BAR_WIDTH,
rect.bottom - BOTTOM_POS);
DrawFocusRect(hdc, &focusrect);
}
ReleaseDC(hWnd, hdc);
}
}
if( (xPos > (nleftWnd_width - SPLITTER_BAR_WIDTH) &&
xPos < (nleftWnd_width + SPLITTER_BAR_WIDHT))
{
SetCursor(hcSizeEW);
}
}
break;
case WM_LBUTTONUP :
if(xSizing)
{
RECT focusrect;
HDC hdc;
ReleaseCapture();
hdc = GetDC(hWnd);
GetClientRect(hWnd, &rect);
if(xSizing)
{
SetRect(&focusrect, nleftWnd_width - (WIDTH_ADJUST * 2),
rect.top + TOP_POS, nleftWnd_width + WIDTH_ADJUST,
rect.bottom - 80);
DrawFocusRect(hdc, &focusrect);
xSizing = FALSE;
}
ReleaseDC(hWnd, hdc);
}
PostMessage(hWnd, WM_SIZE, 0, 0);
break;
We will get this message whenever the user tries to resize the window using the splitter bar. Here we will get the postion of the splitter bar and move the left and right windows accordingly, using the API MoveWindow()
.
case WM_SIZE:
GetClientRect(hWnd, &rect);
MoveWindow(g_hleftwnd, rect.left,
rect.top + TOP_POS, rect.left + (nleftWnd_width - WIDTH_ADJUST),
(rect.bottom - (TOP_POS + BOTTOM_POS)), FALSE);
MoveWindow(g_hrightwnd, rect.left + nleftWnd_width + WIDTH_ADJUST,
rect.top + TOP_POS, rect.right - (nleftWnd_width + WIDTH_ADJUST),
rect.bottom - (TOP_POS + BOTTOM_POS), FALSE);
MoveWindow(hclose_button, rect.right - SPACE_BUTTON_RIGHT,
rect.bottom - SPACE_BUTTON_BOTTOM, CLOSE_BUTTON_WIDTH,
BUTTON_HEIGHT, FALSE);
MoveWindow(hcolor_button,
rect.right - (SPACE_BUTTON_RIGHT + COLOR_BUTTON_WIDTH + BUTTON_ADJUST),
rect.bottom - SPACE_BUTTON_BOTTOM, COLOR_BUTTON_WIDTH,
BUTTON_HEIGHT, FALSE);
MoveWindow(hText, rect.left, rect.top,
rect.right, STATIC_TEXT_HEIGHT, FALSE);
InvalidateRect(hWnd, &rect, TRUE);
break;
Code to handle the keyboard events when a tab or enter key is pressed
case WM_KEYDOWN :
switch(wParam)
{
case VK_TAB :
if (focus == hcolor_button)
{
SendMessage(focus, WM_KILLFOCUS, 0, 0);
SendMessage(hclose_button, WM_SETFOCUS, 0, 0);
focus = hclose_button;
}
else if (focus == hclose_button)
{
SendMessage(focus, WM_KILLFOCUS, 0, 0);
SendMessage(hcolor_button, WM_SETFOCUS, 0, 0);
focus = hcolor_button;
}
break;
case VK_RETURN:
if (IsWindowEnabled(focus))
{
SendMessage(hWnd, WM_COMMAND, 0, (LPARAM)focus);
}
break;
}
break;
Code to used to color the window with the user selected RGB values
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
hmain_Wnd_brush = CreateSolidBrush(RGB(main_Wnd_clr[0],
main_Wnd_clr[1], main_Wnd_clr[2]));
FillRect(hdc, &rect, hmain_Wnd_brush);
DeleteObject(hmain_Wnd_brush);
EndPaint(hWnd, &ps);
break;
The same code will be used in the window procedures of the left and right windows to paint them with the user-selected RGB values.
Points of interest
Most Win32 applications will not use Windows themes until explicitly asked to use them. This can be done by using the manifest file given below
="1.0"="UTF-8"="yes"
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="x86" name="App"
type="win32">
</assemblyIdentity>
<description>Splitter App</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT"
version="8.0.50608.0" processorArchitecture="*"
publicKeyToken="1fc8b3b9a1e18e3b">
</assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" processorArchitecture="*"
publicKeyToken="6595b64144ccf1df" language="*">
</assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>
Follow these steps: Copy this file into a text file, naming it as "Your Application Name(XXXX).exe.manifest" and add this file to the project in the resource folder as shown below.
This manifest file will help in using the themes to the control used in the windows.
Conclusion
This our first submission to The Code Project, so people who find any bugs and/or have suggestions are always welcome.
History
- 26 July, 2007 -- Original version posted