AnimateWindow() incorrectly draws background on high dpi (Win10) - winapi

I am creating a simple Win32/MFC application with a main window, and a child-window that uses AnimateWindow() to show (slide up) and hide (slide down) the window. When running the application on 100% dpi scaling, everything behaves normally.
I have overridden WM_ERASEBKGND to render a random red color, to demonstrate the effect. As the window slides-down (hide) on every "step" of the animation the "update rectangle" of the background is repainted, exactly where the child-window disappeared, and the background is to become visible again.
However, when changing the dpi-scaling via Windows Settings (in this case to 125%), an incorrect area is redrawn. It appears as if the area passed into OnEraseBkgnd() is still using the 100% dpi-scaling size, but also the position is off: it appears as if the x/y position of the upper-left corner is passed in screen-space instead of client-space. So the redrawn area looks different, depending on where the on the screen the window is positioned.
The white area is where the child window was actually positioned, and where the redraw of the background should actually have happened.
I have confirmed this effect on Win10 (1803 and 1809) and on Win8.1
Is this a bug in the OS, or is there something I can do to avoid the problem - other than not using AnimateWindow()? ShowWindow() (with SW_SHOW or SW_HIDE) works just fine, btw.
Update: added the full source-code to reproduce the issue.
The problem occurs when using no dpi-aware manifest at all, but it also occurs when using <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
class CDialogTestApp : public CWinApp
{
virtual BOOL InitInstance();
};
CDialogTestApp theApp;
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {}
};
class CDialogTestDlg : public CDialogEx
{
public:
CDialogTestDlg(CWnd* pParent = nullptr) : CDialogEx(IDD_DIALOGTEST_DIALOG, pParent) {}
protected:
virtual BOOL OnInitDialog();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
private:
CAboutDlg mDialog;
};
BEGIN_MESSAGE_MAP(CDialogTestDlg, CDialogEx)
ON_WM_ERASEBKGND()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
BOOL CDialogTestApp::InitInstance()
{
CWinApp::InitInstance();
CDialogTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
return FALSE;
}
BOOL CDialogTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
mDialog.Create(IDD_ABOUTBOX, this);
return TRUE;
}
BOOL CDialogTestDlg::OnEraseBkgnd(CDC* pDC)
{
COLORREF color = RGB(rand() & 255, 20, 40);
CRect rect;
GetClipBox(pDC->m_hDC, &rect); // retrieve the update-rectangle
CBrush brush(color);
FillRect(pDC->m_hDC, &rect, (HBRUSH)brush.m_hObject);
return TRUE;
}
void CDialogTestDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if (mDialog.IsWindowVisible())
{
mDialog.AnimateWindow(200, AW_HIDE | AW_SLIDE | AW_VER_POSITIVE);
}
else
{
mDialog.SetWindowPos(&CWnd::wndTop, 0, 50, 0, 0, SWP_NOSIZE);
mDialog.AnimateWindow(200, AW_ACTIVATE | AW_SLIDE | AW_VER_NEGATIVE);
}
CDialogEx::OnLButtonUp(nFlags, point);
}

Related

How to paint title bar window only?

