An auto-sizing bitmap picture control

CBitmapPicture image Download source files (2.8Kb) or sample project (20.7Kb)

The Picture control available from the dialog editor component bar is great for quickly displaying a picture in a dialog, but it only displays the picture at the original picture's size. A problem occurs if you want to display a bitmap which must be aligned with other controls (eg. a bitmap of an arrow). If you change the size of the dialog box font, then the size and position of each control will also change, but the size of the displayed bitmap will not, resulting in a mis-aligned picture. The problem also occurs if the system font size is changed (The matrox millenium drivers allow you to do this).

To overcome this problem I wrote a CStatic derived class that displays a bitmap according to the size of the underlying CStatic window. When the font size changes, the CStatic window size changes, and the bitmap will be StretchBlt'd to the new size. This allows images to be displayed smaller and larger than their original size.

The easist way to use this class is to add the CBitmapPicture class to your project then create a CStatic object to yuour dialog, and attach a member variable of type CBitmapPicture to the object. Then in your OnInitDialog function, call CBitmapPicture::SetBitmap() to set the bitmap to be used.
    BOOL SetBitmap(UINT nIDResource);           // Loads bitmap from resource ID
    BOOL SetBitmap(LPCTSTR lpszResourceName);   // Loads bitmap from resource name
    BOOL SetBitmap(HBITMAP hBitmap);            // Not recommended, as reloads can't be done

The implementation handles the SYSCOLORCHANGE message as described by Pel K. Txnder in his article //www.codeguru.com/misc/syscol_static.shtml"> CStatic with bitmap sensitive to change in system colours.

Source:


#if !defined(AFX_BITMAPPICTURE_H__A4BE2021_689E_11D1_ABBA_00A0243D1382__INCLUDED_)
#define AFX_BITMAPPICTURE_H__A4BE2021_689E_11D1_ABBA_00A0243D1382__INCLUDED_

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

// BitmapPicture.h : header file
//
// Copyright (c) Chris Maunder (Chris.Maunder@cbr.clw.csiro.au) 
// Written 1 December, 1997


/////////////////////////////////////////////////////////////////////////////
// CBitmapPicture window

class CBitmapPicture : public CStatic
{
// Construction
public:
    CBitmapPicture();
    
// Operations
public:
    BOOL SetBitmap(HBITMAP hBitmap);            // Not recommended
    BOOL SetBitmap(UINT nIDResource);
    BOOL SetBitmap(LPCTSTR lpszResourceName);
    BOOL ReloadBitmap();

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CBitmapPicture)
    protected:
    virtual void PreSubclassWindow();
    virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CBitmapPicture();

// Attributes
protected:
    HBITMAP  m_hBitmap;
    BITMAP   m_bmInfo;

private:
    int     m_nResourceID;
    CString m_strResourceName;

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

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BITMAPPICTURE_H__A4BE2021_689E_11D1_ABBA_00A0243D1382__INCLUDED_)


// BitmapPicture.cpp : implementation file
//
// Copyright (c) 1997 Chris Maunder (Chris.Maunder@cbr.clw.csiro.au)
// Written 1 December, 1997

#include "stdafx.h"
#include "BitmapPicture.h"

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

// if UPDATE_ENTIRE_CLIENT_AREA is not defined then only the update region of
// the bitmap is updated. Otherwise, on each update, the whole client area of
// the bitmap is drawn. UPDATE_ENTIRE_CLIENT_AREA is slower, but distortion 
// of the picture may occur if it is not defined.

#define UPDATE_ENTIRE_CLIENT_AREA

/////////////////////////////////////////////////////////////////////////////
// CBitmapPicture

CBitmapPicture::CBitmapPicture()
{
    m_hBitmap = NULL;

    m_nResourceID = -1;
    m_strResourceName.Empty();
}

CBitmapPicture::~CBitmapPicture()
{
    if (m_hBitmap) ::DeleteObject(m_hBitmap);
}

BEGIN_MESSAGE_MAP(CBitmapPicture, CStatic)
    //{{AFX_MSG_MAP(CBitmapPicture)
    ON_WM_ERASEBKGND()
    ON_WM_DRAWITEM_REFLECT()
    ON_WM_SYSCOLORCHANGE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CBitmapPicture message handlers

BOOL CBitmapPicture::SetBitmap(HBITMAP hBitmap)
{
    ::DeleteObject(m_hBitmap);
    m_hBitmap = hBitmap;
    return ::GetObject(m_hBitmap, sizeof(BITMAP), &m_bmInfo);
}

