Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Custom Tab Controls, Tabbed Frame and Tabbed MDI

, 13 Jul 2005
An extensible framework for creating customized tabs in ATL/WTL, with a VS.NET-like tab control implementation, tabbed frames, tabbed MDI, and more.
tabbingframework_demo.zip
TabbedSDISplitter
Release
TabbedSDISplitter.exe
res
Doc.ico
folder_c.ico
folder_o.ico
TabbedSDISplitter.exe.manifest
TabbedSDISplitter.ico
toolbar.bmp
TabbedSDISplitter.dsp
TabbedSDISplitter.dsw
TabDemo
Release
TabDemo.exe
res
error.ico
folder_closed.ico
folder_open.ico
info.ico
log.ico
TabDemo.exe.manifest
TabDemo.ico
TabDemodoc.ico
toolbar.bmp
warning.ico
TabDemo.dsp
TabDemo.dsw
TabbingFramework_ScreenShot.png
DockingDemo
DockingDemo.dsp
DockingDemo.dsw
Release
DockingDemo.exe
res
DockingDemo.exe.manifest
DockingDemo.ico
DockingDemodoc.ico
error.ico
folder_closed.ico
folder_open.ico
info.ico
log.ico
msdev_tab_icons.bmp
toolbar.bmp
warning.ico
include
Sergey Klimov
SimpleTabbedMDIDemo
Release
SimpleTabbedMDIDemo.exe
res
SimpleTabbedMDIDemo.exe.manifest
SimpleTabbedMDIDemo.ico
SimpleTabbedMDIDemodoc.ico
toolbar.bmp
SimpleTabbedMDIDemo.dsp
SimpleTabbedMDIDemo.dsw
tabbingframework_history.zip
tabbingframework_priorhistory.zip
tabbingframework_src.zip
#ifndef __ATLGDIX_H__
#define __ATLGDIX_H__

/////////////////////////////////////////////////////////////////////////////
// Additional GDI/USER wrappers
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2001-2002 Bjarke Viksoe.
// Thanks to Daniel Bowen for COffscreenDrawRect.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. 
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//

#pragma once

#ifndef __cplusplus
   #error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLGDI_H__
   #error atlgdix.h requires atlgdi.h to be included first
#endif

namespace WTL
{

/////////////////////////////////////////////////////////////////////////////
// Macros

// The GetXValue macros below are badly designed and emit
// compiler warnings e.g. when using RGB(255,255,255)...
#pragma warning(disable : 4310)

#ifndef BlendRGB
   #define BlendRGB(c1, c2, factor) \
      RGB( GetRValue(c1) + ((GetRValue(c2) - GetRValue(c1)) * factor / 100L), \
           GetGValue(c1) + ((GetGValue(c2) - GetGValue(c1)) * factor / 100L), \
           GetBValue(c1) + ((GetBValue(c2) - GetBValue(c1)) * factor / 100L) )
#endif

#ifndef COLOR_INVALID
   #define COLOR_INVALID  (COLORREF) CLR_INVALID
#endif


#if _WTL_VER < 0x0750

/////////////////////////////////////////////////////////////////////////////
// CIcon

template< bool t_bManaged >
class CIconT
{
public:
   HICON m_hIcon;

   // Constructor/destructor/operators

   CIconT(HICON hIcon = NULL) : m_hIcon(hIcon)
   { 
   }

   ~CIconT()
   {
      if( t_bManaged && m_hIcon != NULL ) ::DestroyIcon(m_hIcon);
   }

   CIconT<t_bManaged>& operator=(HICON hIcon)
   {
      m_hIcon = hIcon;
      return *this;
   }

   void Attach(HICON hIcon)
   {
      if( t_bManaged && m_hIcon != NULL ) ::DestroyIcon(m_hIcon);
      m_hIcon = hIcon;
   }  
   HICON Detach()
   {
      HICON hIcon = m_hIcon;
      m_hIcon = NULL;
      return hIcon;
   }

   operator HICON() const { return m_hIcon; }

   bool IsNull() const { return m_hIcon == NULL; }

   // Create methods

