I have outputted an image (bitmap) which is created by Bitblt.
Now I want to get rid of it. How can I do? (Do not use the patch, like FillSolidRect, etc.)
There is no way to "undo" or "erase" a BitBlt or any other drawing output (except in very special cases where you do XOR-based drawing, which you can undo by doing another XOR drawing operation on top of the original).
The only thing you can do is to draw on something else on top of it, which is what you are calling a "patch". Typically, you would draw a solid rectangle of the window's background color. This is precisely what the OnEraseBkgrnd message handler does by default, which runs just before OnPaint. Specifically, it uses your window class's background brush, which is typically a brush that draws using the COLOR_3DFACE (for a dialog) or COLOR_WINDOW (for a window) system color.
Of course, you could always just not do the BitBlt in the first place. All painting code should always go inside of the OnPaint message handler function, so there is no way that you could end up with "stale" graphics. Whenever the window needs repainting, it is going to call this function, and your code inside of that function will repaint the window. If you don't want it to be painted with a bitmap, don't call BitBlt.
If you've done a BitBlt on top of your window using a temporary CDC object (which you generally should not be doing), you can force this to be erased by triggering a repaint of the window. The easiest way is to use the window's InvalidateRect() member function; passing NULL as the pointer to the rectangle to be invalidated will invalidate the window's entire client area, or you can just invalidate the area that you blitted.
Related
I am currently developing a kind of "WinMerge" clone, and currently I am trying to implement a custom scrollbar which should later represent both compared files as a rectangle in the background each.
This is what it looks like at the startup:
However, after scrolling around a little bit, this is what I end up with:
As you can clearly see, only those parts look correct which I explicitely paint over in my paint routine:
void LocationPane::OnPaint(CDCHandle dc)
{
DefWindowProc();
dc = GetDC();
DrawLocationPaneFigures(dc);
}
This is how my control is configured in my .rc file:
CONTROL "",IDC_LOCATIONPANE,"Static",SS_OWNERDRAW | SS_NOTIFY | WS_BORDER | WS_GROUP,7,21,91,541
As you can see, it is an owner drawn control.
How can I erase the background for this control while repainting it?
A static control with SS_OWNERDRAW style receives a WM_DRAWITEM message when it needs to be redrawn.
So first you need to replace your OnPaint() handler by a handler for WM_DRAWITEM. Instead of calling GetDC() use the device context supplied to you in the DRAWITEMSTRUCT.
To erase the background it's generally best to do it as part of the regular painting code to reduce flickering (by calling FillRect() for instance).
I suggest to always draw the whole client area of your control. Then you may handle WM_ERASEBKGND to return TRUE without calling DefWindowProc() to reduce flickering even more.
I am trying to implement something like this in mfc:
A parent window that contains a few child windows. I want some horizontal and vertical grid lines to appear around a child window when ever the window is being dragged anywhere inside the parent. For that, I am tracking movement in my OnPaint() handler for the child and accessing the parent and drawing these grid lines around the child. But its not behaving the way I want it to.
My guess is that the child's OnPaint() only updates the child's drawing region, so even if I do access the parent's DC, I cant draw anything on it unless it's being redrawn?
Can anybody suggest a neater method of achieving above functionality? What am I doing wrong? I need everything to happen in the child's OnPaint()
One way is to just pass data to the parent window and call its Invalidate function. Then it would paint the grid lines in its own OnPaint.
Or you could paint directly on the parent window from the child, but use the parent's GetDC function instead of using the child's DC.
One way to solve this problem is to draw a semi-transparent grid window using UpdateLayeredWindow on top of the parent client area while you arrange the child window on top of it. The grid window can then be destroy after the arrangement is done. In this way, there will be minimum changes required to the existing display code.
This is for C++ - win32. Basically I've loaded an image (bmp) into a HBITMAP from a file and bitblitted it to the device context for the main window.
How would I call it again in case I want to change the image?
I've called InvalidateRectangle() and UpdateWindow() but that causes the window controls to flicker.
Normally you invalidate the area (e.g. via InvalidateRect) and let your WM_PAINT handler repaint it. Reasons why you would get flicker often are because you haven't overridden the WM_ERASEBKGND handler, your WM_PAINT handler isn't doing double-buffered painting, or you're invalidating (or repainting) an area larger than you need to.
This page might help: Flicker-free Drawing: Techniques to eliminate flicker from your applications
I'm trying to set background image in a MFC Dialog, I succeeded in getting the Client device context, while writing CDC* pDC=GetDC(), or CClientDC dc(this) , inside functions like OnTimer, OnPaint, OnEraseBkgrd, but not in a new declared function(also in CDialog class).
I tested this by a simple drawing function, like drawing a rectangle in both case, the result is that, if I write it in OnTimer, OnPaint, OnEraseBkgrd, it works well, but in my declared new function it doesn't !
Anyone can teach me how to get client dc in non-message functions?
Why OnTimer handler for drawing?
You should use WM_PAINT (or in some cases WM_ERASEBKGND) message to do the drawing.
What is the new declared function?
All names you quoted are message handlers. Those functions are mapped to the message map and they are called by the framework when a message is received.
To set the background you should use WM_ERASEBKGND. The handler supplies device context you should use for drawing.
Your drawing to screen should only be done in OnPaint (or OnEraseBkgrd). If you need to prompt drawing from elsewhere in your dialog then you need to call InvalidateRect() which will subsequently call OnPaint(). When you OnPaint() is called, you can call pDC->GetClipBox() to get the rect which needs updating.
I think the best way to do what you want to do would be to store a pointer to the background image in your dialog class, then when you want to change the background image, set the bitmap pointer and call InvalidateRect(). Your OnPaint() function will then do the BitBlt to actually draw the new bitmap.
ok i can draw ellipse the problem is this, im trying to draw one ellipse but change its x value to different one. like this i draw one ellipse and the x value is 1 after ten seconds i want it the x value to be 10 but it seems that im creating new ellipse with x value 10. here is my code
while(sd==1)//sd equal 1
{
sf++;//sf equals 1
onPaint(hdc);
InvalidateRect(hWnd,0,true);
}
//on paint function
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen,sf , 0, 50, 50);
}
well i thought that invalidate rect will clear everything have been painted and repaint it but it didn't work
If you want to make an animation you are better setting a timer.
Using InvalidateRect as a way to generate WM_PAINTs seems overkill, it will do much more thant that. Instead you can draw directly in OnTimer call, since it is outside a WM_PAINT you will need to get a device context with GetDC.
For example if you can have the function DrawFrame(HDC hDC). OnTimer will update the current position and call DrawFrame, OnPaint will call DrawFrame but will not update the position (that way if you want to stop the animation you will have the last frame draw).
The DrawFrame will clear the background (probably with a FillRect), and draw the circle in the new position. If you have a large area this will flicker, to avoid it as Tom suggested you may use a memory DC and a HBITMAP for the double buffer.
You shouldn't try to draw multiple frames of an animation in one shot.
Save your variable sf somewhere, and in OnPaint(), increment sf, draw a single ellipse, and call Invalidate()
The Invalidate will trigger OnPaint() to be called again.
This should work, but will be very flickery :) You can fix the flicker by double-buffering.
InvalidateRect marks the window as "invalid", but that doesn't cause an erase and repaint to happen right away. The erasing and painting happen only when your message pump is running (e.g., the loop with GetMessage and DispatchMessage). When the message queue runs dry, GetMessage will synthesize WM_ERASEBKGND and WM_PAINT messages for the invalid windows. When those messages are dispatched to the window procedure, the window gets a chance to draw.
Your onPaint function only draws, it doesn't erase. And since your loop never exits, the message pump never gets to run.
For simple animations, the solution is to SetTimer. In your handler for the WM_TIMER messages, update your variables for single frame, call InvalidateRect, and return (which lets the message pump keep running). The erasing and painting message will happen, then the timer will fire again, and you'll get the next frame.