Click here to Skip to main content
15,897,371 members
Articles / Desktop Programming / MFC
Article

Transparent Static Text In Dialogs

Rate me:
Please Sign up or sign in to vote.
4.39/5 (25 votes)
4 Jul 20043 min read 194.8K   7.6K   61   29
A simple way to make static text controls draw transparently over the dialog background.

Sample Image - TransWiz.gif

Introduction

Recently, I was implementing a PropertySheet based wizard, and part of the graphic design was to place a bitmap as the window background and then draw the text and other controls over that. I found a few examples of using a bitmap as a dialog background - the best of which simply involved overriding the default WM_ERASEBKGND behavior.

That worked fine, but of course, the Static text controls were still being drawn on their beige backgrounds. I found a couple of ideas about drawing transparent text, but they generally involved overriding WM_PAINT and doing all the text drawing yourself. I was hoping for something simpler.

There are numerous articles about overriding WM_CTLCOLOR, and they generally discuss using this as a way to change the text color or the background color. I was put off for a long time by the name 'WM_CTLCOLOR' thinking that it was just about color. However, you can change several characteristics of the drawing environment here just before a control is drawn.

It is possible to call SetBkMode and set the background mode to TRANSPARENT which keeps the text drawing routines from erasing before drawing text. This is not quite enough however because when a Static control is drawn, the entire control background is first painted with the HBRUSH returned by OnCtlColor. Fortunately, having OnCtlColor return the NULL_BRUSH takes care of this as well.

Using the code

This example implements a PropertySheet based wizard. The same techniques would apply to a Dialog.

To change the CStatic text behavior to transparent drawing requires adding one message handler with four lines of code.

I create a sub-class of CPropertyPage (for a regular dialog, you would create a sub-class of CDialog). Then I use ClassWizard to create a handler for WM_CTLCOLOR. In that handler, OnCtlColor, I check the nCtlColor parameter, and if it shows that we are about to draw a Static control (CTLCOLOR_STATIC) then I call SetBkMode to change the DC mode to TRANSPARENT. I also get a handle to the stock NULL_BRUSH object and return that as the function result. Then Windows takes care of all the text drawing itself.

You can change a number of things about the DC in OnCtlColor. All of the various CDC functions are available such as SetTextColor which allows you to change the text color. Of special note is the ability to use CDC::SelectObject which allows you to change the Pen, Brush, Font, Bitmap and Region. The ability to modify the font here is especially useful in working with PropertySheets because there is no way to set the face, size, or style of the font used (PropertySheets always use MS Shell).

This example also implements using a bitmap for the dialog background. This is done by using ClassWizard to create a handler for WM_ERASEBKGND in the CPropertyPage (CDialog) sub-class. The OnEraseBkgrnd routine then fills the window with the bitmap instead of painting with the background brush.

History

  • 1.0 -15 Jul 2004 - first version.

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


Written By
Web Developer
United States United States
Software engineer working on commercial applications used in color management.

Comments and Discussions

 
GeneralRe: Alternative Pin
talbot23-Feb-07 4:14
talbot23-Feb-07 4:14 
I did it by this way. You can reuse it:

//NEEDED - Whole file

#if !defined(AFX_TRANSPARENTSTATIC_H__EF437069_0E0C_4816_B8AC_E40D2AD0C02D__INCLUDED_)
#define AFX_TRANSPARENTSTATIC_H__EF437069_0E0C_4816_B8AC_E40D2AD0C02D__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TransparentStatic.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CTransparentStatic window

class CTransparentStatic : public CStatic
{
// Construction
public:
static CBrush m_brushTransparent;

CTransparentStatic();
void SetWindowText(LPCTSTR lpszString);

// Attributes
public:

// Operations
public:

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTransparentStatic)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CTransparentStatic();

// Generated message map functions
protected:
//{{AFX_MSG(CTransparentStatic)
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TRANSPARENTSTATIC_H__EF437069_0E0C_4816_B8AC_E40D2AD0C02D__INCLUDED_)




//NEEDED - Whole file

// TransparentStatic.cpp : implementation file
//

#include "stdafx.h"
#include "vs6transparentstatic.h"
#include "TransparentStatic.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTransparentStatic

CBrush CTransparentStatic::m_brushTransparent;

CTransparentStatic::CTransparentStatic()
{
if (m_brushTransparent.GetSafeHandle())
return;
m_brushTransparent.CreateStockObject(NULL_BRUSH);
}