   HICON LoadIcon(_U_STRINGorID icon)
   {
      ATLASSERT(m_hIcon==NULL);
#if (_ATL_VER >= 0x0700)
      m_hIcon = ::LoadIcon(ATL::_AtlBaseModule.GetResourceInstance(), icon.m_lpstr);
#else
      m_hIcon = ::LoadIcon(_Module.GetResourceInstance(), icon.m_lpstr);
#endif
      return m_hIcon;
   }
   HICON LoadIcon(_U_STRINGorID icon, int cxDesired, int cyDesired, UINT fuLoad = 0)
   {
      ATLASSERT(m_hIcon==NULL);
#if (_ATL_VER >= 0x0700)
      m_hIcon = (HICON) ::LoadImage(ATL::_AtlBaseModule.GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
#else
      m_hIcon = (HICON) ::LoadImage(_Module.GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
#endif
      return m_hIcon;
   }
   HICON LoadOEMIcon(UINT nIDIcon) // for IDI_ types
   {
      ATLASSERT(m_hIcon==NULL);
      m_hIcon = ::LoadIcon(NULL, MAKEINTRESOURCE(nIDIcon));
      return m_hIcon;
   }
   HICON CreateIcon(int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE* lpbANDButs, CONST BYTE *lpbXORbits)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(lpbANDbits);
      ATLASSERT(lpbXORbits);
#if (_ATL_VER >= 0x0700)
      m_hIcon = ::CreateIcon(ATL::_AtlBaseModule.GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits);
#else
      m_hIcon = ::CreateIcon(_Module.GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits);
#endif
      return m_hIcon;
   }
   HICON CreateIconFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(pBits);
      m_hIcon = ::CreateIconFromResource(pBits, dwResSize, TRUE, dwVersion);
      return m_hIcon;
   }
   HICON CreateIconFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(pbBits);
      ATLASSERT(cbBits>0);
      m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, TRUE, dwVersion, cxDesired,  cyDesired, uFlags);
      return m_hIcon;
   }
   HICON CreateIconIndirect(PICONINFO pIconInfo)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(pIconInfo);
      m_hIcon = ::CreateIconIndirect(pIconInfo);
      return m_hIcon;
   }
   HICON ExtractIcon(LPCTSTR lpszExeFileName, UINT nIconIndex)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(!::IsBadStringPtr(lpszExeFileName,-1));
#if (_ATL_VER >= 0x0700)      
      m_hIcon = ::ExtractIcon(ATL::_AtlBaseModule.GetModuleInstance(), lpszExeFileName, nIconIndex);
#else
      m_hIcon = ::ExtractIcon(_Module.GetModuleInstance(), lpszExeFileName, nIconIndex);
#endif
      return m_hIcon;
   }
   HICON ExtractAssociatedIcon(HINSTANCE hInst, LPCTSTR lpIconPath, LPWORD lpiIcon)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(!::IsBadStringPtr(lpIconPath,-1));
      ATLASSERT(lpiIcon);
      m_hIcon = ::ExtractAssociatedIcon(hInst, lpIconPath, lpiIcon);
      return m_hIcon;
   }

   // Operations

   BOOL DestroyIcon()
   {
      ATLASSERT(m_hIcon!=NULL);
      BOOL bRet = ::DestroyIcon(m_hIcon);
      if( bRet ) m_hIcon = NULL;
      return bRet;
   }
   HICON CopyIcon()
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::CopyIcon(m_hIcon);
   }
   HICON DuplicateIcon()
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::DuplicateIcon(NULL, m_hIcon);
   }

   BOOL DrawIcon(HDC hDC, int x, int y)
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::DrawIcon(hDC, x, y, m_hIcon);
   }
   BOOL DrawIcon(HDC hDC, POINT pt)
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::DrawIcon(hDC, pt.x, pt.y, m_hIcon);
   }
   BOOL DrawIconEx(HDC hDC, int x, int y, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::DrawIconEx(hDC, x, y, m_hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);
   }
   BOOL DrawIconEx(HDC hDC, POINT pt, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)
   {
      ATLASSERT(m_hIcon!=NULL);
      return ::DrawIconEx(hDC, pt.x, pt.y, m_hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);
   }

   BOOL GetIconInfo(PICONINFO pIconInfo)
   {
      ATLASSERT(m_hIcon!=NULL);
      ATLASSERT(pIconInfo);
      return ::GetIconInfo(m_hIcon, pIconInfo);
   }
};

typedef CIconT<true> CIcon;
typedef CIconT<false> CIconHandle;


/////////////////////////////////////////////////////////////////////////////
// CCursor

// Protect template against silly macro
#ifdef CopyCursor
   #undef CopyCursor
