CStatic透明大讨论
vc6.0中静态文本控件CStatic透明的大讨论:
初衷:窗体背景为图片,开了一个子线程AfxBeginThread(...)间隔10毫秒刷新该CStatic,来实现不断的数字变化。
为了让CStatic透明,采取如下方案:
 在 WM_CTLCOLOR中用pDC->SetBackMode(TRANSPARENT)并返回空刷子来实现。
可在文字变化后出现重叠现象,所谓的黑乎乎的一堆。
有人建议用Invaldate(rect)来实现。但!!请别忘记了,我用的子线程间隔10毫秒就刷新一次,如此一来,运行不一会儿,就出现CPU利用率高且风扇狂转,虽然程序能正常运行,但仍然出现闪烁现象!这结果太差劲了。也有人建议自绘,让CStatic绘制父窗口的背景图片,再绘制文字,来实现,但我怎么也没成功!
希望有高人给出代码,或者更好的方案。我的目的是风扇不狂转,CPU不高且界面不闪烁。有人说这是不可能的,但这句话实在是难以置信。强大的VC,一个小标签控件透明都要风扇狂转?不太可能吧。

解决方案 »

  1.   

    现在的问题不是必要不必要的问题,而是如何解决的问题。就算200ms间隔的时间,仔细观察,也会闪烁。
    哪位留下邮箱或QQ,我发源代码过来,帮我解决一下
      

  2.   

    又发了一次哈。使用OnCtlColor方式很容易内存泄露的。直接改成双缓存DC绘图输出文本不是更好看些?而且还不会闪屏。
      

  3.   


    afx_msg LRESULT OnFresh(WPARAM wParam, LPARAM lParam);#define UM_FRESH WM_USER+11BEGIN_MESSAGE_MAP(CXXDlg, CDialog)
    ...
    ON_MESSAGE(UM_FRESH, OnFresh)
    END_MESSAGE_MAP()UINT __cdecl ThreadProc(LPVOID lParam)
    {
    HWND hWnd = (HWND)lParam;
    int nCount = 0;
    while(TRUE)
    {
    SendMessage(hWnd, UM_FRESH, (WPARAM)nCount, 0);
    nCount++;
    Sleep(10);
    }
    return 0;
    }
    void CXXDlg::OnOK() 
    {
    // TODO: Add extra validation here

    AfxBeginThread(ThreadProc, (LPVOID)GetSafeHwnd());
    }LRESULT CXXDlg::OnFresh(WPARAM wParam, LPARAM lParam)
    {
    UNREFERENCED_PARAMETER(lParam);

    int nCount = (int)wParam;
    CString str;
    str.Format(_T("Current value: %d"), nCount);
    CWnd* pWnd = GetDlgItem(IDC_STATIC1);
    CRect rc;
    pWnd->GetWindowRect(rc);
    ScreenToClient(rc);
    InvalidateRect(rc, TRUE);
    SetDlgItemText(IDC_STATIC1, str); return 0;
    }HBRUSH CXXDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
    {
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    // TODO: Change any attributes of the DC here
    if(pWnd->GetDlgCtrlID() == IDC_STATIC1)
    {
    pDC->SetBkMode(TRANSPARENT);
    pDC->SetTextColor(RGB(255, 0, 128));
    return (HBRUSH)GetStockObject(NULL_BRUSH);
    }
    // TODO: Return a different brush if the default is not desired
    return hbr;
    }
      

  4.   

    办法是有滴,定义一个CStatic的继承类
    class CStaticTrans : public CStatic
    在CStaticTrans的cpp文件中#define TRANS_BACK -1
    CStaticTrans::CStaticTrans(void)
    {
    m_TextColor = RGB(0,0,0);
    m_BackColor = TRANS_BACK;
    }HBRUSH CStaticTrans::CtlColor(CDC* pDC, UINT mCtrlColor)
    {
    m_Brush.DeleteObject(); if (m_BackColor == TRANS_BACK)
    {
    m_Brush.CreateStockObject(HOLLOW_BRUSH);
    pDC->SetBkMode(TRANSPARENT);
    }
    else
    {
    m_Brush.CreateSolidBrush(m_BackColor);
    pDC->SetBkColor(m_BackColor);
    } pDC->SetTextColor(m_TextColor); return (HBRUSH)m_Brush;
    }void CStaticTrans::UpdateCtrl(BOOL bErase /* = FALSE */)
    {
    CWnd *pParent = GetParent();
    CRect rect; GetWindowRect(rect);
    pParent->ScreenToClient(rect); pParent->InvalidateRect(rect, bErase);
    }BOOL CStaticTrans::OnEraseBkgnd(CDC* pDC)
    {
    return true;
    }在你的工程中用CStaticTrans对象即可。应该没有你说的这些毛病。
      

  5.   

    可以看一下这个例子http://www.viksoe.dk/code/iconpacksample.htm先取背景,然后自绘static
      

  6.   

    1、在父窗口(对话框)中响应WM_CTLCOLOR,SetBkMode(TRANSPARENT)并return GetStockObject(NULL_BRUSH)。
    2、在对Static SetWindowText之前,先对父窗口InvalidateRect和UpdateWindow,重画Static所占用的父窗口位置(擦除Static以前绘制的文本)。注意InvalidateRect中应该仅把Static所占用的rect送进去(通过Static的GetWindowRect和父窗口的ScreenToClient获得),而不要重画整个父窗口,否则严重影响性能,并且闪烁。
    3、(重要!)由于父窗口重画会导致Static的客户区无效,因此父窗口重画完成后会自动再次引发static的重画,从而无法达到擦除的目的,因此,在父窗口重画完成后(父窗口OnPaint的最后),要调用Static的ValidateRect(NULL),使其客户区有效化,从而避免其重画。
      

  7.   

    楼上的不用“唉”了,呵呵,我已经亲自、亲自、亲自试验过了,要满足如下要求:
    1、背景透明
    2、快速变化文字
    3、CPU利用率及风扇不狂转
    那些回复没有能实现的。
      

  8.   

    没有所谓“透明”的控件,你看到的都是控件把它挡住的父窗口的部分,作为控件本身的背景,看上去透明。
    如果楼主要自己实现。可以用代码截取父窗口背景图,然后BitBlt到static背景上。然后在背景上写字。注意。这两步要在内存dc上实现,然后弄好后,一次性贴到OnPaint的dc上。在static的OnEraseBkgnd中直接return TRUE,以防止背景闪烁。
    如果楼主不想自己写代码,可以用LibUIDK界面库,你说的都已经实现好了。你1毫秒刷新一次也没问题。闪烁与刷新频率没有关系,并且这点小事,不可能导致cpu风扇狂转的。
      

  9.   

    实践证明,9楼的不可行,失败。!13楼的兄台就是我最初的方案,似乎可行,关键代码如下:void Label::OnPaint() 
    {
      if(ParentBitmap==NULL)return; 
    CPaintDC dc(this);   
    CRect lo_rcClient;
    this->GetClientRect(&lo_rcClient);  
    CDC lo_dcMem;
    lo_dcMem.CreateCompatibleDC(&dc);
    lo_dcMem.SelectObject(ParentBitmap); 
      lo_dcMem.SelectObject((HBRUSH)GetStockObject(NULL_BRUSH));
    lo_dcMem.Rectangle(1,1, lo_rcClient.right, lo_rcClient.bottom);  
    //lo_dcMem.SetBkMode(TRANSPARENT);
    lo_dcMem.SetTextColor(RGB(255,0,0));
    CFont* lo_pFont = this->GetFont();
    lo_dcMem.SelectObject(lo_pFont);
    lo_dcMem.TextOut(Bound.left, Bound.top,Caption); 
    ::BitBlt(dc, 0, 0, lo_rcClient.Width(), lo_rcClient.Height(), lo_dcMem, Bound.left, Bound.top, SRCCOPY); 
    ::DeleteDC(lo_dcMem);}上面就是我继承了CStatic,叫做Label类,自绘OnPaint过程的代码。
    但,有一个关键的问题:
    背景图片能正常绘制,但:
    写文字的效果出现问题,加入
    lo_dcMem.SetBkMode(TRANSPARENT);
    就会出现文字部分(仅文字区域部分)重叠,黑乎乎的。
    不加这句吧,文字区域部分又不透明,
     
      

  10.   

    不可行?你怎么测试的?
    下面的代码是测试通过的:void CAaaDlg::OnPaint() 
    {
    CDialog::OnPaint();
    GetDlgItem(IDC_STATIC_1)->ValidateRect(NULL);
    }BOOL CAaaDlg::OnEraseBkgnd(CDC* pDC) 
    {
    CRect rect;
    CDC MemDC;
    BITMAP bm;
    CBitmap *pbmpOld; GetClientRect(&rect);
    m_bmpBack.GetBitmap(&bm);
    MemDC.CreateCompatibleDC(pDC);
    pbmpOld = MemDC.SelectObject(&m_bmpBack);
    pDC->SetStretchBltMode(COLORONCOLOR);
    pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &MemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
    MemDC.SelectObject(pbmpOld);
    MemDC.DeleteDC(); return TRUE;
    }HBRUSH CAaaDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
    {
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    if(pWnd->GetDlgCtrlID() == IDC_STATIC_1) {
    pDC->SetBkMode(TRANSPARENT);
    return (HBRUSH)GetStockObject(NULL_BRUSH);
    } return hbr;
    }void CAaaDlg::OnTimer(UINT nIDEvent) 
    {
    static int n = 0;
    CString str; CRect rect;
    GetDlgItem(IDC_STATIC_1)->GetWindowRect(&rect);
    ScreenToClient(&rect);
    InvalidateRect(&rect);
    UpdateWindow(); str.Format("%d", n++);
    GetDlgItem(IDC_STATIC_1)->SetWindowText(str);
    }
      

  11.   

    15楼请问,m_bmpBack是哪来的?
      

  12.   

    另,不知15楼的注意到了没有,
    你的代码,仍然是风扇狂转啊。
    我是8G内存,500G硬盘,I73.6G的CPU,够强悍了吧,照样狂转。
      

  13.   

    那是我在OnInitDialog里随便Load了一幅背景图,在OnEraseBkgnd里往对话框上贴,否则无法看出Static透明不透明。你不用管OnEraseBkgnd里的内容,那不过是测试用的,你用你自己画父窗口的代码就好。
      

  14.   

    风扇狂转?我的程序运行起来的时候CPU占用几乎是0。Timer是设成10毫秒的,不过我的机器显然跟不上这个速度就是了,算了一下,大约每秒能执行60次左右。
      

  15.   

    那还是看我的代码吧?
    http://topic.csdn.net/u/20100818/17/2b5209c7-b366-48d3-a657-6b321dbebae9.html
      

  16.   

    看到了,代码够乱。一运行就非法访问,结果发现ParentBitmap没赋初值,不知道你怎么能运行的。在CLabel构造函数里将ParentBitmap设为NULL,运行起来果然CPU狂转,结果发现你在Label::OnPaint()里面第一句是:if(ParentBitmap==NULL)return,第二句才是CPaintDC dc(this),结果当ParentBitmap==NULL的时候无限制地循环调用OnPaint,CPU当然狂转。这两句交换一下就好。至于其他方面,背景图像的载入和解码不应该放在CCopyScreenDlg::OnPaint()里面,而是应该放在CCopyScreenDlg::OnInitDialog()里,而CCopyScreenDlg::OnPaint()只管用保存好的Bitmap往屏幕上贴图。否则效率太低。其它代码明天再说吧。要改的地方实在太多。
      

  17.   

    使用OnCtlColor方式很容易内存泄露的。
      

  18.   

    临睡前顺便再说几句:你确定你的工作确实需要用多线程吗?就你目前的应用而言,用Timer足够了。当然,不排除目前这些仅仅是个测试,真正需要实现的功能确实不是Timer能应付的,非多线程不可。不过真要用多线程的话,那么你现在所做的显然还差得远。
    首先,在多个线程都需要访问的数据中,不能使用CString(比如你的那个Label类的成员Caption),因为CString内部所设计的算法就是不适合多线程的;
    第二,即使你把CString改成字符数组也是不够的,既然有多个线程都要访问、并且至少一个线程需要写入,那么对这个变量的访问应该进行同步处理;
    第三,不要在多个线程中对同一个界面元素进行操作(比如你在子线程中对主线程界面上的Label窗口进行Invalidate),否则不能保证不出问题。当然,就你目前所做的操作而言,还不至于出什么问题,但假若你以后对功能进行一些改进和调整呢?在现有的这个不安全的架构之上再添几句,就有可能出问题了。正确的方式是:谁的界面元素谁操作,其他线程如果需要改变界面的话,都应通过消息传递(PostMessage或者SendMessage)之类的机制来通知拥有界面的线程所需要进行的操作(这样的封装性也好得多,否则当你发现你的界面改变的时候,你连这段代码写在哪都很难找)。
      

  19.   

    xxd_qd
    邮件收到了吗?
    帮我解决一下吧,谢谢