I am trying to create a control that implements the per-pixel alpha blend while painting a 32-bit bitmap.
I extended a CWnd and use static control in the resource editor. I managed to paint the alpha channel correctly but still the static control keep painting the gray background.
I overwrote the OnEraseBkgnd to prevent the control from painting the background but it didn't worked. I finally managed to do it by using WS_EX_TRANSPARENT.
My problem now is that my control is placed over other control. The first time the dialog is painted all works fine...but if I click over the "parent" control (ie the one beneath my control) my control doesn't received the WM_PAINT message. So it is not painted anymore.
If I minimize the aplication and maximized it again the controls are painted again.
Please, can anybody give a hint? I am getting crazy with this control!!!
Thanks.
I would have though that if you are processing both the WM_ERASEBKGND and the WM_PAINT messages then you should have cover all the painting options, without having to resort to using the WS_EX_TRANSPARENT style.
Are you sure your code is not passing these messages on to the default processing?
One other option might be to subclass the static control, just to make sure your code is the only one handling these two messages.
BEGIN_MESSAGE_MAP(CTransparentStatic, CStatic)
ON_WM_ERASEBKGND()
ON_WM_CTLCOLOR_REFLECT()
END_MESSAGE_MAP()
BOOL CTransparentStatic::OnEraseBkgnd(CDC* /*pDC*/)
{
// Prevent from default background erasing.
return FALSE;
}
BOOL CTransparentStatic::PreCreateWindow(CREATESTRUCT& cs)
{
cs.dwExStyle |= WS_EX_TRANSPARENT;
return CStatic::PreCreateWindow(cs);
}
HBRUSH CTransparentStatic::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
pDC->SetBkMode(TRANSPARENT);
return reinterpret_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
}
void CTransparentStatic::PreSubclassWindow()
{
CStatic::PreSubclassWindow();
const LONG_PTR exStyle = GetWindowLongPtr(m_hWnd, GWL_EXSTYLE);
SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, exStyle | WS_EX_TRANSPARENT);
}
Related
We have a requirement to change the color of pushbutton/control. I have controls defined in resource file.
We have tried multiple ways
1)Using CMFCButton Object I got defination ambiguity errors
2)
Using CBUtton CDC* pdcWindow1 = m_Button.GetWindowDC(); CRect rect1; GetClientRect(&rect1); pdcWindow1->FillSolidRect(&rect1, (RGB(0, 0, 255)));
No effect on Button color no error as well.
Inputs which i have got so far : we have used ATLcontrols and to color Button we need MFC Functions, here ATL and MFC libs can't coexist they are causing ambiguity errors as both have same functional definitions.
Is it even possible to color ATL controls without MFC functions.?
only solution is --https://jeffpar.github.io/kbarchive/kb/173/Q173974/??
Look of standard Windows GDI buttons is customized according to Button Color Messages:
The system sends a WM_CTLCOLORBTN message to a button's parent window before drawing a button. This message contains a handle to the button's device context and a handle to the child window. The parent window can use these handles to change the button's text and background colors. However, only owner-drawn buttons respond to the parent window processing the message.
In ATL project, you would either handle this notification message in parent window class or use more sophisticated message forwarding (reflection) to reflect this message to button class.
Either way you don't really paint (FillSolidRect), you can just update colors in the message handler. And also pay attention that only owner-drawn buttons expose this functionality.
See also: Owner-drawn button, WM_CTLCOLORBTN and WM_DRAWITEM (clearing an HDC)
This is how I was able to achieve :
HINSTANCE hInstance = GetModuleHandle(NULL);
HANDLE hBitmap = LoadImage(hInstance, MAKEINTRESOURCE(IDB_CURCANCEL_LOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
HANDLE hBitmap1 = LoadImage(hInstance, MAKEINTRESOURCE(IDB_CURLOGIN_LOGO), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
const HWND hOriginalLoginButton = GetDlgItem(IDOK);
const HWND hOriginalCancelButton = GetDlgItem(IDCANCEL);
SendMessage(hOriginalLoginButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap1);
SendMessage(hOriginalCancelButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
Now i am trying to make corners rounded.
I am trying to implement a static control which refreshes(change text) in response to some event, which occurs once every second. Since I didn't want to paint the entire client area every second and so I decided to use a static control, now the problem is the parent window is skinned, meaning it has custom bitmap as its background, and the static control doesn't fit in, so am looking for ways to make the static control's background transparent.
This is what I have now:
hHandle = CreateWindowEx( WS_EX_TRANSPARENT, "STATIC", "", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 60, 212, 477, 20, hwnd, 0, hInstance, 0) ;
case WM_CTLCOLORSTATIC:
{
HDC hdC = (HDC)wParam;
SetTextColor( hdC, RGB(31,122,179) );
SetBkMode( hdC, TRANSPARENT );
return 0;//(HRESULT)GetStockObject(NULL_BRUSH);
}
break;
I tried returning NULL brush to paint the background, hoping it would make its background transparent but it didn't what more it forced the static control to not repaint properly, what I mean is that the text gets painted on top of the old text so its all messy.
Is subclassing is the only option ?
The only way I've found to do this reliably is to sub-class the static control and paint the background manually.
WS_EX_TRANSPARENT does not make a control truly transparent to underlying pixels (although it may appear like that) and WS_EX_COMPOSITED can not be used for child windows.
Instead, sub-class the static, and catch the WM_ERASEBKGND message. You can then paint the appropriate portion of the underlying bitmap.
The way to get a WC_STATIC control to show only text in the color you choose (over an image or other surface) is the return, as I understand this. This is what worked for me from this link.
case WM_CTLCOLORSTATIC:
SetTextColor((HDC)wParam, RGB(255, 0, 0));
SetBkMode((HDC)wParam, TRANSPARENT);
// the correct return needs HOLLOW_BRUSH
return (LRESULT)GetStockObject(HOLLOW_BRUSH);
break;
You don't mention the window styles on the static control, specifically the extended styles WS_EX_TRANSPARENT seems intended to solve the problem you describe with the repaint. Also as I understand it WS_EX_COMPOSITED might be useful in your context.
You also might consider whether the background window should have WS_CLIPCHILDREN set as that might affect the repaint.
As the title says.
Even with CPaintDC in the derived class the GDI drawing is not cut off.
Thanks in advance.
void CGraph::OnPaint ()
{
CPaintDC dc(this);
dc.SetViewportOrg (0, 400);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(1000, 800);
dc.SetViewportExt(1000, -800);
// MessageBox(L"OnPaint");
ProcessData ();
DrawCoordinateSystem (&dc);
DrawGrid (&dc);
DrawGraph (&dc);
}
So, your CGraph is derived from CStatic, and the drawing code you show draws outside of the CStatic control, onto the dialog it is on? That's impossible, a control can only draw on itself. Are you sure the control isn't bigger than you think it is, and what you think is off-control actually isn't? Use spy++ to select your cstatic, it'll show you the border of the window.
Maybe what you are seeing is improper invalidation. Try dragging another window over your control, see what that does.
Otherwise, the methods to restrict the drawing area are
You manually track where to draw. Tedious.
Use SetClipRgn() to set the area to which to restrict drawing.
Not quite the same, but symptoms sometimes look similar: check the WS_CLIPSIBLINGS and WS_CLIPCHILDREN flags of your control and the dialog it's on.
Here is the way I solved the problem
CDC* pDC = GetDC();
CRect rClient(0,0,1000,800);
//GetClientRect(rClient);
CRgn ClipRgn;
if (ClipRgn.CreateRectRgnIndirect(&rClient))
{
pDC->SelectClipRgn(&ClipRgn);
}
pDC->SelectObject (PenBlack);
pDC->MoveTo (-leftMargin*zoomWidth, setPointsCorrected);
pDC->LineTo (1000*zoomWidth, setPointsCorrected);
pDC->SelectClipRgn(NULL);
ReleaseDC(pDC);
I've made a program with a custom non-client area. Instead of handling WM_NCPAINT, i set the client area size to zero in WM_NCCALCSIZE and then simulate the non-client area by handling WM_NCHITTEST. All painting to the window is handled in WM_PAINT. WM_PAINT works fine when i start the program and whenever the program needs repainted. However, once i minimize the program with a call to ShowWindow(hwnd, SW_MINIMIZE), and then restore the program by clicking on it in the task bar, WM_PAINT no longer works. I've checked every function call in WM_PAINT and they all return success, but the window just shows up as black. Anybody have any ideas as to why?
Here's the code for my WM_PAINT handler:
http://pastebin.com/MXmWzb4M
Here's the output to the console window:
BeginPaint successful
hdcBuffer is valid
MainWindow->hbmBuffer is valid
hdcMem is valid
So, seeing as every function call in my WM_PAINT handler is returning success, why is my entire window showing up as black?
Turns out the problem was with handling the WM_NCCALCSIZE message to remove the non-client area. Here's the alternative that i used:
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
dwStyle &= ~WS_OVERLAPPEDWINDOW;
SetWindowLong(hwnd, GWL_STYLE, dwStyle);
I'm writing a control that may have some of its parts transparent or semitransparent. Basically this control shows a png image (with alpha channel). The control's parent window has some graphics on it. Therefore, to make the png control render correctly it needs to get image of what the parent window would draw beneath it. Parent dialog might have WS_CLIPCHILDREN flag set, that means that the the parent window won't draw anything under the the png control and in this case png control won't work properly.
This control has to work on Windows Mobile also, so it can't have WS_EX_TRANSPARENT
Funny you should ask. I just wrote code to do this yesterday. It's in the code base for Project Resistance. Look at how ResistorView (which has a PNG with transparency) interacts with MainForm (which has the background image).
I'm not doing c#, so I don't think that could be helpful. I tried to look into your code and I don't see anything that can solve my problem even though you accomplish the same task as me.
To give more details, my control also supports gif animation and I also used the same IImage load from stream as you do in your project. From my experience, that IImage load from stream is unbelievable junk code, it's extremely slow. I have no idea how that possibly could be so slow. Loading 32x32 gif with 31 frames takes like 1.5secods using that junk IImage stuff. I wrote my own loader (using some opensource gif library) and without any optimization entire decoding/loading of the gif frames takes less than 100ms. I'm using TouchPro2... I can't imagine how badly this code would perform on a low end device.
As a quick alternative here's a possible solution to my question:
in the WM_PAINT handler of the child control that draws images (gif or png) I do the following:
first, I call SetRedraw(false) on self and on parent window.
then I hide the child control, and send WM_PAINT to parent window with optional HDC (as wParam). The parent window renders everything to the offscreen bitmap (passed via hdc to WM_PAINT) and after WM_PAINT successfully returns I take relevant part of the offscreen bitmap.
Then I enable show the child window and call SetRedraw(true) on both child and the parent window. That trick works but has some disadvantages obviously (I need to createa huge offscreen bitmap to capture entire screen area even though I need 32x32 pixel from the middle of parent window).
the code is below:
bool pic_control::get_parent_bg(MyBitmap & bg)
{
CWindow parent = GetParent();
CClientDC dc(parent);
bool is_visible = IsWindowVisible() && parent.IsWindowVisible();
if(!is_visible){
return false;
}
parent.SetRedraw(false);
SetRedraw(false);
CRect rect;
parent.GetClientRect(rect);
MyBitmap bmp;
bmp.create(rect.Width(), rect.Height());
ShowWindow(SW_HIDE);
parent.SendMessage(WM_PAINT, (WPARAM)(HDC)bmp.dc());
ShowWindow(SW_SHOW);
GetWindowRect(rect);
parent.ScreenToClient(rect);
bg.create(rect.Width(), rect.Height());
bg.dc().BitBlt(0, 0, rect.Width(), rect.Height(), bmp.dc(), rect.left, rect.top, SRCCOPY);
IF_DEBUG SAL::saveHBITMAPToJpeg(bg.GetBitmap(), "frames/BG.jpg", 100);
SetRedraw(true);
parent.SetRedraw(true);
return true;
}
WS_CLIPCHILDREN is forced on in WinCe, you cannot toggle it. I dont know why, maybe it is done for performance reasons.
From my experience what i did in this situation.
1) If the parent window bacgkround is dynamic (for instance window that contains map, which can be moved), then it is painted to memory canvas first, then to the screen, and memory canvas is saved and used for painting transparent childs. Memory canvas would not contain holes in place of child windows, so it can be used for futher pixel merge. The disadvantage here is memory consumption to hold canvas in memory.
2) If parent window background is static (dialog box, menu, etc) then you can make non-window childs.
class CImageButton
{
public:
bool IsPointInside(POINT pt);
void OnPaint(HDC canvas);
void OnClick();
void SetRect(RECT& rc);
private:
RECT m_rc;
};
Your parent window will contain an array of such objects and redirect WM_PAINT and
mouse clicks to them.
The disavantage is additional code needed to be added to parent window, but you can make a base class for all your parent windows, which would handle issues with non-windowed controls.