#endif

template< bool t_bManaged >
class CCursorT
{
public:
   HCURSOR m_hCursor;

   // Constructor/destructor/operators

   CCursorT(HCURSOR hCursor = NULL) : m_hCursor(hCursor)
   { 
   }

   ~CCursorT()
   {
      if( t_bManaged && m_hCursor != NULL ) ::DestroyCursor(m_hCursor);
   }

   CCursorT<t_bManaged>& operator=(HCURSOR hCursor)
   {
      m_hCursor = hCursor;
      return *this;
   }

   void Attach(HCURSOR hCursor)
   {
      if( t_bManaged && m_hCursor != NULL ) ::DestroyCursor(m_hCursor);
      m_hCursor = hCursor;
   }
   HCURSOR Detach()
   {
      HCURSOR hCursor = m_hCursor;
      m_hCursor = NULL;
      return hCursor;
   }

   operator HCURSOR() const { return m_hCursor; }

   bool IsNull() const { return m_hCursor == NULL; }

   // Create methods

   HCURSOR LoadCursor(_U_STRINGorID cursor)
   {
      ATLASSERT(m_hCursor==NULL);
#if (_ATL_VER >= 0x0700)
      m_hCursor = ::LoadCursor(ATL::_AtlBaseModule.GetResourceInstance(), cursor.m_lpstr);
#else
      m_hCursor = ::LoadCursor(_Module.GetResourceInstance(), cursor.m_lpstr);
#endif
      return m_hCursor;
   }
   HCURSOR LoadOEMCursor(UINT nIDCursor) // for IDC_ types
   {
      ATLASSERT(m_hCursor==NULL);
      m_hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(nIDCursor));
      return m_hCursor;
   }
   HICON LoadCursor(_U_STRINGorID cursor, int cxDesired, int cyDesired, UINT fuLoad = 0)
   {
      ATLASSERT(m_hCursor==NULL);
#if (_ATL_VER >= 0x0700)
      m_hCursor = (HCURSOR) ::LoadImage(ATL::_AtlBaseModule.GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
#else
      m_hCursor = (HCURSOR) ::LoadImage(_Module.GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
#endif
      return m_hCursor;
   }
   HCURSOR LoadCursorFromFile(LPCTSTR pstrFilename)
   {
      ATLASSERT(m_hCursor==NULL);
      ATLASSERT(!::IsBadStringPtr(pstrFilename,-1));
      m_hCursor = ::LoadCursorFromFile(pstrFilename);
      return m_hCursor;
   }
   HCURSOR CreateCursor(int xHotSpot, int yHotSpot, int nWidth, int nHeight, CONST VOID *pvANDPlane, CONST VOID *pvXORPlane)
   {
      ATLASSERT(m_hCursor==NULL);
#if (_ATL_VER >= 0x0700)
      m_hCursor = ::CreateCursor(ATL::_AtlBaseModule.GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane);
#else
      m_hCursor = ::CreateCursor(_Module.GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane);
#endif
      return m_hCursor;
   }
   HICON CreateCursorFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(pBits);
      m_hIcon = ::CreateIconFromResource(pBits, dwResSize, FALSE, dwVersion);
      return m_hIcon;
   }
   HICON CreateCursorFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)
   {
      ATLASSERT(m_hIcon==NULL);
      ATLASSERT(pbBits);
      ATLASSERT(cbBits>0);
      m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, FALSE, dwVersion, cxDesired,  cyDesired, uFlags);
      return m_hIcon;
   }
  
   // Operations

   BOOL DestroyCursor()
   {
      ATLASSERT(m_hCursor!=NULL);
      BOOL bRet = ::DestroyCursor(m_hCursor);
      if( bRet ) m_hCursor = NULL;
      return bRet;
   }

   HCURSOR CopyCursor()
   {
      ATLASSERT(m_hCursor!=NULL);
      return (HCURSOR) ::CopyIcon( (HICON) m_hCursor );
   }

#if(WINVER >= 0x0500)
   BOOL GetCursorInfo(LPCURSORINFO pCursorInfo)
   {
      ATLASSERT(m_hCursor!=NULL);
      ATLASSERT(pCursorInfo);
      return ::GetCursorInfo(pCursorInfo);
   }
#endif
};

typedef CCursorT<true> CCursor;
typedef CCursorT<false> CCursorHandle;


