CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3
  1. #1
    Join Date
    Feb 2000
    Location
    Indore, India
    Posts
    1,046

    MFC SDI: How do I create transparent SDI views?

    Q: How do I create transparent SDI views?

    A: One can make a the view of an MFC application transparent by handling 'CView::OnEraseBkgnd()' function and returning 'FALSE' instead of the default 'CView::OnEraseBkgnd()' call.


    Code:
    BOOL CTransparentView::OnEraseBkgnd(CDC* pDC) 
    {
      // TODO: Add your message handler code here and/or call default
      return FALSE;
    //  return CView::OnEraseBkgnd(pDC);
    }
    But this will not give a real transparent window, since the original view background will be dragged along while moving the application window. To overcome this, I have handled 'OnMove()' and 'OnSize()' functions of 'CMainFrame' class to update the view to reflect the real background, by repainting the view each time the application window is moved / sized.


    Code:
    void CMainFrame::OnMove(int x, int y) 
    {
      CFrameWnd::OnMove(x, y);
      
      // TODO: Add your message handler code here
      RepaintView();
    }
    
    void CMainFrame::OnSize(UINT nType, int cx, int cy) 
    {
      CFrameWnd::OnSize(nType, cx, cy);
      
      // TODO: Add your message handler code here
      RepaintView();
    }
    I have defined a function 'RepaintWindow()' as part of the 'CMainFrame' class and implemented to repaint the view with correct background information.


    Code:
    void CMainFrame::RepaintView()
    {
      // Gets current view
      CView* pView = GetActiveView();
      if (pView)    // Valid view
      {
        // Hides view to get background
        ShowWindow(SW_HIDE);
        // Gets desktop device context
        HDC hDC = ::GetDC(NULL);
        if (hDC)  // Valid device context handle
        {
          // Get clients rectangle in screen coordinates
          CRect Rect;
          pView->GetClientRect(Rect);
          pView->ClientToScreen(Rect);
    
          // Gets device context for current view
          CClientDC DC(pView);
    
          // Pastes background to view
          ::BitBlt(DC.m_hDC, 0, 0, Rect.Width(), Rect.Height(),
            hDC, Rect.left, Rect.top, SRCCOPY);
    
          // Releases desktop device context
          ::ReleaseDC(NULL, hDC);
        }
    
        // Shows view with right background info
        ShowWindow(SW_SHOW);
      }
    }
    At present, this is implemented for SDI application. With little modification (for updating all views), this methodology can be extended for MDI applications as well.



    Last edited by Andreas Masur; April 7th, 2006 at 01:39 AM.

  2. #2
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,223

    Transparent SDI View by Using Window Regions

    Override CFrameWnd::RecalcLayout and...
    Code:
    class CMainFrame final : public CFrameWnd
    {
        // ...
        // Overrides
        virtual void RecalcLayout(BOOL bNotify = TRUE);
        // ...
    };
    ...after calling the base class virtual method, set a window region that excludes the view's client area.
    Code:
    void CMainFrame::RecalcLayout(BOOL bNotify /*= TRUE*/)
    {
        // call the base class RecalcLayout
        CFrameWnd::RecalcLayout(bNotify);
    
        CView* pView = GetActiveView();
        if (pView->GetSafeHwnd())
        {
            // create a region which covers the whole main frame area
            CRect rcFrame;
            GetWindowRect(rcFrame);
            CRgn rgnFrame;
            rgnFrame.CreateRectRgn(0, 0, rcFrame.Width(), rcFrame.Height());
    
            // create a region which covers the view's client area
            CRect rcView;
            pView->GetClientRect(rcView);
            pView->ClientToScreen(rcView);
    
            int x = rcView.left - rcFrame.left;
            int y = rcView.top - rcFrame.top;
            CRgn rgnView;
            rgnView.CreateRectRgn(x, y, x + rcView.Width(), y + rcView.Height());
    
            // combine the two regions in a new one, which excludes
            // the view's client area
            CRgn rgnNew;
            rgnNew.CreateRectRgn(0, 0, 0, 0);
            rgnNew.CombineRgn(&rgnFrame, &rgnView, RGN_DIFF);
    
            // set the new window region for the mai frame window 
            SetWindowRgn(rgnNew, TRUE);
        }
    }
    Notes:
    • So far, I have tested it under Windows 10, but IMO there is no reason to not work under other Windows versions which support window regions, beginning with Windows 2000 Professional, as stated in MSDN.
    • This works much better than previous solution, but unfortunatelly alters the main frame visual effects (no shadow and a veird, obsolete border). So I higly recommend for the same purpose, using of layered windows.
    Last edited by VictorN; October 4th, 2022 at 04:06 AM. Reason: A typo was fixed
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpert.ro/blog/

  3. #3
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,223

    Transparent SDI View by using Layered Windows

    Here it is step-by-step!

    1. Define a "transparent" color, preferable in the application's class header.
    Code:
    // ...
    #define TRANSPARENT_COLOR       RGB(1, 2, 254)
    2. In the main frame class override PreCreateWindow and...
    Code:
    class CMainFrame : public CFrameWndEx
    {
        //...
         // Overrides
    public:
        virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
        // ...
    };
    ...add WS_EX_LAYERED extened style.
    Code:
    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
        // call base class virtual method
        if (!CFrameWndEx::PreCreateWindow(cs))
            return FALSE;
    
        // add layered window extended style
        cs.dwExStyle |= WS_EX_LAYERED;
    
        return TRUE;
    }
    3. In WM_CREATE message handler, call SetLayeredWindowAttributes in order to use the defined "transparent" color and 100% opacity.
    Code:
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        // call base class message handler
        if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        // set layered window attributes (use the defined "transparent" color and 100% opacity)
        SetLayeredWindowAttributes(TRANSPARENT_COLOR, 255, LWA_COLORKEY | LWA_ALPHA);
        // ...
    }
    4. In the view's class, handle WM_ERASEBKGND and "erase" the background with the "transparent" color.
    Code:
    BOOL CTransparentSDIView::OnEraseBkgnd(CDC* pDC)
    {
        CRect rcClient;
        GetClientRect(rcClient);
        pDC->FillSolidRect(rcClient, TRANSPARENT_COLOR);
        return TRUE; // indicates to the system that background is already "erased".
    }
    Thats'all, nice and easy like a walking in the park!

    Notes:
    • Unlike the solution that uses window regions, this one allows drawing in the client area, if necessary.
    • Also it has not any problems like altering the visual effects of the main window.
    • This example makes transparent the whole client area of the view, but if you want, you can make transparent only portions of it.
    • I have tested under Windows XP, 10 and 11.
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpert.ro/blog/

Posting Permissions

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





Click Here to Expand Forum to Full Width

Featured