As mention from microsft doc. WM_NCPAINT is use to paint non-client area. it means like title bar. https://learn.microsoft.com/en-us/windows/win32/gdi/wm-ncpaint. But i get unexpected result. it paint client area too. and the weird one. title bar is gone. when launch. after ALT+TAB. the title bar appears with windows 7 style in windows 10.
class CMainFrame::CFrameWnd
{
public:
CMainFrame()
{
Create(
NULL,
"Hello World!",
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
CRect(CPoint(100, 100), CSize(640, 360));
}
protected:
afx_msg void OnNcPaint();
DECLARE_MESSAGE_MAP()
}
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_NCPAINT()
END_MESSAGE_MAP()
void CMainFrame::OnNcPaint()
{
PAINTSTRUCT ps;
CBrush brush;
brush.CreateSolidBrush(RGB(0, 0, 255));
CDC *pDC = BeginPaint(&ps);
pDC->FillRect(&ps.rcPaint, &brush);
EndPaint(&ps);
}
class CApplication : public CWinApp {
BOOL InitInstance() {
CMainFrame* mainWnd = new CMainFrame();
m_pMainWnd = mainWnd;
mainWnd->ShowWindow(SW_NORMAL);
mainWnd->UpdateWindow();
return TRUE;
}
};
In InitInstance, you must call the base class CWinApp::InitInstance() in the first line.
Create your main frame window using
CreateEx(0, AfxRegisterWndClass(0), "Hello World!",
WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 640, 360, NULL, 0);
Add PreCreateWindow to control the edges and other properties
int CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
auto res = CFrameWnd::PreCreateWindow(cs);
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
return res;
}
Use the appropriate paint classes, CPaintDC dc(this), CWindowDC, for overriding OnPaint or OnNcPaint (avoid overriding OnNcPaint), use CClientDC dc(this) for other client area paints. Avoid calling WinAPI functions directly, for example avoid calling BeginPaint. You can also use Visual Studio to create a sample dialog based application, it will be easier to get started with that.

MFC: how to fix redraw properly for inherited CDialogBar?

I make an inherited class from CDialogBar.
class CMyDialogBar : public CDialogBar
{
DECLARE_DYNAMIC(CMyDialogBar)
// Implementation
public:
BOOL Create(CWnd * pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID);
BOOL Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID);
protected:
virtual void DoDataExchange(CDataExchange* pDX) { return CDialogBar::DoDataExchange(pDX); }
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
DECLARE_MESSAGE_MAP()
};
The only big change is the function OnEraseBkgnd() because I like the background to be white.
BOOL CMyDialogBar::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
It works OK. However, when I move the rebar around it doesn't redraw correctly as shown in the figure below.
The source code can be downloaded here: https://138.197.210.223/test/My.zip.
You need your OnEraseBkgnd override to actually erase the background! For example, to set the entire client rectangle to white, you could do this:
BOOL CMyDialogBar::OnEraseBkgnd(CDC *pDC)
{
RECT wr; GetClientRect(&wr);
pDC->FillSolidRect(&wr, RGB(255,255,255));
return TRUE;
}
EDIT: Maybe you already have this, but also be sure to add ON_WM_ERASEBKGND to your message map:
BEGIN_MESSAGE_MAP(CMyDialogBar, CDialogBar)
// ... (other message handlers, if any) ...
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

CEdit works in ComCtrl32 Version 5.82 but not with 6.10

I want port a MFC project to the current available resources.
I develop with Microsoft Visual Studio Community.
In the older project the Windows SDK Version is 10.0.15063.0
in the new project the Windows SDK Version is 10.0.17763.0
the older project uses ComCtrl32.dll Version 5.82
the new project uses ComCtrl32.dll Version 6.10
After the update with SetWindowTextW(textp) the used CEdit control shows a black control rectangle
If I move the cursor over the control it looks as expected.
ValEdit.h :
class ValEdit : public CEdit
{
public:
ValEdit();
virtual ~ValEdit();
int ZeroMeansInactiv;
protected:
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnUpdate();
DECLARE_MESSAGE_MAP()
private:
COLORREF m_TextColor;
HBRUSH m_hBackgroundBrush;
HBRUSH m_hBackgrInactivBrush;
};
ValEdit.cpp :
ValEdit::ValEdit()
{
ZeroMeansInactiv = 1;
m_TextColor = Black;
m_hBackgroundBrush = CreateSolidBrush(RGB(255, 255, 255));
m_hBackgrInactivBrush = CreateSolidBrush(RGB(90, 90, 90));
}
ValEdit::~ValEdit()
{
}
BEGIN_MESSAGE_MAP(ValEdit, CEdit)
ON_WM_ERASEBKGND()
ON_CONTROL_REFLECT(EN_UPDATE, OnUpdate)
END_MESSAGE_MAP()
BOOL ValEdit::OnEraseBkgnd(CDC* pDC)
{
RECT rc;
this->GetClientRect(&rc);
SetMapMode(*pDC, MM_TEXT);
FillRect(*pDC, &rc, !!ZeroMeansInactiv ? m_hBackgroundBrush : m_hBackgrInactivBrush );
return TRUE;
}
void ValEdit::OnUpdate()
{
RedrawWindow();
}
Thank you for advice
Erhy
please, I need explanations!
I programmed the app step by step and found the code, which is responsible for the malfunction, that the CEdit control is not updated correctly.
HBRUSH CStyleToolkitDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_STATIC)
{
// We handle this message only if we have set the region
BOOL bHandled = m_bIsRgnSet;
if (bHandled)
{
HDC hDC = pDC->GetSafeHdc();
SetBkMode(hDC, TRANSPARENT);
return (HBRUSH)GetStockObject(HOLLOW_BRUSH); //causes the malfunction
}
}
return hbr;
}
If I exclude CEdit with
if (!pWnd->IsKindOf(RUNTIME_CLASS(CEdit)))
return (HBRUSH)GetStockObject(HOLLOW_BRUSH);
the CEdit Control is updated as expected.
Thank you for discussion