/////////////////////////////////////////////////////////////////////////////
// CAccelerator

template< bool t_bManaged >
class CAcceleratorT
{
public:
   HACCEL m_hAccel;

   // Constructor/destructor/operators

   CAcceleratorT(HACCEL hAccel = NULL) : m_hAccel(hAccel)
   { 
   }

   ~CAcceleratorT()
   {
      if( t_bManaged && m_hAccel != NULL ) ::DestroyAcceleratorTable(m_hAccel);
   }

   CAcceleratorT<t_bManaged>& operator=(HACCEL hAccel)
   {
      m_hAccel = hAccel;
      return *this;
   }

   void DestroyObject()
   {
      if( m_hAccel != NULL ) {
         ::DestroyAcceleratorTable(m_hAccel);
         m_hAccel = NULL;
      }
   }

   void Attach(HACCEL hAccel)
   {
      if( t_bManaged && m_hAccel != NULL ) ::DestroyAcceleratorTable(m_hAccel);
      m_hAccel = hAccel;
   }  
   HCURSOR Detach()
   {
      HACCEL hAccel = m_hAccel;
      m_hAccel = NULL;
      return hAccel;
   }

   operator HACCEL() const { return m_hAccel; }

   bool IsNull() const { return m_hAccel == NULL; }

   // Create methods

   HACCEL LoadAccelerators(_U_STRINGorID accel)
   {
      ATLASSERT(m_hAccel==NULL);
#if (_ATL_VER >= 0x0700)
      m_hAccel = ::LoadAccelerators(ATL::_AtlBaseModule.GetResourceInstance(), accel.m_lpstr);
#else
      m_hAccel = ::LoadAccelerators(_Module.GetResourceInstance(), accel.m_lpstr);
#endif
      return m_hAccel;
   }
   HACCEL CreateAcceleratorTable(LPACCEL pAccel, int cEntries)
   {
      ATLASSERT(m_hAccel==NULL);
      ATLASSERT(!::IsBadReadPtr(lpAccelDst, sizeof(ACCEL)*cEntries));
      m_hAccel = ::CreateAcceleratorTable(pAccel, cEntries);
      return m_hAccel;
   }

   // Operations

   int CopyAcceleratorTable(LPACCEL lpAccelDst, int cEntries)
   {
      ATLASSERT(m_hAccel!=NULL);
      ATLASSERT(!::IsBadWritePtr(lpAccelDst, sizeof(ACCEL)*cEntries));
      return ::CopyAcceleratorTable(m_hAccel, lpAccelDst, cEntries);
   }

   BOOL TranslateAccelerator(HWND hWnd, LPMSG pMsg)
   {
      ATLASSERT(m_hAccel!=NULL);
      ATLASSERT(::IsWindow(hWnd));
      ATLASSERT(pMsg);
      return ::TranslateAccelerator(hWnd, m_hAccel, pMsg);
   }
};

typedef CAcceleratorT<true> CAccelerator;
typedef CAcceleratorT<false> CAcceleratorHandle;


/////////////////////////////////////////////////////////////////////////////
// CLogFont