BOOL CBitmapPicture::SetBitmap(UINT nIDResource)
{
    m_nResourceID = nIDResource;
    m_strResourceName.Empty();

    HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), 
                                        MAKEINTRESOURCE(nIDResource),
                                        IMAGE_BITMAP, 
                                        0,0, 
                                        LR_LOADMAP3DCOLORS);
    if (!hBmp) return FALSE;
    return CBitmapPicture::SetBitmap(hBmp);
}

BOOL CBitmapPicture::SetBitmap(LPCTSTR lpszResourceName)
{
    m_nResourceID = -1;
    m_strResourceName = lpszResourceName;

    HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), 
                                        lpszResourceName,
                                        IMAGE_BITMAP, 
                                        0,0, 
                                        LR_LOADMAP3DCOLORS);
    if (!hBmp) return FALSE;
    return CBitmapPicture::SetBitmap(hBmp);
}

// Suggested by Pel K. Used to reload the bitmap on system colour changes.
BOOL CBitmapPicture::ReloadBitmap()
{
    if (m_nResourceID > 0) 
        return SetBitmap(m_nResourceID);
    else if (!m_strResourceName.IsEmpty())
        return SetBitmap(m_strResourceName);
    else    // if SetBitmap(HBITMAP hBitmap) was used directly then we can't reload.
        return FALSE;
}

void CBitmapPicture::PreSubclassWindow() 
{
    CStatic::PreSubclassWindow();
    ModifyStyle(0, SS_OWNERDRAW);
}

BOOL CBitmapPicture::OnEraseBkgnd(CDC* pDC) 
{
    CRect rect;
    GetClientRect(rect);

    // If no bitmap selected, simply erase the background as per normal and return
    if (!m_hBitmap)
    {
        CBrush backBrush(::GetSysColor(COLOR_3DFACE)); // (this is meant for dialogs)
        CBrush* pOldBrush = pDC->SelectObject(&backBrush);

        pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
        pDC->SelectObject(pOldBrush);

        return TRUE;
    }

    // We have a bitmap - draw it.

    // Create compatible memory DC using the controls DC
    CDC dcMem;
    VERIFY( dcMem.CreateCompatibleDC(pDC));
    
    // Select bitmap into memory DC.
    HBITMAP* pBmpOld = (HBITMAP*) ::SelectObject(dcMem.m_hDC, m_hBitmap);

    // StretchBlt bitmap onto static's client area
#ifdef UPDATE_ENTIRE_CLIENT_AREA
    pDC->StretchBlt(rect.left, rect.top, rect.Width(), rect.Height(), 
                     &dcMem, 0, 0, m_bmInfo.bmWidth-1, m_bmInfo.bmHeight-1,
                     SRCCOPY);
#else
    CRect TargetRect;                // Region on screen to be updated
    pDC->GetClipBox(&TargetRect);
    TargetRect.IntersectRect(TargetRect, rect);

    CRect SrcRect;                    // Region from bitmap to be painted
    SrcRect.left    = MulDiv(TargetRect.left,   m_bmInfo.bmWidth,  rect.Width());
    SrcRect.top     = MulDiv(TargetRect.top,    m_bmInfo.bmHeight, rect.Height());
    SrcRect.right   = MulDiv(TargetRect.right,  m_bmInfo.bmWidth,  rect.Width());
    SrcRect.bottom  = MulDiv(TargetRect.bottom, m_bmInfo.bmHeight, rect.Height());    

    pDC->StretchBlt(TargetRect.left, TargetRect.top, TargetRect.Width(), TargetRect.Height(), 
                    &dcMem, 
                    SrcRect.left, SrcRect.top, SrcRect.Width(), SrcRect.Height(),
                    SRCCOPY);
#endif

    ::SelectObject(dcMem.m_hDC, pBmpOld);

    return TRUE;
}

void CBitmapPicture::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    ASSERT(lpDrawItemStruct != NULL);
    
    CString str;
    GetWindowText(str);
    if (!str.GetLength()) return;

    CDC*  pDC     = CDC::FromHandle(lpDrawItemStruct->hDC);
    CRect rect    = lpDrawItemStruct->rcItem;
    DWORD dwStyle = GetStyle();
    int   nFormat = DT_NOPREFIX | DT_NOCLIP | DT_WORDBREAK | DT_SINGLELINE;

    if (dwStyle & SS_CENTERIMAGE) nFormat |= DT_VCENTER;
    if (dwStyle & SS_CENTER)      nFormat |= DT_CENTER;
    else if (dwStyle & SS_RIGHT)  nFormat |= DT_RIGHT;
    else                          nFormat |= DT_LEFT;

    int nOldMode = pDC->SetBkMode(TRANSPARENT);
    pDC->DrawText(str, rect, nFormat);
    pDC->SetBkMode(nOldMode);
}

