学VC有大半年了,今天发现有几个基本的问题都没有搞清楚.
   建一个对话框,里面放一个树状控件.现在想把对话框的背景弄成渐变色.
   void CTestView::OnPaint()
{
//CPaintDC dc(this); // device context for painting,少这个后树状控件没有了
HDC hdc=::GetDC(m_hWnd);
RECT rect;
GetClientRect(&rect);        //颜色渐变函数
DrawGradientV(hdc,RGB(49,68,212),RGB(147,255,255),rect); ::ReleaseDC(m_hWnd,hdc);
}
  我的问题是:
1)凡是对于背景的操作,比如画图,贴图等。是响应OnPaint()还是OnEraseBkgnd??????我知道都可以,但是谁能说说区别和各自优缺点?
2)上述OnPaint函数中,都是获得dc,都可以画背景,但是
        //CPaintDC dc(this); // 少这个后树状控件没有了????????
HDC hdc=::GetDC(m_hWnd);//CPaintDC dc(this)区别和联系????????
求解,希望大家都说说,讨论都有分,我现在是越来越迷茫了!!!
谢谢!!!!谢谢!!!!

解决方案 »

  1.   

    1. 都可以,一般放到OnPaint函数中,OnEraseBkgnd函数return TRUE; 如果你觉得闪动比较厉害的话。
    2. CPaintDC It performs a CWnd::BeginPaint at construction time and CWnd::EndPaint at destruction time. 绘制完父窗口,会再绘制所有的子窗口。如果你没有设置WS_CLIPCHILDREN属性的话。
      

  2.   

    1)OnEraseBkgnd函数return TRUE;后还是闪得厉害....
    2)“如果你没有设置WS_CLIPCHILDREN属性的话”是设置哪个?树状控件吗?
      

  3.   

    1)凡是对于背景的操作,比如画图,贴图等。是响应OnPaint()还是OnEraseBkgnd??????我知道都可以,但是谁能说说区别和各自优缺点?
    先执行OnEraseBkgnd,再执行OnPaint,执行OnEraseBkgnd时可以选择是否执行OnPaint
    也就是说如果你只重写OnPaint,系统会自动先执行OnEraseBkgnd的,即擦除背景,貌似擦完了还有别的如果重写OnEraseBkgnd,并return true,就不会执行OnPaint,好处是减少重绘时间防止闪烁首先在OnEraseBkgnd指定区域不重绘,其次在内存中把图画完,最后在外部直接draw2)上述OnPaint函数中,都是获得dc,都可以画背景,但是
      //CPaintDC dc(this); // 少这个后树状控件没有了????????

    HDC hdc=::GetDC(m_hWnd);//CPaintDC dc(this)区别和联系????????
    都是dc的知识 关键词有了,自己百度或google,找几篇文章看看,最好结合msdn看
      

  4.   

    注释//CPaintDC dc(this);后,树状控件不是没有了,是没刷新没显示出来,因为你没激活Window的BeginPaint
      

  5.   

    多年VC的困惑
    学VC有大半年了,今天发现有几个基本的问题都没有搞清楚.[
    亮了
      

  6.   

    你这样 处理onpaint 界面会闪的吧?
    CPaintDC 的构造方法中会调用 BeginPaint ,析构中会调用EndPaint。
    这个两个api很重要,BeginPaint  会把当前的无效区设置为有效。如果当前没有 无效区 ,系统就不会再发送 wm_paint 消息了
      

  7.   

    哥,真的好闪
    用了双缓冲,OnEraseBkgnd函数return TRUE;后还是闪得厉害....
      

  8.   

    OnPaint应该用CPaintDC
    不要用别的dc
    CPaintDC也只是用在OnPaint函数中
    别的地方不要用
      

  9.   

    如果窗口无效区,CPaintDC()引起EraseBkgnd,背景擦除后回到paint,你的绘制代码执行。paint退出时窗口变有效。
      

  10.   

    2)上述OnPaint函数中,都是获得dc,都可以画背景,但是
      //CPaintDC dc(this); // 少这个后树状控件没有了????????
    HDC hdc=::GetDC(m_hWnd);//CPaintDC dc(this)区别和联系????????Windows能够识别那些仅需要重绘的区域,比如你把一个窗口的一半拖动到屏幕外面,然后再把它拖回到屏幕中央,那么他只重绘被屏幕盖住看不见的那一半区域,而另一半没有被屏幕盖住的部分是不会重绘的,这样就很大程度上避免了闪烁和提高了绘制速度。如果你用HDC hdc=::GetDC(m_hWnd);//那么上面所说的Windows支持的只绘制无效区域的功能等于没有实现,不管被盖住的是仅仅一丁点的区域还是全部区域,他全部给你重绘了。CPaintDC dc(this); // 如果你用这种DC(其实内部是用的BeginPaint和EndPaint)那么它就只重绘无效(也就是被遮盖住的区域)区域,最大的好处就是避免闪烁。
      

  11.   

      //CPaintDC dc(this); // 少这个后树状控件没有了???????而这种现象,根据我的实际经验,的确会出现这种情况,我以前做CListCtrl界面美化的时候也发现过这个问题,用GetDC获取的DC绘制的时候会绘制到CHeaderCtrl上面了,所以你非要问原因是怎么回事,也不好解释,Windows内部的东西,我没有去纠结它,只知道CPaintDC 就解决了。
      

  12.   

    而这种现象,根据我的实际经验,的确会出现这种情况,我以前做CListCtrl界面美化的时候也发现过这个问题,用GetDC获取的DC绘制的时候会绘制到CHeaderCtrl上面了,所以你非要问原因是怎么回事,也不好解释,Windows内部的东西,我没有去纠结它,只知道CPaintDC 就解决了。
      

  13.   


    哪个地方频繁用invalidata(TRUE); 了
      

  14.   


    不是 调用 invalidata导致的,而是他没有没有使用 CPaintDC 。也就是没有调用BeginPaint,EndPaint,OnPaint执行后,系统中的无效区没有被置为有效,系统会继续网消息队列中放wm_paint消息。自然就会闪了
      

  15.   


    GetDC返回的DC的默认的剪裁区是整个窗口的大小,CPaintDC,的构造函数中调用了BeginPaint,BeginPaint会设置DC的剪裁区。应该是这个导致的
      

  16.   

    我开始没用invalidata(TRUE),后面觉得树状控件太闪了,
    就 GetDlgItem(IDC_TREE1)->InvalidateRect(NULL,true);//true,刷新指定窗口部分
    就不闪了。但是这里true是重绘啊!照理说就是不要它重绘嘛,false才不绘啊,就不闪了嘛。
    这个是怎么回事?
      

  17.   

    很简单,先调用一下父类的onpaint,再添加自己的处理就行了,没有这个,客户区会一直处于无效状态,会不停的刷新,所有没有东西显示,而且CPU老高
      

  18.   

    楼主的问题很好,楼主不是标题党。
    我对于GDI有过长期的研究和使用,向你推荐《Windows图形编程》这本书。1)凡是对于背景的操作,比如画图,贴图等。是响应OnPaint()还是OnEraseBkgnd??????我知道都可以,但是谁能说说区别和各自优缺点?答:都可以。这个是个人习惯问题。我一般不使用OnEraseBkgnd,直接在OnPaint里完成所有事情,包括刷背景。这样做的好处是,如果你使用后备缓冲,那么会完全消除闪烁,而如果在OnEraseBkgnd刷背景,即使使用后备缓冲,也不会消除闪烁。因为许多区域亮度在短时间内变化了2次。请在百度搜索我关于闪烁原理和解决办法的文章。2)上述OnPaint函数中,都是获得dc,都可以画背景,但是
       //CPaintDC dc(this); // 少这个后树状控件没有了????????
     HDC hdc=::GetDC(m_hWnd);//CPaintDC dc(this)区别和联系????????
    对这个的解释是,BeginPaint函数会获得一个设备环境,该设备环境有一个关联的裁剪区域【会裁剪掉子窗口,如果指定了WS_CLIPCHILDREN风格】,并且限制在无效区域【使用InvalidateRect等函数指定】内。而GetDC获得的设备环境的裁剪区域是整个个窗口的客户区,即没有裁剪掉任何部分,你刷背景如果使用这个设备环境,那么会把子窗口的区域也刷了。建议楼主还是看看那本书,避免盲目。
      

  19.   

    我的两篇关于闪烁的博客:MDI子窗口最大化后,切换子窗口会闪烁
    http://hi.baidu.com/chendeping/blog/item/545798f231dd121bb07ec58d.htmlMFC的CFormView在改变大小时子窗口区域闪烁的解决办法
    http://hi.baidu.com/chendeping/blog/item/a7f86bfa02aa589d59ee90d2.html
      

  20.   

      谢谢你,小平同志!我确实不是标题党,握个爪,感动得泪奔...
      我只是想把平时做的东西产生的疑惑寻求解决,做个总结,避免以后再在类似的问题上犹豫和疑惑。
     我还是有些不解。
      你的第一个回答,你肯定听过OnEraseBkgnd画后景色,OnPaint画前景色的说法,不知你怎么看?
      对于你回答的第二个问题,请问【会裁剪掉子窗口,如果指定了WS_CLIPCHILDREN风格】,这句话,谁指定了WS_CLIPCHILDREN风格?
      第三个,你的博客和你推荐的书我一定好好研究。
      谢谢,再次握个抓...
      

  21.   

    CPaintDC 、CWindowDC、 CClientDC、 CDC关系图:一句话概括:CPaintDC            无效区dc,      相当于BeginPaint,   EndPaint
    CClientDC          客户区dc,      相当于GetDC,   ReleaseDC
    CWindowDC      整窗口dc,      相当于GetWindowDC,   ReleaseDC
    CDC                     任何dc,          相当于CreateDC,   DeleteDC 一、CPaintDC Mfc自动生成的OnPaint函数都会定义一个CPaintDC类型的变量。如: 
    [cpp] view plaincopy    void CXXXXXX::OnPaint()  
        {  
            CPaintDC dc(this); // device context for painting  
            // TODO: Add your message handler code here  
            // Do not call CView::OnPaint() for painting messages  
        }  
        // If you add a minimize button to your dialog, you will need the code below  
        //  to draw the icon.  For MFC applications using the document/view model,  
        //  this is automatically done for you by the framework.  
        void CXXXXDlg::OnPaint()  
        {  
            if (IsIconic())  
            {  
                CPaintDC dc(this); // device context for painting  
          
                SendMessage(WM_ICONERASEBKGND, reinterpret_cast<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  
            {  
                CDialog::OnPaint();  
            }  
        }  MSDN上的解释:The CPaintDC class is a device-context class derived from CDC. It performs a CWnd::BeginPaint at construction time and CWnd::EndPaint at destruction time.(翻译:CPaintDC继承自CDC,它在构造函数中执行CWnd::BeginPaint,在析构函数中调用CWnd::EndPaint) A CPaintDC object can only be used when responding to aWM_PAINT message, usually in yourOnPaint message-handler member function.(翻译:CPaintDC对象通常当响应WM_PAINT消息时在OnPaint消息处理函数中被使用。)题外话,这个英文按照中国人的逻辑来说应该这样表达:when responding to a WM_PAINT message, a CPaintDC object can only be used usually in yourOnPaint message-handler member function. 二、CClientDCMSDN上的解释:The CClientDC class is derived from CDC and takes care of calling the Windows functionsGetDC at construction time andReleaseDC at destruction time. This means that the device context associated with aCClientDC object is the client area of a window.(翻译:CClientDC继承自CDC,它在构造函数中调用GetDC,在析构函数中调用ReleaseDC.这意味着与CClientDC对象联系的DC是窗口的客户区。) 三、CWindowDCThe CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that aCWindowDC object accesses the entire screen area of aCWnd (both client and nonclient areas).( 翻译:获取整个屏幕区域,包括客户区和非客户区。) CSDN:CPaintDC只能在OnPaint()中使用,CClientDC只和客户区有关,可以在任何地方使用。 CPaintDC代表整个窗口,而CClientDC代表窗口的客户区(不包括标题栏、边框),要选择合适的DC进行绘制。 CPaintDC一般是用在OnPaint()函数里的。CPaintDC对象一构造,消息队列中的WM_PAINT会被取走,而CClientDC是用在非OnPaint()函数里画图的。用CClientDC时WM_PAINT消息没有从消息队列中清除,CPaintDC结束时会自动清除该消息。 CPaintDC是一个特殊的设备环境封闭类,它主要处理windows的wm_paint消息。CClientDC可以自动调用GetDC和ReleaseDC函数。CwindowDC是从CDC类继承,用于得到桌面窗口设备环境指针。CclinetDC用于窗口客户区,CwindowDC用于整个窗口,包括非客户区。 在OnPaint中用CPaintDC仅仅对需要刷新的地方进行重绘。> 往一个对话框上画图,在OnPaint()里是不是只能用   CPaintDC?
    恰恰相反,CPaintDC是推荐只用在OnPaint中的。诚如   i_noname(晚九朝五)   所言:“它的范围是WM_PAINT消息所要重画的区域大小”,因为这个类的构造函数中调用了BeginPaint,析构函数中调用了EndPaint。这两个API是针对WM_PAINT消息使用的,所以不要把CPaintDC用在非对WM_PAINT消息响应的函数中。
      

  22.   

      既然很多人都提了这个问题,我就解释一下。
      额,当时觉得这几个问题我碰到了很多次,每次都要纠结很久。以我大半年,甚至快一年的VC学习经验来说,这    种问题我认为应该是基础问题。但是我却没有搞明白,觉得很不应该。
      所以发帖请教各位高人,至于用词上的问题,实在是我觉得我学了怎么久的VC,连这些问题都没有搞明白,所以一时郁闷,就用了"多年"这个词。
      让各位见笑了...
      

  23.   

    1. 这两个调用哪个都没问题,不过你画背景的话,推荐在OnEraseBkgnd中画,然后返回TRUE;
    2.CPaintDC dc 这个的话,参考WM_PAINT消息,CPaintDC只不过帮你处理了你应该处理的东西。如果不处理,WM_PAINT消息会一直发送。至于闪嘛,双缓冲。不会有问题的。有也是你用的有问题了。
      

  24.   

    楼主属于没有认真看MSDN文档的人。
      

  25.   

    谢谢你的指教,我确实有这个问题。小段的,还没什么,大段的MSDN就比较有鸭梨了,主要是英语不给力。。,
    惭愧
    好好学英语,好好看MSDN
      

  26.   

    太闪,不要频繁用Invalidate(),
    而应用InvalidateRect
      

  27.   

    频繁使用Invalidate如果使用双缓冲且图像变化不大是不会闪烁的
    楼主可以用InvalidateRect排除掉树状控件
    当然,也可以用DirectUI的方法在父窗口画一个(不是重绘树状控件)并放入双缓冲中。这也是最好的解决途径