I'm programming an MFC application. I created a CStatic derived control on my Form dialog, so that I see the form directly when starting the program. I'm now drawing in this control with the OnPaint() Method in the derived class. So far, everything works.
However, strangely the drawings aren't clipped outside of the control. Instead,the whole form is painted if I draw really big things. In my understanding, only the control should be painted.
Thanks in advance
Andreas D.
#dlb Got it now.
CDC* pDC = GetDC();
CRect rClient();
GetClientRect(rClient);
CRgn ClipRgn;
if (ClipRgn.CreateRectRgnIndirect(&rClient))
{
pDC->SelectClipRgn(&ClipRgn);
}
// Drawing content
pDC->SelectClipRgn(NULL);
ReleaseDC(pDC);
Thanks for your answer
Check if your control is using the 'Unclipped Device Context' option. You can check this by getting the control flags.
You can also call IsOptimizedDraw() and if that return true then your drawing can draw outside the control.
Related
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 inherited an MFC app, and it has a window that has several owner-draw widgets that respond to OnPaint and do various drawing.
I noticed that in order to force the controls to redraw in response to various user actions, there was the following code:
CRect rect;
m_myControl.GetWindowRect(&rect);
ScreenToClient(&rect);
InvalidateRect(&rect, FALSE);
I thought this could be simplified like so:
m_myControl.Invalidate(FALSE);
But, in practice, when I do it this way, the control paints sometimes but not others. Specifically, when I'm interacting with controls in the window, sometimes myControl ends up just painting as solid gray. I changed the code back to the more-complicated InvalidateRect style and it's working great again.
Why would there be a difference here?
When you invalidate a window, you don't invalidate the window underneath it. If the parent window is responsible for drawing the control it won't get triggered because you didn't tell it that it needed updating. The original code does the right thing in that case.
I am currently writing a program in c++ (no MFC) and want to update a label (win32 static control) using the win32 DrawText function. However when i call the function nothing is written to the label. I use the following code:
HDC devCon = ::GetDC(GetDlgItem(IDC_TITLE).m_hWnd);
RECT rect = {10, 10, 100, 15};
::DrawText(devCon, _T("TEST DC TEXT!!!"), -1, &rect, DT_NOCLIP);
::ReleaseDC(GetDlgItem(IDC_TITLE).m_hWnd, devCon);
As you see with the GetDlgItem(...) I am using ATL but that should not be a problem in my opinion. When I specify NULL in the GetDC method the text is drawn in the upper left corner of the screen as it is supossed to be since the method return the DC to the entire screen.
Why doesn't this work with the DC of the label?
Hope you folks can help me.
If you want to draw the text manually because setting the control text doesn't do what you want, then you need to tell Windows that you're doing that. Otherwise the control will draw itself over whatever you do whenever it needs to be redrawn.
To draw it yourself, mark your control as owner draw by setting the SS_OWNERDRAW style, and then handle the WM_DRAWITEM message to draw it in the window procedure of the parent window, or subclass the window and handle the WM_PAINT message in your new window procedure.
I guess that the text is drawn but at the next window message is set to the default text.
Try to set the text with SendMessage(..,WM_SETTEXT,...);
Use SetDlgItemText() to set the text for the control.
You are trying to paint directly onto the static control's device context.
This is not going to be so simple, because:
the control will repaint itself whenever it's update region is invalidated
usually the controls share the device context with the parent window, so what you are getting in GetDC(...) is actually your dialog's device context.
So, use SetDlgItemText, or SetWindowText to set the text of the window.
To use a custom font (or set the text/background color), handle the WM_CTLCOLORSTATIC message in your WindowProc.
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.
I am creating a MFC application in which there is a skin library which handles the UI effect of rendering the controls (it gets called in oninitdialog). But, meanwhile, I have also the requirement of displaying an icon on the buttons. For this, I am marking the buttons as ownerdrawn=true, and able to display icon, but in this case, skin effect is not taking place on those buttons whose ownerdrawing is done by me. So, my question is, how do I ensure that a control gets ownedrawn by me, and also by any other library.
Call the default handler for OnPaint to make sure the skinning library has a chance to draw the button, then draw your own content over the top.
void OnPaint()
{
Default();
CClientDC dc(this);
// your painting code goes here
}
You don't need owner-draw to display icons in buttons !