class CLogFont : public LOGFONT
{
public:
   CLogFont() 
   { 
      ::ZeroMemory( (LOGFONT*) this, sizeof(LOGFONT) );
   }
   CLogFont(const LOGFONT& lf) 
   { 
      Copy(&lf);
   }
   CLogFont(HFONT hFont)
   {
      ATLASSERT(::GetObjectType(hFont)==OBJ_FONT);
      ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*) this);
   }
   HFONT CreateFontIndirect() 
   { 
      return ::CreateFontIndirect(this); 
   }
   void SetBold() 
   { 
      lfWeight = FW_BOLD; 
   }
   BOOL IsBold() const 
   { 
      return lfWeight >= FW_BOLD; 
   }
   void MakeBolder(int iScale = 1)
   {
      lfWeight += FW_BOLD * iScale;
   }
   void MakeLarger(int iScale)
   {
      if( lfHeight > 0 ) lfHeight += iScale; else lfHeight -= iScale;
   }
   void SetHeight(long PointSize, HDC hDC = NULL) 
   { 
      // For MM_TEXT mapping mode...
      // NOTE: MulDiv() gives correct rounding.
      lfHeight = -MulDiv(PointSize, ::GetDeviceCaps(hDC, LOGPIXELSY), 72); 
   }
   long GetHeight(HDC hDC = NULL) const
   {
      // For MM_TEXT mapping mode...
      // NOTE: MulDiv() gives correct rounding.
      return MulDiv(-lfHeight, 72, ::GetDeviceCaps(hDC, LOGPIXELSY));
   }
   long GetDeciPointHeight(HDC hDC = NULL)
   {
      POINT ptOrg = { 0, 0 };
      ::DPtoLP(hDC, &ptOrg, 1);
      POINT pt = { 0, 0 };
      pt.y = abs(lfHeight) + ptOrg.y;
      ::LPtoDP(hDC,&pt,1);
      return MulDiv(pt.y, 720, ::GetDeviceCaps(hDC,LOGPIXELSY)); // 72 points/inch, 10 decipoints/point
   }
   void SetHeightFromDeciPoint(long DeciPtHeight, HDC hDC = NULL)
   {
      POINT pt;
      pt.y = MulDiv(::GetDeviceCaps(hDC, LOGPIXELSY), DeciPtHeight, 720); // 72 points/inch, 10 decipoints/point
      ::DPtoLP(hDC, &pt, 1);
      POINT ptOrg = { 0, 0 };
      ::DPtoLP(hDC, &ptOrg, 1);
      lfHeight = -abs(pt.y - ptOrg.y);
   }
   void SetCaptionFont()
   {
      NONCLIENTMETRICS ncm = { 0 };
      ncm.cbSize = sizeof(ncm);
      ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
      Copy(&ncm.lfCaptionFont);
   }
   void SetMenuFont()
   {
      NONCLIENTMETRICS ncm = { 0 };
      ncm.cbSize = sizeof(ncm);
      ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
      Copy(&ncm.lfMenuFont);
   }
   void SetStatusFont()
   {
      NONCLIENTMETRICS ncm = { 0 };
      ncm.cbSize = sizeof(ncm);
      ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
      Copy(&ncm.lfStatusFont);
   }
   void SetMessageBoxFont()
   {
      NONCLIENTMETRICS ncm = { 0 };
      ncm.cbSize = sizeof(ncm);
      ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
      Copy(&ncm.lfMessageFont);
   }
   void Copy(const LOGFONT* lf)
   {
      ATLASSERT(lf);
      ::CopyMemory( (LOGFONT*) this, lf, sizeof(LOGFONT) );
   }
   CLogFont& operator=(const CLogFont& src)
   {
      Copy(&src);
      return *this;
   }
   CLogFont& operator=(const LOGFONT& src)
   {
      Copy(&src);
      return *this;
   }
   CLogFont& operator=(HFONT hFont)
   {
      ATLASSERT(::GetObjectType(hFont)==OBJ_FONT);
      ::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*) this);
      return *this;
   }
   bool operator==(const LOGFONT& logfont) const
   {
      return( logfont.lfHeight == lfHeight &&
         logfont.lfWidth == lfWidth &&
         logfont.lfEscapement == lfEscapement &&
         logfont.lfOrientation == lfOrientation &&
         logfont.lfWeight == lfWeight &&
         logfont.lfItalic == lfItalic &&
         logfont.lfUnderline == lfUnderline &&
         logfont.lfStrikeOut == lfStrikeOut &&
         logfont.lfCharSet == lfCharSet &&
         logfont.lfOutPrecision == lfOutPrecision &&
         logfont.lfClipPrecision == lfClipPrecision &&
         logfont.lfQuality == lfQuality &&
         logfont.lfPitchAndFamily == lfPitchAndFamily &&
         ::lstrcmp(logfont.lfFaceName, lfFaceName) == 0 );
   }
};

#endif // _WTL_VER


/////////////////////////////////////////////////////////////////////////////
// CMemDC

class CMemDC : public CDC
{
public:
   CDCHandle     m_dc;          // Owner DC
   CBitmap       m_bitmap;      // Offscreen bitmap
   CBitmapHandle m_hOldBitmap;  // Originally selected bitmap
   RECT          m_rc;          // Rectangle of drawing area