What would make a static Picture Control display some pixels as transparent (bg color)

I'm updating the question to remove irrelevant details. The conclusion I've drawn is that if a valid alpha channel exists, it will honor it, but if it doesn't (say a 24-bit PNG w/o alpha channel), it uses F0F0F0 as a transparent color.
I have an image being loaded into a static "picture control" (chosen in visual studio) in a dialog. I noticed that color 0xF0F0F0 is being displayed as a "transparent" color (background of the dialog bleeds through). The bitmap is loaded via CStatic::SetBitmap.
The Picture Control transparent flag is set to false.
The image is loaded via CImage::Load.
If I wanted to mask a color out of a CStatic bitmap set via SetBitmap, how would I do it? I don't, but maybe that would help me find the cause.
Minimum example below. I created a dialog project with the VS wizard, and added a picture control to the main dialog. Then I added only the following code:
//header code added
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
....
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
//cpp code added
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
....
ON_WM_CTLCOLOR()
....
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\\temp\\logo.png"));
pictureCtrl.SetBitmap(logoImage);
....
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
And here is the image file I'm testing with.
And here is what it looks like on the dialog:
// MFCApplication1Dlg.h : header file
//
#pragma once
// CMFCApplication1Dlg dialog
class CMFCApplication1Dlg : public CDialogEx
{
// Construction
public:
CMFCApplication1Dlg(CWnd* pParent = nullptr); // standard constructor
CPngImage logoImage;
CStatic pictureCtrl;
CBrush bgBrush;
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MFCAPPLICATION1_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
// MFCApplication1Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MFCApplication1Dlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCApplication1Dlg dialog
CMFCApplication1Dlg::CMFCApplication1Dlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication1Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATICIMG, pictureCtrl);
}
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
// CMFCApplication1Dlg message handlers
BOOL CMFCApplication1Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 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
bgBrush.CreateSolidBrush(RGB(0, 255, 0));
logoImage.LoadFromFile(_T("C:\\temp\\logo.png"));
pictureCtrl.SetBitmap(logoImage);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 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 CMFCApplication1Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<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
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMFCApplication1Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
return bgBrush;
}
On my system (Windows 10), the color 0xF0F0F0 equals GetSysColor(COLOR_BTNFACE) which is the default dialog background color. When drawing, the static control seems to replace this color in the background image with the brush returned from OnCtlColor() handler of the parent window. This does have the taste of a feature and not a bug (though I couldn't find anything in the reference that specifies this behaviour).
Here is a code snippet to reproduce this issue even without using CPngImage or CImage, just by drawing in a memory DC with color 0xF0F0F0.
As the behaviour only appears when the source image does not contain an alpha channel, a solution would be to convert the source image to 32-bpp ARGB format. This way we don't have to override CStatic::OnPaint():
// Set the alpha channel of a 32-bpp ARGB image to the given value.
HRESULT SetAlphaChannel( CImage& image, std::uint8_t alpha )
{
if( ! image.GetBits() || image.GetBPP() != 32 )
return E_INVALIDARG;
GdiFlush(); // Make sure GDI has finished all drawing in source image.
for( int y = 0; y < image.GetHeight(); ++y )
{
DWORD* pPix = reinterpret_cast<DWORD*>( image.GetPixelAddress( 0, y ) );
for( int x = 0; x < image.GetWidth(); ++x, ++pPix )
{
*pPix = ( *pPix & 0xFFFFFF ) | ( alpha << 24 );
}
}
return S_OK;
}
// Load an image and convert to 32-bpp ARGB format, if necessary.
HRESULT LoadImageAndConvertToARGB32( CImage& image, LPCWSTR pFilePath )
{
CImage tempImage;
HRESULT hr = tempImage.Load( pFilePath );
if( FAILED( hr ) )
return hr;
if( tempImage.GetBPP() == 32 ) // Assume 32 bpp image already has an alpha channel
{
image.Attach( tempImage.Detach() );
return S_OK;
}
if( ! image.Create( tempImage.GetWidth(), tempImage.GetHeight(), 32, CImage::createAlphaChannel ) )
return E_FAIL;
HDC const imageDC = image.GetDC();
BOOL const bitBltSuccess = tempImage.BitBlt( imageDC, 0, 0, SRCCOPY );
image.ReleaseDC();
if( ! bitBltSuccess )
return E_FAIL;
SetAlphaChannel( image, 255 ); // set alpha to opaque
return S_OK;
}
Usage:
Replace call to CImage::Load() by:
LoadImageAndConvertToARGB32( m_image, filePath );
Notes:
There is another static control nastiness when you assign a 32-bpp bitmap with a non-zero alpha channel to the control¹ (as you do when following my solution). In this case, the static control will make a copy of the bitmap you passed in while you are responsible to destroy this copy!
Mandatory OldNewThing read:
"When will the static control automatically delete the image loaded into it, and when is it the responsibility of the application?"
¹) More precisely: When using version 6 of the common controls, which almost all applications do these days.
It's not a PNG problem, it's a color depth problem.
According to your code, I converted 8-bit PNG picture into 8-bit BMP picture by format conversion tool, and the picture still show the background color.
So I saved 8-bit PNG picture to 32-bit png picture, and that's all right.
Why is that?
Answer: A GIF file has two parts: a color table and the image pixel data. The color table is a list of the colors used in that image (an 8-bit GIF can have up to 2^8 = 256 colors in the color table, but a 4-bit GIF can have only 2^4 = 16 colors), and each color is assigned a number. The image pixel data are for the image itself, and each pixel is assigned a number that points to its color in the color table. For example, if color #10 in the color table is red (#FF0000), then any pixel in the image with the number 10 will be displayed as red. The colors in the color table will vary from GIF file to GIF file based on the image itself; color #10 will not always be red. The color table is the set of up to 256 colors necessary to render that image.
When we add index transparency, every color in the color table is given a transparency designation in addition to its color data (i.e., RGB values):
zero (o = False in Boolean algebra) means do not display this color, or
one (1 = True in Boolean Algebra) means display this color.
There are no intermediate opacities; the color is either displayed or it is not. The end result is that a pixel with an index transparency color will not be displayed and whatever is in the background behind that pixel will show through. For example, if color #10 is red (#FF0000) and is designated as transparent (index transparency = 0), then any pixel that is color #10 will not be displayed and the background will show through.
There can be multiple transparent colors in index transparency, because every color in the color table has a designation of either opaque (1) or transparent (0). Most graphics programs assume that the canvas color (often white, but it could be any color) is the default transparent color, but you can specify any color (or any number of colors) as transparent or not.
This type of transparency is very common in GIF and PNG8 files and is easy to identify, because there is no fading, there are no partially transparent pixels, and the edges are often described as “hard” or “pixelated.”
I'm afraid the following is the best answer I'm going to get. MFC/Win32 does something funny and it's not clear why. But the image has no problems displaying correctly if you draw it manually.
I created a custom (very rough) CStatic class and added the OnPaint below. In the screenshot, the first is using my class, and the second is using regular CStatic. Both are using an identical image (24 bit BMP).
struct AFX_CTLCOLOR {
HWND hWnd;
HDC hDC;
UINT nCtlType;
};
void CMyStatic::OnPaint() {
CPaintDC dc(this); // device context for painting
CRect r;
GetClientRect(r);
WPARAM w = (WPARAM)&dc;
AFX_CTLCOLOR ctl;
ctl.hWnd = this->GetSafeHwnd();
ctl.nCtlType = CTLCOLOR_STATIC;
ctl.hDC = dc.GetSafeHdc();
HBRUSH bg=(HBRUSH)::SendMessage(GetParent()->GetSafeHwnd(), WM_CTLCOLORSTATIC, w, (LPARAM)&ctl);
CBrush cbg;
cbg.Attach(bg);
dc.FillRect(r, &cbg);
cbg.Detach();
HBITMAP hbmp=GetBitmap();
BITMAP binfo;
CBitmap* cbmp = CBitmap::FromHandle(hbmp);
cbmp->GetBitmap(&binfo);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
dcMem.SelectObject(cbmp);
dc.BitBlt((r.Width()-binfo.bmWidth)/2, (r.Height() - binfo.bmHeight) / 2, binfo.bmWidth, binfo.bmHeight, &dcMem, 0, 0, SRCCOPY);
}

