-
April 6th, 2006, 06:45 AM
#1
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.
-
October 4th, 2022, 02:51 AM
#2
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
-
October 4th, 2022, 05:33 AM
#3
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.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|