Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 862
  • Last Modified:

Flicker in CWnd

I am drawing in a CWnd derived class using a CPaintDC device context. I am displaying real-time data that could change every 200 miliseconds. I have created rectangular areas in my client area for display and draw to them using the device context method DrawText. When the data does not change rapidly, the display flickers. Is there any way to draw to a CWnd derived object at this rate and not have it flicker?
0
JagerM
Asked:
JagerM
  • 4
  • 4
1 Solution
 
anichiniCommented:
You need to render into an offscreen bitmap and blit that into your CPaintDC. The step-by-step:

1) Add these members to your CWnd-derived class:

protected:
CDC *m_pMemDC; // memory dc
CBitmap *m_pBitmap, *m_pOldBitmap; // bitmap

2) Have your constructors include these lines:
m_pMemDC = NULL;
m_pBitmap = NULL;

3) Override your the WM_SIZE message and have it include these lines:

void CYourWnd::OnSize(UINT nType, int cx, int cy)
{
    if(m_pBitmap != NULL)
    {
        m_pMemDC->SelectObject(m_pOldBitmap);
        delete m_pBitmap;
    }
    if(m_pMemDC != NULL)
    {
       delete m_pMemDC;
    }
    m_pMemDC = new CDC;
    m_pBitmap = new CBitmap;
    m_pMemDC->CreateCompatibleDC(NULL); // use screen DC
    m_pBitmap->CreateCompatibleBitmap(NULL, cx, cy);
    m_pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
}

3) Make your WM_PAINT handler look like this:

void CYourWnd::OnPaint()
{
CPaintDC dc(this);
CDC *pDC = m_pMemDC;
CRect rectClip;
dc.GetClipBox(&rectClip);

doDraw(pDC); // draw your shtuff. Make sure to erase the drawing surface using FillRect or something

dc.BitBlit(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), m_pMemDC, rectClip.left, rectClip.top, SRCCOPY);
}

4) Override WM_ERASEBKGND with ClassWizard. Make it look like this:

BOOL CYourWnd::OnEraseBkgnd(CDC *pDC)
{
return TRUE;
}

5) Override WM_DESTROY and add these lines:

void CYourWnd::OnDestroy()
{
    if(m_pBitmap != NULL)
    {
        m_pMemDC->SelectObject(m_pOldBitmap);
        delete m_pBitmap;
    }
    if(m_pMemDC != NULL)
    {
       delete m_pMemDC;
    }
}

// END_CODE
A couple of caveats:

This doesn't handle printing. In general, you shouldn't use offscreen drawing when drawing to a printer DC. A simple check in your OnPaint can determine if you are printing or not.
If you wanted to be really suave, you'd pass the clip rect to your doDraw() function and only redraw the parts that were going to be blitted anyway.

Hope this helps.
0
 
JagerMAuthor Commented:
This doesn't seem to work. I keep getting a first chance exception from the CreateCompatibleBitmap() method in the OnSize() routine.
0
 
MikeP090797Commented:
When using CreateCompatibleDC and CreateCompatibleBitmap the first param should be a dc handle and not a NULL. you can obtain the handle by calling CClientDC dc(this); dc->m_hdc
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
anichiniCommented:
Already figured this one out. Waiting for a comment form anichini. Sorry.
0
 
JagerMAuthor Commented:
Answering for credit I hope?

0
 
anichiniCommented:
1. A little bitter? It doesn't seem like EE has a method to hold a dialog between expert and asker. How do you expect to be given credit for something that hasn't been tested? If there is a way to hold this dialog, please inform me of it. It wasn't my intent to not award any points, only clarify the answer.

2. As far as the solution goes. Do you have to have members (CDC* and CBitmap* 's) or can you just create the variables you need in OnPaint(), do the drawing, and repalce all GDI resources? I have a CRect member that holds the cleint rect and is updated every time OnSize() is called. This can be used for the CreateCompatibleBitmap call.

3. Fooling around with this type of solution, there still seems to be noticeable flicker? I'm basically redrawing every 200mS, is this going to happen no matter what or should there be absolutely no flicker?

No hard feelings.

0
 
JagerMAuthor Commented:
1) Not bitter at you, but at the way EE is set up. I don't like EE's interface and it can be frustrating at times. For example, it would be nice for the asker to be able to divvy up points among multiple experts, because I agree that I shouldn't get full credit for an answer that didn't work 100% the way it should, and MikeP probably should have gotten some points for pointing out the problem, etc.

As for getting credit for something that hasn't been tested: You have to understand that I have a real job that I get paid for, and that I answer these questions for free. I don't have time to completely test every answer I post. I usually test answers that I haven't ever done before. But I have a lot of experience with what you are trying to do - I've written a lot of offscreen drawing apps, and I know that the above method works. I just screwed up one of the details when writing the answer.

2) You could do that, but creating a DC/bitmap can be expensive, so its best to do the creation/destruction only when you need to (when the size of the window changes)

3) There shouldn't be a flicker. Make sure that the only drawing command you send to the CPaintDC is the bitblt call. Can you post a comment with the relevant portions of your code, perhaps I can figure out whats going on by taking a look.

0
 
anichiniCommented:
I got it working this morning and it looks great. I noticed memory leaks for a CBitmap and CDC members and found that my CWnd derived class's OnDestroy wasn't getting called for some reason. Any thoughts? I put that code into the destructor and it works fine. Thanks for your help, this is a great trick to know!
0
 
JagerMAuthor Commented:
If you just delete your window without calling DestroyWindow() on it, overriden OnDestroy methods will not get called:

so if you do this:
delete pMyWnd;

instead you should do this:
pMyWnd->DestroyWindow();
delete pMyWnd;

The reason: when the destructor is called, it first calls all the derived classes' destructors. The derived classes' destructors all remove their entries from the vtable for the object (it's part of the C++ language definition). By the time it gets to CWnd::~CWnd, where DestroyWindow is called if the window has not already been destroyed, your override of OnDestroy has been erased by CYourWnd::~CYourWnd.

0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 4
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now