What parameters are valid to be together to create an HWND?

I'm trying to create a window using CWnd::CreateEx(). Unfortunately, I can't seem to get the parameters correct to get it to generate a HWND (function always returns FALSE).
class COverlay : public CWnd
{
public:
COverlay();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(COverlay, CWnd)
END_MESSAGE_MAP()
COverlay::COverlay()
{
}
class CMyView : CView
{
public:
CMyView() {}
int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_overlay.CreateEx(WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT
, _T("Static"), _T(""), WS_VISIBLE | WS_CHILD, CRect(), this, -1);
return 0;
}
I'm trying to create a click through window that overlays the CMyView. In this way I can paint over the view via the overlay and not get flickering as the OS should handle the double buffering and I will only have to deal with the view implementation in a minimal fashion. I will also have an ON_WM_SIZE hander to deal with resizing the overlay to the extents of the view.
What am I doing wrong?
Is there an easier way to do what I am trying to do?
WS_POPUP flag is required to create window with transparency level:
static CString className = AfxRegisterWndClass(0);
DWORD style = WS_CAPTION | WS_SYSMENU | WS_POPUP;
DWORD styleEx = WS_EX_LAYERED;// | WS_EX_TRANSPARENT;
if (!m_overlay.CreateEx(styleEx, className, 0, style, CRect(), this, 0))
{
//error...
return;
}
m_overlay.SetLayeredWindowAttributes(0, 255 * 50 / 100, LWA_ALPHA);
m_overlay.SetWindowPos(0, 0, 0, 400, 300, SWP_SHOWWINDOW | SWP_NOACTIVATE);
Or to change the transparency color:
COLORREF transparentColor = RGB(255, 0, 0);
...
m_overlay.SetLayeredWindowAttributes(transparentColor, 0, LWA_COLORKEY);
void COverlay::OnPaint()
{
CPaintDC dc(this);
CRect rc;
GetClientRect(&rc);
dc.FillSolidRect(rc, transparentColor);
//opaque drawings...
dc.TextOut(0, 0, L"Test...");
}
Note, WS_EX_TRANSPARENT is commented out because that only affects mouse clicks, otherwise it doesn't change the appearance. You can add that flag back in.
This window is popup so it doesn't move when you move the frame window or child window. You would have to override frame and child frame window's OnWindowPosChanged to move it manually to sync with View window.

Resources