// Suggested by Pel K. Txnder.
void CBitmapPicture::OnSysColorChange() 
{
    CStatic::OnSysColorChange();
    ReloadBitmap(); 
}



Comments

  • Drawing on CStatic PictureBoxControl in evc

    Posted by Legacy on 06/07/2002 12:00am

    Originally posted by: Gaurav

    Hi,
    
    I have created a class derived from CStatic. I have specified SS_BITMAP in its create parameters. I capture mouse movements and draw lines accordingly.
    class CMyPB : public CStatic
    {

    void CMyPB::OnMouseMove(UINT nFlags, CPoint point)
    {
    CDC * pDC = this->GetDC();
    pDC->Polygon(pts,2);
    }

    I get the line drawn this way. But when I do
    this->GetBitmap() I get NULL.
    I have also tried creating the bitmap using CreateCompatibleBitmap in parent OnInitDialog method. but get NULL ther also.
    BOOL CSetupComplete::OnInitDialog()
    {
    ...
    CDC * pDC = this->m_Signature.GetDC();
    HBITMAP bit = ::CreateCompatibleBitmap( HDC(pDC),
    GetDeviceCaps(HDC(pDC), HORZRES),
    GetDeviceCaps(HDC(pDC), VERTRES));
    this->m_Signature.SetBitmap(bit);
    ...
    }
    the bit is NULL.
    Can someone help?
    Is there any way to implement Signature capture kind of control?
    Regards
    Gaurav

    Reply
  • How to use to add the background bitmap for property page

    Posted by Legacy on 05/14/2002 12:00am

    Originally posted by: shashi

    Hello frinds,
    I would like to add bitmap as a background in property page
    so hiw i supposed to add bitmap in property page.(i'm using CPropertypage class)

    thanks in advance,
    shashi

    Reply
  • A new Method

    Posted by Legacy on 02/10/2002 12:00am

    Originally posted by: Gasparote

    Hey Chris,
    
    

    Excellent contribution, I just added this method, in order to have a clearing function...

    void CBitmapPicture::Clear()
    {
    CRect rect;
    GetClientRect(rect);

    CDC* pDC = CStatic::GetDC();

    CBrush backBrush(::GetSysColor(COLOR_3DFACE));
    CBrush* pOldBrush = pDC->SelectObject(&backBrush);

    pDC->PatBlt(rect.left, rect.top,
    rect.Width(), rect.Height(), PATCOPY);
    pDC->SelectObject(pOldBrush);
    }

    Reply
  • File Support

    Posted by Legacy on 08/29/2001 12:00am

    Originally posted by: Ken

    To load the image from a file, the following will work
    
    

    use 'LR_LOADFROMFILE' instead of 'LOADMAP3DCOLORS'

    For example:

    BOOL CBitmapPicture::SetBitmap(LPCTSTR lpszResourceName)
    {
    m_nResourceID = -1;
    m_strResourceName = lpszResourceName;

    HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
    lpszResourceName,
    IMAGE_BITMAP,
    0,0,
    LR_LOADFROMFILE);
    if (!hBmp) return FALSE;
    return CBitmapPicture::SetBitmap(hBmp);
    }

    Reply
  • To load a file

    Posted by Legacy on 12/13/2000 12:00am

    Originally posted by: Thorben Stangenberg

    Just use the

    BOOL CBitmapPicture::SetBitmap(LPCTSTR lpszResourceName)

    method and set lpszResourceName to your Filename.

    Change the LoadImage function to this

    HBITMAP hBmp = (HBITMAP)::LoadImage(
    AfxGetResourceHandle(), lpszResourceName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );


    Reply
  • Thank's for the great little tutorial

    Posted by Legacy on 08/29/2000 12:00am

    Originally posted by: Tom

    This is the information I needed. And I found the tutorial style especially helpful. Thanks a bunch.

    Reply
  • File Support?

    Posted by Legacy on 08/18/2000 12:00am

    Originally posted by: Tom Russell

    I would like to be able to load images from files, also. It would really be nice if I could implement drag/drop, but this may require OLE.

    Reply
  • Question: File Support?

    Posted by Legacy on 09/22/1999 12:00am

    Originally posted by: Clifford

    Will loading and switching the bitmaps from files be easily supported?
    Thanks
    Clifford

    Reply
  • Little correction

    Posted by Legacy on 05/28/1999 12:00am

    Originally posted by: Adeluc

    In the the following function:
    
    

    void CBitmapPicture::PreSubclassWindow()
    {
    CStatic::PreSubclassWindow();
    ModifyStyle(0, SS_OWNERDRAW);
    }

    It is preferable to use this call...
    SetButtonStyle( SS_OWNERDRAW, FALSE );

    because ModifyStyle() use binary AND/OR operations and button styles are not binary values but sequential values.

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds