各位前辈大家好,小弟初学VC,前两天写了个时钟,我用灰色作为背景画刷,在中间画了一个用位图作画刷的圆作为钟面,再在上面画指针,使用一个timer每秒钟刷新一次客户区重画以实现时针,分针和秒针的重画,基本实现了预期的效果。但是现在出现了一个无法忍受的问题,由于是每隔一秒钟就重画一次,钟面有非常严重的闪烁感,这个应该是1s的刷新频率无法避免的,但是如果将timer中的频率调成100ms或10ms,闪烁感觉更加严重,不知道是为什么原因。
这个问题应该如何解决?请各位高手给个解决方法。或者VC里面有没有什么分层画图,分层重画的方法。在此,先谢过了。

解决方案 »

  1.   

    转:用MFC对话框做无闪烁图片重绘-界面类编程-VC
    用应用程序向导生成一个基于对话框的应用程序 把对话框Styles里的Border设置为Resizing,并把Minimize box跟Maximize box复选按钮都勾上 现在我们为资源导入一张位图然后我们为程序添加四个私有变量private: int height; 
    int width; 
    CBitmap myBitmap; 
    BITMAP bm; 然后用类向导添加WM SIZE消息输入下列代码: width = cx; 
    height = cy; 
    Invalidate(); 然后在添加WM_PAINT消息在函数里添加以下代码: CDC *pDC=new CDC; 
    CPaintDC dc(this); 
    //CClientDC dc(NULL); 
    pDC->CreateCompatibleDC(&dc); 
    pDC->SelectObject(myBitmap);
    //将图片伸缩成我们设定的大小。 dc.StretchBlt(0,0,width,height,pDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY); //如果不准备进行缩放可以用BITBLT。 
    //dc.BitBlt(0,0,bm.bmWidth,bm.bmHeight,pDC,0,0,SRCCOPY); delete pDC; 然后做最后一步在程序初始化时候导入我们的图片并让对话大小跟图片一样 在 OnInitDialog函数最里添加如下代码: 
    // TODO: Add extra initialization here 
    myBitmap.LoadBitmap(IDB_BITMAP1); 
    myBitmap.GetObject(sizeof(BITMAP),&bm); 好了我们现在就做了一个可以显示图片的对话框并且对话改变大小的时候图片大小也会改变,不过这个程序有一个缺点,就是重绘的时候有闪烁,性能不怎么好, 现在我们按上面的步骤在做一个对话框,导入位图, 添加以下变量:public: 
    CSize m_sizeBuffer; 
    CBitmap m_bmpBackBuffer; 
    CBitmap* m_pbmpPattern; 在OnInitDialog函数里添加以下代码 
    m_pbmpPattern=CBitmap::FromHandle((HBITMAP)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDB_BACKGROUND), IMAGE_BITMAP, 0,0, LR_SHARED)); 重写OnPaint函数 if (IsIconic()) 

          CPaintDC dc(this); // device context for painting 
          SendMessage(WM_ICONERASEBKGND, (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 { 
        CPaintDC dc(this); 
        CDC dcBackBuffer; 
        dcBackBuffer.CreateCompatibleDC(&dc); 
        //重新计算区域面积 
        CRect rectClient;
        GetClientRect(rectClient); 
        if ( m_sizeBuffer != rectClient.Size() ) { 
             m_sizeBuffer = rectClient.Size(); 
            if ( m_bmpBackBuffer.GetSafeHandle() != NULL ) 
                m_bmpBackBuffer.DeleteObject(); 
            m_bmpBackBuffer.CreateCompatibleBitmap(&dc, m_sizeBuffer.cx, m_sizeBuffer.cy); 
       }    CBitmap* pOldBmp = dcBackBuffer.SelectObject(&m_bmpBackBuffer); 
       //画背景 
       if ( NULL == m_pbmpPattern ) { 
            dcBackBuffer.FillSolidRect(rectClient, RGB(255,0,255)); 
        } 
      else { 
          CDC dcPat; 
          dcPat.CreateCompatibleDC(&dcBackBuffer); 
          CBitmap* pbmpOld = dcPat.SelectObject(m_pbmpPattern); 
          BITMAP bitmap; 
          if ( m_pbmpPattern->GetBitmap(&bitmap) && bitmap.bmWidth > 0 && bitmap.bmHeight > 0 ) { 
                    BOOL m_bTilePattern ; 
                    m_bTilePattern =TRUE; 
                    if ( m_bTilePattern ) { 
                           for (int y=0; y<rectClient.bottom+bitmap.bmHeight; y += bitmap.bmHeight) { 
                                for (int x=0; x<rectClient.right+bitmap.bmWidth; x += bitmap.bmWidth) { 
                                      dcBackBuffer.BitBlt(x,y, bitmap.bmWidth, bitmap.bmHeight, &dcPat, 0,0, SRCCOPY);         }
                           } 
                    } 
                   else { 
                        dcBackBuffer.FillSolidRect(rectClient, RGB(255,0,255)); 
                        dcBackBuffer.BitBlt((m_sizeBuffer.cx-bitmap.bmWidth)/2,(m_sizeBuffer.cy-                                 bitmap.bmHeight)/2, bitmap.bmWidth, bitmap.bmHeight, &dcPat, 0,0, SRCCOPY);
                   } 
            } 
            dcPat.SelectObject(pbmpOld); 
        } 
        dc.BitBlt(0,0,m_sizeBuffer.cx,m_sizeBuffer.cy,&dcBackBuffer,0,0,SRCCOPY); 
        dcBackBuffer.SelectObject(pOldBmp); 
        CDialog::OnPaint(); 

    现在我们的对话框就是真正利用了双缓冲来绘图性能比起第一个要好,而且还解决了闪烁问题
      

  2.   

    当应用程序中使用多次GDI调用绘制到窗口中时,在窗口清除并被重新绘制时,会出现明显的闪烁。使用双缓冲技术可轻松消除闪烁。步骤如下:1、假设当前显示设备上下文为CDC * pDC;2、使用CDC对象创建可兼容设备上下文,      CDC memdc;      memdc.CreateCompatibleDC(pDC);3、使用CBitmap对象创建可兼容为图,      CBitmap bmp;      bmp.CreateCompatibleBitmap(pDC,SCREEN_WIDTH,SCREEN_HEIGHT);4、将CBitmap对象选入CDC对象中,     CBitmap *poldbmp=memdc.SelectObject(&bmp);5、绘图操作,在memdc中进行,如:     for(int i=0;i<N;i++)
        {
                  memdc.MoveTo(i,0);
                  memdc.LintTo(i,N);
         }    Rectangle(hdcMem,0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
       DrawMenuButton(hdcMem);6、使用BitBlt函数将memdc内容拷贝至pDCpDC->BitBlt(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,&memdc,0,0,SRCCOPY);7、最后别忘了,    memdc.SelectObject(poldbmp);
    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1684756
      

  3.   

    谢谢楼上的两位大哥
    我知道双缓存绘图可以解决图片闪烁的问题,开始我也试着用双缓存来解决闪烁的问题,代码如下:   //创建一个与目前显示设备描述表hdc兼容的内存设备描述表
       HDC hdcMem=CreateCompatibleDC(hdc);
       //创建一个符合目前设备DC的位图格式
       HBITMAP MemBitmap=CreateCompatibleBitmap(hdc,rt.right -rt.left ,rt.bottom -rt.top );
       //把创建的位图和内存DC关联
       HBITMAP pOldBmp=(HBITMAP)SelectObject(hdcMem,MemBitmap); 
       DeleteObject(MemBitmap);   //自己写的函数DrawClockFace来绘制钟表外观
       DrawClockFace(hdcMem);
       //根据当前时间来绘制指针,总之是在hdcMem中把图画好
       ...   BitBlt(hdc,0,0,rt.right -rt.left ,rt.bottom -rt.top ,hdcMem,0,0,SRCCOPY);
       SelectObject(hdcMem, pOldBmp);
       DeleteDC(hdcMem);但是运行之后问题依旧,因为我依然采用的是每过1s重画一次,所以仍然会有闪烁感.而且双缓存好象只能缓存方行的区域,而我画的钟面是原形的,使用了BitBlt后圆形区域外的那部分与整个背景不协调.
    现在的问题是我的图象中的一部分(绘制指针的那部分)必须每隔1s就重画一次,而另一部分DrawClockFace(hdcMem)这部分我并不想让其重画.
    我采用的是::InvalidateRect(hwnd,NULL,1),每过1s就让整个客户区全部重画,所以会有闪烁感,
    还有没有其他办法,让图中的线条部分失效,或者说能不能把客户区分为几层,只重画其中的一部分.
    请各位前辈尽量用API,对C++类的写法不太习惯,呵呵.
    继续等待,再次谢谢大家,谢谢大家的关注.
      

  4.   

    真是太感谢Mackz了,就是这句出的问题,改成0之后闪烁感好多了,不过还是有些轻微的闪
    这个Mark是什么意思呀,楼上的大哥能不能说详细一点
      

  5.   

    楼主的问题也是我一直找的问题,但我看了Mackz的回答有了一点的解惑。
    我知道这种双缓冲技术,似乎是这么说的,但一般做了下都还是闪烁,更有闪的猛的势头。
      

  6.   

    虽然没找到mackz说的::InvalidateRect(hwnd,NULL,0);但找了InvalidateRect(NULL,true);然后把true改成0,神了真的不闪了。。实在太感谢mackz了。。