   CMemDC(HDC hDC, LPRECT pRect = NULL)
   {
      ATLASSERT(hDC!=NULL);
      m_dc = hDC;
      if( pRect != NULL ) m_rc = *pRect; else m_dc.GetClipBox(&m_rc);

      CreateCompatibleDC(m_dc);
      ::LPtoDP(m_dc, (LPPOINT) &m_rc, sizeof(RECT) / sizeof(POINT));
      m_bitmap.CreateCompatibleBitmap(m_dc, m_rc.right - m_rc.left, m_rc.bottom - m_rc.top);
      m_hOldBitmap = SelectBitmap(m_bitmap);
      ::DPtoLP(m_dc, (LPPOINT) &m_rc, sizeof(RECT) / sizeof(POINT));
      SetWindowOrg(m_rc.left, m_rc.top);
   }
   ~CMemDC()
   {
      // Copy the offscreen bitmap onto the screen.
      m_dc.BitBlt(m_rc.left, m_rc.top, m_rc.right - m_rc.left, m_rc.bottom - m_rc.top,
                  m_hDC, m_rc.left, m_rc.top, SRCCOPY);
      // Swap back the original bitmap.
      SelectBitmap(m_hOldBitmap);
   }
};


/////////////////////////////////////////////////////////////////////////////
// COffscreenDraw

// To use it, derive from it and chain it in the message map.
template< class T >
class COffscreenDraw
{
public:
   BEGIN_MSG_MAP(COffscreenDraw)
      MESSAGE_HANDLER(WM_PAINT, OnPaint)
      MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
      MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
   END_MSG_MAP()

   LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      T* pT = static_cast<T*>(this);
      if( wParam != NULL )
      {
         CMemDC memdc( (HDC) wParam, NULL );
         pT->DoPaint(memdc.m_hDC);
      }
      else
      {
         RECT rc;
         pT->GetClientRect(&rc);
         CPaintDC dc(pT->m_hWnd);
         CMemDC memdc(dc.m_hDC, &rc);
         pT->DoPaint(memdc.m_hDC);
      }
      return 0;
   }
   LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      return 1; // handled; no need to erase background; do it in DoPaint();
   }
   void DoPaint(CDCHandle dc)
   {
      ATLASSERT(false); // must override this
   }
};

// To use it, derive from it and chain it in the message map.
template< class T >
class COffscreenDrawRect
{
public:
   BEGIN_MSG_MAP(COffscreenDrawRect)
      MESSAGE_HANDLER(WM_PAINT, OnPaint)
      MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
      MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
   END_MSG_MAP()

   LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      T* pT = static_cast<T*>(this);
      if( wParam != NULL )
      {
         CMemDC memdc( (HDC) wParam, NULL );
         pT->DoPaint(memdc.m_hDC, memdc.m_rc);
      }
      else
      {
         CPaintDC dc(pT->m_hWnd);
         CMemDC memdc(dc.m_hDC, &dc.m_ps.rcPaint);
         pT->DoPaint(memdc.m_hDC, dc.m_ps.rcPaint);
      }
      return 0;
   }
   LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      return 1; // handled; no need to erase background; do it in DoPaint();
   }
   void DoPaint(CDCHandle dc, RECT& rcClip)
   {
      ATLASSERT(false); // must override this
   }
};


/////////////////////////////////////////////////////////////////////////////
// CSaveDC

class CSaveDC
{
public:
   HDC m_hDC;
   int m_iState;

   CSaveDC(HDC hDC) : m_hDC(hDC)
   {
      ATLASSERT(::GetObjectType(m_hDC)==OBJ_DC || ::GetObjectType(m_hDC)==OBJ_MEMDC);
      m_iState = ::SaveDC(hDC);
      ATLASSERT(m_iState!=0);
   }
   ~CSaveDC()
   {
      Restore();
   }
   void Restore()
   {
      if( m_iState == 0 ) return;
      ATLASSERT(::GetObjectType(m_hDC)==OBJ_DC || ::GetObjectType(m_hDC)==OBJ_MEMDC);
      ::RestoreDC(m_hDC, m_iState);
      m_iState = 0;
   }
};


/////////////////////////////////////////////////////////////////////////////
// CHandle

#if (_ATL_VER < 0x0700)

class CHandle
{
public:
   HANDLE m_h;

   CHandle(HANDLE hSrc = INVALID_HANDLE_VALUE) : m_h(hSrc)
   { }

   ~CHandle()
   {
      Close();
   }

   operator HANDLE() const { return m_h; };
  
   LPHANDLE operator&()
   {
      ATLASSERT(!IsValid());
      return &m_h;
   }

   CHandle& operator=(HANDLE h)
   {
      ATLASSERT(!IsValid());
      m_h = h;
      return *this;
   }

