Introduction
This article came as a result of trying to create a custom border for the MFC application. The simplest way to customize the window of an application is to bind it to a specific theme that gives it the look and feel we want. But sometimes we can't get the specific design we want and more importantly, the shape we want. So that gave the idea of removing the existing borders and title bars and making the window handle the basic actions which the borders and title bar can.
So when we remove the title bar and the border from the window, we have to handle some basic actions which they perform. Those are:
- Moving the window
- Resizing the window
- Close, Restore and Minimize
The Code
First we can remove the title bar and clip the region for the window:
BOOL CCustomFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
CRect rect;
GetClientRect(rect);
m_CustomRgn.CreateRoundRectRgn(rect.left + winStats.frm_clp_wdth,
rect.top + winStats.frm_clp_wdth,
rect.right - winStats.frm_clp_wdth,
rect.bottom - winStats.frm_clp_wdth,
20, 20);
VERIFY(SetWindowRgn(m_CustomRgn , TRUE ));
return CFrameWnd::OnCreateClient(lpcs, pContext);
}
BOOL CCustomFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle ,
CWnd* pParentWnd , CCreateContext* pContext)
{
BOOL bRet = CFrameWnd::LoadFrame
(nIDResource, dwDefaultStyle, pParentWnd, pContext);
if(bRet)
{
ModifyStyle(WS_CAPTION, WS_CLIPCHILDREN);
ModifyStyleEx(WS_EX_CLIENTEDGE,0);
}
return true;
}
As above, we can override the functions OnCreateClient
and LoadFrame
to modify the window.
Next since we remove the borders and title bar, we can use the basic events to capture the actions which now the borderless window cannot. For this, we can use...
afx_msg void OnLButtonDown(UINT nFlags, CPoint point)
... and capture the default message processing. Based on the region clicked, we can let the window process different messages which will perform the tasks. To process the messages, DefWindowProc
can be used.
if (r_windRects.r_Move.PtInRect(point)){
DefWindowProc(WM_SYSCOMMAND,
SC_MOVE + 1 ,MAKELPARAM(point.x,point.y));
return 0;
}
if (Val == -1)
return 0;
::SetCursor(AfxGetApp()->LoadStandardCursor(cursor));
DefWindowProc(WM_SYSCOMMAND, SC_SIZE + Val ,MAKELPARAM(point.x ,point.y));
return 0;
With this part of the code, resizing and moving operations are handled. Since it processes the resizing, now we can resize the window and clip it so that we can keep the basic shape we want.
void CCustomFrame::OnSize(UINT nType, int cx, int cy)
{
CRect rect;
GetClientRect(rect);
m_CustomRgn.Detach();
m_CustomRgn.CreateRoundRectRgn(rect.left + winStats.frm_clp_wdth,
rect.top + winStats.frm_clp_wdth
, rect.right- winStats.frm_clp_wdth,
rect.bottom - winStats.frm_clp_wdth, 30, 30);
SetWindowRgn(m_CustomRgn, TRUE);
CFrameWnd::OnSize(nType, cx, cy);
}
Another thing to handle is the maximization and restoration of the frame. We can't use ShowWindow(SW_SHOWMAXIMIZED)
since we already clipped some of the area from the window. So the below code is used for that:
int CCustomFrame::maximize(void)
{
CRect rect,rectD, rect1;
GetClientRect(rect);
GetWindowRect(m_PrevRect);
int scrWidth = GetSystemMetrics(SM_CXSCREEN);
int scrHeight = GetSystemMetrics(SM_CYSCREEN);
rect.right = scrWidth + winStats.frm_clp_wdth;
rect.bottom = scrHeight + winStats.frm_clp_wdth;
rect.left -= winStats.frm_clp_wdth;
rect.top -= winStats.frm_clp_wdth;
CWnd* pTray = FindWindow(_T("Shell_TrayWnd"), _T(""));
pTray->GetWindowRect(&rectD);
rect.bottom = rectD.top + winStats.frm_clp_wdth;
MoveWindow(rect);
b_maximized = TRUE;
return 0;
}
Points of Interest
The good part of doing this is that by handling some basic window actions and events, we can create the borders as we like. Since we paint the borders, they won't change prior to the Windows platform it runs (e.g.: XP or Vista). We can also use the CDialog
class instead of CFrameWnd
to create the custom window.
But the bad point is handling processing of many messages. Moreover, there can be small defects in drawing things like flickering. So this code might not have a commercial value, but it can be a good learning material!
History
- 30th November, 2008: Initial version