Win32 splitter window project






3.43/5 (20 votes)
Jul 26, 2007
2 min read

74019

5479
Creating a simple, pure WIN32 SDI application with a split main window
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:
// In WM_CREATE of the main window procedure
case WM_CREATE : GetClientRect(hWnd, &rect);
// Creates the left window using the width and height read from the XML
// files
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);
}
// Creates the right window using the width and height read from the XML
// files
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 statement to handle the left mouse button down message
// received while the mouse left button is down
case WM_LBUTTONDOWN :
{
int xPos;
int yPos;
// Varible used to get the mouse cursor x and y co-ordinates
xPos = (int)LOWORD(lParam);
yPos = (int)HIWORD(lParam);
// Checks whether the mouse is over the splitter window
xSizing = (xPos > nleftWnd_width - SPLITTER_BAR_WIDTH &&
xPos < nleftWnd_width + SPLITTER_BAR_WIDTH );
// If the mouse is over the splitter then set mouse cursor
// image to IDC_SIZEWE which helps the user to drag the window
if(xSizing)
{
// Api to capture mouse input
SetCapture(hWnd);
if(xSizing)
{
SetCursor(hcSizeEW);
}
}
}
break;
case WM_MOUSEMOVE :
{
int xPos;
int yPos;
// Get the x and y co-ordinates of the mouse
xPos = (int)LOWORD(lParam);
yPos = (int)HIWORD(lParam);
// Checks if the left button is pressed during dragging the splitter
if(wParam == MK_LBUTTON)
{
// If the window is dragged using the splitter, get the
// cursors current postion and draws a focus rectangle
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);
// Draw a rectangle while the window is dragged uisng
// the splitter bar
DrawFocusRect(hdc, &focusrect);
// Get the size of the left window to increase
nleftWnd_width = xPos;
// Draws a focus rectangle
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);
}
}
// Set the cursor image to east west direction when the mouse is over
// the splitter window
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;
// Releases the captured mouse input
ReleaseCapture();
// Get the main window dc to draw a focus rectangle
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);
// Call api to vanish the dragging rectangle
DrawFocusRect(hdc, &focusrect);
xSizing = FALSE;
}
// Release the dc once done
ReleaseDC(hWnd, hdc);
}
// Post a WM_SIZE message to redraw the windows
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);
// Call Api to move and adjust the left window postion and its
// height and width
MoveWindow(g_hleftwnd, rect.left,
rect.top + TOP_POS, rect.left + (nleftWnd_width - WIDTH_ADJUST),
(rect.bottom - (TOP_POS + BOTTOM_POS)), FALSE);
// Call Api to move and adjust the right window postion and its
// height and width
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);
// Get the main window rectangle values
GetClientRect(hWnd, &rect);
// Create the brush with the RGB values selected by the user
hmain_Wnd_brush = CreateSolidBrush(RGB(main_Wnd_clr[0],
main_Wnd_clr[1], main_Wnd_clr[2]));
// Fill the window with the created brush
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
<?xml version="1.0" encoding="UTF-8" standalone="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