   bool IsValid() const { return m_h != INVALID_HANDLE_VALUE; };
   
   void Attach(HANDLE h)
   {
      if( IsValid() ) ::CloseHandle(m_h);
      m_h = h;
   }   
   HANDLE Detach()
   {
      HANDLE h = m_h;
      m_h = INVALID_HANDLE_VALUE;
      return h;
   }
   
   BOOL Close()
   {
      BOOL bRes = FALSE;
      if( m_h != INVALID_HANDLE_VALUE ) {
         bRes = ::CloseHandle(m_h);
         m_h = INVALID_HANDLE_VALUE;
      }
      return bRes;
   }

   BOOL Duplicate(HANDLE hSource, bool bInherit = false)
   {
      ATLASSERT(!IsValid());
      HANDLE hOurProcess = ::GetCurrentProcess();
      BOOL b = ::DuplicateHandle(hOurProcess, 
         hSource,
         hOurProcess, 
         &m_h,
         DUPLICATE_SAME_ACCESS,
         bInherit,
         DUPLICATE_SAME_ACCESS);
      ATLASSERT(b);
      return b;
   }
};

#endif // _ATL_VER


/////////////////////////////////////////////////////////////////////////////
// Mouse Hover helper

#ifndef NOTRACKMOUSEEVENT

#ifndef WM_MOUSEENTER
   #define WM_MOUSEENTER WM_USER + 253
#endif // WM_MOUSEENTER

// To use it, derive from it and chain it in the message map.
// Make sure to set bHandled to FALSE when handling WM_MOUSEMOVE or
// the WM_MOUSELEAVE message!
template< class T >
class CMouseHover
{
public:   
   bool m_fMouseOver;          // Internal mouse-over state
   bool m_fMouseForceUpdate;   // Update window immediately on event

   CMouseHover() : 
      m_fMouseOver(false),
      m_fMouseForceUpdate(true)
   {
   }

   void SetForceMouseOverUpdate(bool bForce = false)
   {
      m_fMouseForceUpdate = bForce;
   }

   BEGIN_MSG_MAP(CMouseHover)
      MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
      MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
   END_MSG_MAP()

   LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
   {
      T* pT = static_cast<T*>(this);
      if( !m_fMouseOver )   {
         m_fMouseOver = true;
         pT->SendMessage(WM_MOUSEENTER, wParam, lParam);         
         if( m_fMouseForceUpdate ) {
            pT->Invalidate();
            pT->UpdateWindow();
         }
         _StartTrackMouseLeave(pT->m_hWnd);
      }
      bHandled = FALSE;
      return 0;
   }
   LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
   {
      T* pT = static_cast<T*>(this);
      if( m_fMouseOver ) {
         m_fMouseOver = false;         
         if( m_fMouseForceUpdate ) {
            pT->Invalidate();
            pT->UpdateWindow();
         }
      }
      bHandled = FALSE;
      return 0;
   }
   BOOL _StartTrackMouseLeave(HWND hWnd) const
   {
      ATLASSERT(::IsWindow(hWnd));
      TRACKMOUSEEVENT tme = { 0 };
      tme.cbSize = sizeof(tme);
      tme.dwFlags = TME_LEAVE;
      tme.hwndTrack = hWnd;
      return _TrackMouseEvent(&tme);
   }
   BOOL _CancelTrackMouseLeave(HWND hWnd) const
   {
      TRACKMOUSEEVENT tme = { 0 };
      tme.cbSize = sizeof(tme);
      tme.dwFlags = TME_LEAVE | TME_CANCEL;
      tme.hwndTrack = hWnd;
      return _TrackMouseEvent(&tme);
   }
};

#endif // NOTRACKMOUSEEVENT


}; // namespace WTL

#endif // __ATLGDIX_H__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Daniel Bowen
Architect
United States United States
Daniel Bowen used to work as a Software Engineer for Evans & Sutherland in Salt Lake City, Utah working on the modeling tools for high end flight simulators. He then worked for the startup company WiLife Inc. in Draper, Utah working on the software portion of an easy to use and affordable digital video surveillance system. WiLife Inc. is now part of Logitech Inc.

| Advertise | Privacy | Mobile
Web03 | 2.8.140926.1 | Last Updated 14 Jul 2005
Article Copyright 2002 by Daniel Bowen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid