MFC MDI: How to change the background of the MDI main frame?
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3

Thread: MFC MDI: How to change the background of the MDI main frame?

  1. #1
    ovidiucucu's Avatar
    ovidiucucu is offline Moderator/Reviewer Power Poster
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,058

    MFC MDI: How to change the background of the MDI main frame?

    Q: How can I change the background of the MDI main frame?

    A: In fact, on the main frame background lies another window of 'MDIClient' class. So do the following:


    • Using ClassWizard, add a generic CWnd-derived class, let's say it CMDIClientWnd


    • Add a member of type CMDIClientWnd to CMainFrame class

      Code:
      class CMainFrame : public CMDIFrameWnd
      {
      // ...
      // Attributes
      protected:
         CMDIClientWnd m_wndMDIClient;
      // ...
      };
    • In CMainFrame::OnCreate subclass the MDI client window

      Code:
      int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
      {
         if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
         // NOTE: m_hWndMDIClient is a public member of CMDIFrameWnd
         m_wndMDIClient.SubclassWindow(m_hWndMDIClient);
      // ...
      }
    • Add a member of type CBrush to class CMDIClientWnd

      Code:
      class CMDIClientWnd : public CWnd
      {
      // Attributes
      protected:
         CBrush  m_brush; // will be used to fill the backgound
      };
    • Create the brush in CMDIClientWnd constructor

      Code:
      CMDIClientWnd::CMDIClientWnd()
      {
         // this example uses a pattern brush filled from a bitmap resource
         CBitmap bitmap;
         bitmap.LoadBitmap(IDB_FISHING);
         m_brush.CreatePatternBrush(&bitmap);
      }
    • Finally, handle 'WM_ERASEBKGND'

      Code:
      BOOL CMDIClientWnd::OnEraseBkgnd(CDC* pDC) 
      {
         CBrush* pbrushOld = pDC->SelectObject(&m_brush);
         CRect rect;
         pDC->GetClipBox(&rect);
         pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
         pDC->SelectObject(pbrushOld);
      
         return TRUE;
      }


    Last edited by Andreas Masur; July 25th, 2005 at 05:07 PM.

  2. #2
    ovidiucucu's Avatar
    ovidiucucu is offline Moderator/Reviewer Power Poster
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,058

    MFC MDI: How to change the background of the MDI main frame? (2)

    Q: I have changed the MDI client background like in example shown above. Next, I have tried to draw a centered bitmap image, but encountered a strange behavior when resizing the frame window: the image is not entirely drawn or appears multiplied. I have tried both in 'WM_ERASEBKGND' and 'WM_PAINT' message handlers but without success. What I'm missing?

    A: That happens because 'MDIClient' window class has not 'CS_VREDRAW' and 'CS_HREDRAW' styles (if 'CS_VREDRAW' and 'CS_HREDRAW' are set, the entire window is redrawn if a movement or size adjustment changes the width of the client area).

    You can fix the problem by invalidate the client area in the 'WM_SIZE' message handler:

    Code:
    void CMDIClientWnd::OnSize(UINT nType, int cx, int cy) 
    {
       CWnd::OnSize(nType, cx, cy);
       Invalidate(FALSE);
    }
    
    void CMDIClientWnd::OnPaint() 
    {
       CPaintDC dc(this); // device context for painting
    
       CDC dcMem;
       dcMem.CreateCompatibleDC(&dc);
       CBitmap bitmap;
       bitmap.LoadBitmap(IDB_LOGO);
       CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap);
    
       BITMAP bmp;
       bitmap.GetBitmap(&bmp);
       CRect rc;
       GetClientRect(rc);
       const int nWidth  = bmp.bmWidth;
       const int nHeight = bmp.bmHeight;
       const int x       = (rc.Width() / 2) - (nWidth / 2);
       const int y       = (rc.Height() / 2) - (nHeight / 2);
    
       dc.BitBlt(x, y, nWidth, nHeight, &dcMem, 0, 0, SRCCOPY);  
    
       dcMem.SelectObject(pOldBitmap);
    }
    Q: The centered bitmap drawn as in previous example flickers when resizing the frame window. Can be this avoided?

    A: Yes.


    • In 'WM_ERASEBKGND' do nothing, just return 'FALSE'

      Code:
      BOOL CMDIClientWnd::OnEraseBkgnd(CDC* pDC) 
      {
         // All drawing moved in OnPaint
         return FALSE;
      }
    • Perform all drawing (including filling with the brush) in WM_PAINT mesage handler using 'double buffering', i.e. draw in an additional memory device context, then copy the memory device context contents in the client device context.

      Code:
      void CMDIClientWnd::OnPaint() 
      {
         CPaintDC dc(this);
         CRect rcClient;
         GetClientRect(rcClient);
      
         CDC dcMem1; // memory DC for double buffering
         dcMem1.CreateCompatibleDC(&dc);
         CBitmap bitmap1;
         bitmap1.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
         CBitmap* pOldBitmap1 = dcMem1.SelectObject(&bitmap1);
         // fill memory device context with the desired brush
         dcMem1.FillRect(rcClient, &m_brush);
      
         CDC dcMem2; // memory DC for the bitmap to draw
         dcMem2.CreateCompatibleDC(&dc);
         CBitmap bitmap2;
         bitmap2.LoadBitmap(IDB_LOGO);
         CBitmap* pOldBitmap2 = dcMem2.SelectObject(&bitmap2);
      
         BITMAP bmp;
         bitmap2.GetBitmap(&bmp);
         int nWidth  = bmp.bmWidth;
         int nHeight = bmp.bmHeight;
         int x       = (rcClient.Width() / 2) - (nWidth / 2);
         int y       = (rcClient.Height() / 2) - (nHeight / 2);
         // copy the bitmap in memory device context 
         dcMem1.BitBlt(x, y, nWidth, nHeight, &dcMem2, 0, 0, SRCCOPY);
      
         CRect rcClip;
         dc.GetClipBox(rcClip);
      
         nWidth  = rcClip.Width();
         nHeight = rcClip.Height();
         x       = rcClip.left;
         y       = rcClip.top;
         // copy memory device context in client device context
         dc.BitBlt(x, y, nWidth, nHeight, &dcMem1, x, y, SRCCOPY); 
      
         dcMem1.SelectObject(pOldBitmap1);
         dcMem2.SelectObject(pOldBitmap2);
      }


    See also



    Last edited by ovidiucucu; January 29th, 2013 at 11:51 AM.

  3. #3
    ovidiucucu's Avatar
    ovidiucucu is offline Moderator/Reviewer Power Poster
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,058

    MFC MDI: How to change the background of the MDI main frame? (3)

    If the main frame class is derived from CMDIFrameWndEx (Visual Studio 2008 and newer), the solution is much simpler: just override CMDIFrameWndEx::OnEraseMDIClientBackground.

    Example
    Code:
    BOOL CMainFrame::OnEraseMDIClientBackground(CDC* pDC)
    {
       CRect rc;
       GetClientRect(rc);
    
       // fill the MDI client background 
       CBrush* pbrushOld = pDC->SelectObject(&m_brush);
       pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
    
       // may perform more painting here...
    
       // free the GDI objects
       pDC->SelectObject(pbrushOld);
    
       return TRUE; // returns TRUE for no further default processing
    }
    See also
    Ovidiu Cucu
    "When in Rome, do as Romans do."
    Visit: Microsoft Virtual Academy
    Follow: https://twitter.com/#!/ovidiucucu
    My blog: http://codexpert.ro/blog/author/ovidiu-cucu/

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center