CTransparentStatic::~CTransparentStatic()
{
}


BEGIN_MESSAGE_MAP(CTransparentStatic, CStatic)
//{{AFX_MSG_MAP(CTransparentStatic)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CTransparentStatic message handlers



void CTransparentStatic::SetWindowText(LPCTSTR lpszString)
{
CWnd *pParent = GetParent();
if (pParent)
{
CRect rcStatic;
GetWindowRect(rcStatic);
pParent->ScreenToClient(rcStatic);
pParent->InvalidateRect(rcStatic);
pParent->SendMessage(WM_PAINT);
}

CStatic::SetWindowText(lpszString);
}



BOOL CTransparentStatic::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}







// VS6TransparentStaticDlg.h : header file
//

#if !defined(AFX_VS6TRANSPARENTSTATICDLG_H__F0DDB6B4_6447_4FD0_BE53_93214E7672D5__INCLUDED_)
#define AFX_VS6TRANSPARENTSTATICDLG_H__F0DDB6B4_6447_4FD0_BE53_93214E7672D5__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000



#include "TransparentStatic.h"



/////////////////////////////////////////////////////////////////////////////
// CVS6TransparentStaticDlg dialog

class CVS6TransparentStaticDlg : public CDialog
{
// Construction
public:
CVS6TransparentStaticDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
//{{AFX_DATA(CVS6TransparentStaticDlg)
enum { IDD = IDD_VS6TRANSPARENTSTATIC_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CVS6TransparentStaticDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
HICON m_hIcon;

//NEEDED
CTransparentStatic m_oTS;

// Generated message map functions
//{{AFX_MSG(CVS6TransparentStaticDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();

//NEEDED
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);

afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
afx_msg BOOL OnEraseBkgnd(CDC *pDC);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_VS6TRANSPARENTSTATICDLG_H__F0DDB6B4_6447_4FD0_BE53_93214E7672D5__INCLUDED_)






// VS6TransparentStaticDlg.cpp : implementation file
//

#include "stdafx.h"
#include "VS6TransparentStatic.h"
#include "VS6TransparentStaticDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CVS6TransparentStaticDlg dialog

CVS6TransparentStaticDlg::CVS6TransparentStaticDlg(CWnd* pParent /*=NULL*/)
: CDialog(CVS6TransparentStaticDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CVS6TransparentStaticDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CVS6TransparentStaticDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CVS6TransparentStaticDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CVS6TransparentStaticDlg, CDialog)
//{{AFX_MSG_MAP(CVS6TransparentStaticDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()

//NEEDED
ON_WM_CTLCOLOR()

ON_WM_TIMER()
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVS6TransparentStaticDlg message handlers

BOOL CVS6TransparentStaticDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

//NEEDED
CWnd *pStatic = GetDlgItem(IDC_S);
if (pStatic)
m_oTS.SubclassWindow(pStatic->GetSafeHwnd());

SetTimer(1, 50, NULL);

return TRUE; // return TRUE unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.

void CVS6TransparentStaticDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CVS6TransparentStaticDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

HBRUSH CVS6TransparentStaticDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
//NEEDED
if (nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetBkMode(TRANSPARENT);
return CTransparentStatic::m_brushTransparent;
}

return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}

void CVS6TransparentStaticDlg::OnTimer(UINT nIDEvent)
{
static int i = 0;
CString str;
str.Format(_T("%d"), i++);

//NEEDED - Invokation of overriden CTransparentStatic::SetWindowText()
CTransparentStatic *pS = (CTransparentStatic *)GetDlgItem(IDC_S);
pS->SetWindowText(str);

CDialog::OnTimer(nIDEvent);
}

void CVS6TransparentStaticDlg::OnDestroy()
{
CDialog::OnDestroy();

KillTimer(1);
}


BOOL CVS6TransparentStaticDlg::OnEraseBkgnd(CDC *pDC)
{
CRect rc;
GetClientRect(rc);
CBrush brush;
brush.CreateHatchBrush(HS_DIAGCROSS, 0);
pDC->FillRect(rc, &brush);

return TRUE;
}







B.
GeneralRe: Alternative Pin
cristitomi21-Mar-07 21:25
cristitomi21-Mar-07 21:25 
GeneralRe: Alternative [modified] Pin
ashwinjam30-Jun-08 21:28
ashwinjam30-Jun-08 21:28 
GeneralOn Windows CE... Pin
Johann Gerell5-Jul-04 20:56
Johann Gerell5-Jul-04 20:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.