duilib中CTextUI控件設置文字時不刷新顯示的bug

  duilib中常用的文本控件CTextUI,有個很重要的特性:控件的長度會自適應所設置文本的長度,即所設置文本的的長度變化了,控件的長度也會隨之變化。但是會有這樣的問題:當設置的文本長度大於先前舊的文本的長度時,界面沒有及時刷新,仍然顯示的是之前的舊的文本,需要通過頁面的切換才能刷新顯示。下面我們來詳細看一下自適應特性是如何實現的、是如何被應用的、自適應特性引起的問題以及問題是如何來解決的。

        1、自適應特性的實現

        CTextUI控件提供了EstimateSize接口,接口的代碼如下所示。

  1. SIZE CTextUI::EstimateSize(SIZE szAvailable)  
  2. {  
  3.     RECT rcText = { 0, 0, MAX(szAvailable.cx, m_cxyFixed.cx), 9999 };  
  4.     rcText.left += m_rcTextPadding.left;  
  5.     rcText.right -= m_rcTextPadding.right;  
  6.     if( m_bShowHtml )  
  7.     {     
  8.         int nLinks = 0;  
  9.         CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, NULL, NULL, nLinks, DT_CALCRECT | m_uTextStyle);  
  10.     }  
  11.     else  
  12.     {  
  13.         CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, m_iFont, DT_CALCRECT | m_uTextStyle);  
  14.     }  
  15.     SIZE cXY = {rcText.right - rcText.left + m_rcTextPadding.left + m_rcTextPadding.right,  
  16.         rcText.bottom - rcText.top + m_rcTextPadding.top + m_rcTextPadding.bottom};  
  17.   
  18.     if( m_cxyFixed.cy != 0 ) cXY.cy = m_cxyFixed.cy;  
  19.     return cXY;  
  20. }  
SIZE CTextUI::EstimateSize(SIZE szAvailable)
{
    RECT rcText = { 0, 0, MAX(szAvailable.cx, m_cxyFixed.cx), 9999 };
    rcText.left += m_rcTextPadding.left;
    rcText.right -= m_rcTextPadding.right;
    if( m_bShowHtml )
	{   
        int nLinks = 0;
        CRenderEngine::DrawHtmlText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, NULL, NULL, nLinks, DT_CALCRECT | m_uTextStyle);
    }
    else
	{
        CRenderEngine::DrawText(m_pManager->GetPaintDC(), m_pManager, rcText, m_sText, m_dwTextColor, m_iFont, DT_CALCRECT | m_uTextStyle);
    }
    SIZE cXY = {rcText.right - rcText.left + m_rcTextPadding.left + m_rcTextPadding.right,
        rcText.bottom - rcText.top + m_rcTextPadding.top + m_rcTextPadding.bottom};

    if( m_cxyFixed.cy != 0 ) cXY.cy = m_cxyFixed.cy;
    return cXY;
}
從上面代碼我們看出,給DrawHtmlText和DrawText函數均傳入了rcText,並且在接下來的代碼中,利用rcText修正了控件相關的位置信息。代碼看起來會有疑問,本接口僅僅是用來估計控件的大小,爲什麼要調用DrawHtmlText和DrawText函數呢?是用這兩個函數來實現估計的?那我們接着看看CRenderEngine::DrawText的代碼。

  1. void CRenderEngine::DrawText( HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle )  
  2. {  
  3.     ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);  
  4.     if (pstrText == NULL || pManager == NULL) return;  
  5.     ::SetBkMode(hDC, TRANSPARENT);  
  6.     ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));  
  7.     HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont));  
  8.     ::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );  
  9.     ::SelectObject(hDC, hOldFont);  
  10. }  
void CRenderEngine::DrawText( HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle )
{
    ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);
    if (pstrText == NULL || pManager == NULL) return;
    ::SetBkMode(hDC, TRANSPARENT);
    ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));
    HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont));
    ::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
    ::SelectObject(hDC, hOldFont);
}
代碼是將文字繪製到制定的dc上,那到底是如何實現估計的呢?只能一步步看,於是到msdn中查看::DrawText API的函數說明,如下所示。

  1. int DrawText(  
  2.   HDC hDC,          // handle to DC   
  3.   LPCTSTR lpString, // text to draw   
  4.   int nCount,       // text length   
  5.   LPRECT lpRect,    // formatting dimensions   
  6.   UINT uFormat      // text-drawing options   
  7. );  
int DrawText(
  HDC hDC,          // handle to DC
  LPCTSTR lpString, // text to draw
  int nCount,       // text length
  LPRECT lpRect,    // formatting dimensions
  UINT uFormat      // text-drawing options
);
注意第四個參數,對應的說明:

lpRect
[in/out] Pointer to a RECT structure that contains the rectangle (in logical coordinates) in which the text is to be formatted.

它既是一個傳入參數,也是一個傳出參數,傳出應該就是所繪畫文字繪畫後所佔用的實際區域大小。所以結合上面的追蹤過程,正是給DrawHtmlText和DrawText函數均傳入了rcText參數,並且執行完後將繪畫的文字佔用的實際大小返回給了rcText傳了出來,進而用實際佔用大小來重新更新CTextUI控件的大小,這樣就實現了控件對文字長度的自適應特性。

        2、自適應特性的使用

        這種自適應的特性可以巧妙的用在一些場合下面。比如類似於QQ討論組的聊天框,在聊天框的右側會顯示當前的討論組成員列表,如果成員是管理員,我們需要在成員名稱後面添加一個小圖標,以標識該成員是管理員。一般的做法是,利用CContainer的自動佈局特性,我們將不變寬度的控件設置固定寬度值,位於右邊的管理員圖標寬度也設置爲固定值,這樣中間的成員名稱是文本,可長可短,不設置成固定值,使用CLabelUI控件即可。這樣,中間的名稱寬度就是可變的,但是這樣做有個問題,管理員圖標會固定在每個成員item的左右邊,不能跟隨名稱緊貼名稱後面,顯得不太美觀。後來考慮到CTextUI的對文字長度能自適應,就改用CTextUI,能很好的實現管理員圖標能緊貼在成員名稱後面。

        3、自適應特性引起的問題及問題分析

        上面列舉的例子中,當成員名稱過長時,由於討論組成員列表的寬度是固定的,當名稱過長時,會將管理員圖標給擠掉,看不見了或顯示不全。這該如何解決呢?好在CTextUI有個maxwidth屬性來設置最大寬度,這樣我們將管理員圖標位置預留出來,估計出控件的最大寬度,設置一下即可,當成員名稱顯示不下時,以省略號顯示。

        除了這個問題,還有個更嚴重的問題:當設置的文本長度大於先前舊的文本的長度時,界面沒有及時刷新,仍然顯示的是之前的舊的文本,需要通過頁面的切換才能刷新顯示。CTextUI是duilib中的基本控件,有這個問題是沒法使用的,是要想辦法解決掉的。

        原因估計是這樣的:由於CTextUI是自適應文字寬度的,對於舊的能顯示全的文字,寬度是和文字寬度一致的;當我們設置更長的文字時,估計沒有沒有觸發CTextUI::EstimateSize接口的調用,導致控件還是以前的大小,從而出現新文字沒有刷新或顯示不全的問題。

       下面我們就來從代碼中看一下不能刷新顯示的問題是如何產生的。設置文字時,會調用基類CControlUI的SetText接口,接口的代碼如下所示。

  1. void CControlUI::SetText( LPCTSTR pstrText )  
  2. {  
  3.     if( m_sText == pstrText )   
  4.     {  
  5.         return;  
  6.     }  
  7.   
  8.     m_sText = pstrText;  
  9.     Invalidate();  
  10. }  
void CControlUI::SetText( LPCTSTR pstrText )
{
    if( m_sText == pstrText ) 
	{
		return;
	}

    m_sText = pstrText;
    Invalidate();
}

        在上面的代碼中調用CControlUI::Invalidate函數,

  1. void CControlUI::Invalidate()  
  2. {  
  3.     if( !IsVisible() ) return;  
  4.   
  5.     RECT invalidateRc = m_rcItem;  
  6.   
  7.     CControlUI* pParent = this;  
  8.     RECT rcTemp;  
  9.     RECT rcParent;  
  10.     while( pParent = pParent->GetParent() )  
  11.     {  
  12.         rcTemp = invalidateRc;  
  13.         rcParent = pParent->GetPos();  
  14.         if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) )   
  15.         {  
  16.             return;  
  17.         }  
  18.     }  
  19.   
  20.     if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc);  
  21. }  
void CControlUI::Invalidate()
{
	if( !IsVisible() ) return;

	RECT invalidateRc = m_rcItem;

	CControlUI* pParent = this;
	RECT rcTemp;
	RECT rcParent;
	while( pParent = pParent->GetParent() )
	{
		rcTemp = invalidateRc;
		rcParent = pParent->GetPos();
		if( !::IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) 
		{
			return;
		}
	}

	if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc);
}
        然後調到CPaintManagerUI::Invalidate中,即

  1. void CPaintManagerUI::Invalidate(RECT& rcItem)  
  2. {  
  3.     ::InvalidateRect(m_hWndPaint, &rcItem, FALSE);  
  4. }  
void CPaintManagerUI::Invalidate(RECT& rcItem)
{
    ::InvalidateRect(m_hWndPaint, &rcItem, FALSE);
}
        也就是最終調用API函數::InvalidateRect,產生一個WM_PAINT消息。於是進入CPaintManagerUI::MessageHandler中,查看收到WM_PAINT消息是如何處理的。

  1. bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)  
  2. {  
  3. ......  
  4.     case WM_PAINT:  
  5.         {  
  6.             // Should we paint?   
  7.             RECT rcPaint = { 0 };  
  8.             if( !::GetUpdateRect( m_hWndPaint, &rcPaint, FALSE) )   
  9.             {  
  10.                 return true;  
  11.             }  
  12.   
  13.             if( NULL == m_pRoot )   
  14.             {  
  15.                 PAINTSTRUCT ps = { 0 };  
  16.                 ::BeginPaint( m_hWndPaint, &ps );  
  17.                 ::EndPaint( m_hWndPaint, &ps );  
  18.                 return true;  
  19.             }              
  20.             // Do we need to resize anything?   
  21.             // This is the time where we layout the controls on the form.   
  22.             // We delay this even from the WM_SIZE messages since resizing can be   
  23.             // a very expensive operation.   
  24.             if( m_bUpdateNeeded )   
  25.             {  
  26.                 m_bUpdateNeeded = false;  
  27.                 RECT rcClient = { 0 };  
  28.                 ::GetClientRect( m_hWndPaint, &rcClient );  
  29.                 if( !::IsRectEmpty( &rcClient ) )   
  30.                 {  
  31.                     if( m_pRoot->IsUpdateNeeded() )   
  32.                     {  
  33.                         m_pRoot->SetPos( rcClient );  
  34.                         if( m_hDcOffscreen != NULL ) ::DeleteDC( m_hDcOffscreen );  
  35.                         if( m_hDcBackground != NULL ) ::DeleteDC( m_hDcBackground );  
  36.                         if( m_hbmpOffscreen != NULL ) ::DeleteObject( m_hbmpOffscreen );  
  37.                         if( m_hbmpBackground != NULL ) ::DeleteObject( m_hbmpBackground );  
  38.                         m_hDcOffscreen = NULL;  
  39.                         m_hDcBackground = NULL;  
  40.                         m_hbmpOffscreen = NULL;  
  41.                         m_hbmpBackground = NULL;  
  42.                     }  
  43.                     else   
  44.                     {  
  45.                         CControlUI* pControl = NULL;  
  46.                         while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) )   
  47.                         {  
  48.                             pControl->SetPos( pControl->GetPos() );  
  49.                         }  
  50.                     }  
  51. ......  
  52. }  
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
......
    case WM_PAINT:
        {
            // Should we paint?
            RECT rcPaint = { 0 };
            if( !::GetUpdateRect( m_hWndPaint, &rcPaint, FALSE) ) 
			{
				return true;
			}

            if( NULL == m_pRoot ) 
			{
                PAINTSTRUCT ps = { 0 };
                ::BeginPaint( m_hWndPaint, &ps );
                ::EndPaint( m_hWndPaint, &ps );
                return true;
            }            
            // Do we need to resize anything?
            // This is the time where we layout the controls on the form.
            // We delay this even from the WM_SIZE messages since resizing can be
            // a very expensive operation.
            if( m_bUpdateNeeded ) 
			{
                m_bUpdateNeeded = false;
                RECT rcClient = { 0 };
                ::GetClientRect( m_hWndPaint, &rcClient );
                if( !::IsRectEmpty( &rcClient ) ) 
				{
                    if( m_pRoot->IsUpdateNeeded() ) 
					{
                        m_pRoot->SetPos( rcClient );
                        if( m_hDcOffscreen != NULL ) ::DeleteDC( m_hDcOffscreen );
                        if( m_hDcBackground != NULL ) ::DeleteDC( m_hDcBackground );
                        if( m_hbmpOffscreen != NULL ) ::DeleteObject( m_hbmpOffscreen );
                        if( m_hbmpBackground != NULL ) ::DeleteObject( m_hbmpBackground );
                        m_hDcOffscreen = NULL;
                        m_hDcBackground = NULL;
                        m_hbmpOffscreen = NULL;
                        m_hbmpBackground = NULL;
                    }
					else 
					{
						CControlUI* pControl = NULL;
						while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) 
						{
							pControl->SetPos( pControl->GetPos() );
						}
					}
......
}
        上面只給出了一部分代碼。搜索一下調用EstimateSize函數的地方,發現CVerticalLayoutUI、CHorizontalLayoutUI、CTileLayoutUI和CTabLayoutUI類的SetPos函數中都會調到EstimateSize函數。從上面的代碼可以看出,僅僅是產生一個WM_PAINT消息是不行的,是執行不到CVerticalLayoutUI、CHorizontalLayoutUI、CTileLayoutU等類的SetPos函數,執行不到CTextUI::EstimateSize函數的。也就是說,在設置文字的CControlUI::SetText函數中僅僅調用CControlUI::Invalidate函數是行不通的,是沒法更新CTextUI控件大小的。
         要調用到CVerticalLayoutUI、CHorizontalLayoutUI、CTileLayoutU等類的SetPos函數,首先要CPaintManagerUI::m_bUpdateNeeded爲TRUE,那是不是要調用CPaintManagerUI::NeedUpdate(),先將m_bUpdateNeeded置爲TRUE呢?於是加上代碼,CTextUI文字還是刷新不了。於是繼續看上面的代碼,發現只有當CControlUI::m_bUpdateNeeded爲TRUE時,纔會調用控件對應的SetPos接口。於是有添加了CControlUI::NeedUpdate調用,問題還是沒解決。其實,此時只是觸發了控件本身CTextUI::SetPos的調用,此函數並沒有調用CTextUI::EstimateSize,當然CTextUI::SetPos中不可能直接調用CTextUI::EstimateSize,是由於牽涉到父佈局中的所有子控件(包括CTextUI控件)的擺放,理應由父佈局統一統籌。

        4、自適應特性引起的不刷新問題的解決

        那如何才能調到CTextUI::EstimateSize呢?該如何處理呢?考慮到CVerticalLayoutUI、CHorizontalLayoutUI、CTileLayoutUI和CTabLayoutUI類的SetPos函數中都會調到EstimateSize函數,而CTextUI控件一般是依附在CVerticalLayoutUI、CHorizontalLayoutUI等父佈局下的,那麼只要我們觸發這些父佈局調用對應的SetPos,就可以調用到CTextUI::EstimateSize函數,進而利用最新的文字來估計CTextUI控件的尺寸,將CTextUI控件的尺寸更新掉,從而能讓最新的文字刷新顯示出來。

        那如何觸發這些父佈局調用對應的SetPos呢?我們且看CPaintManagerUI::MessageHandler中代碼,

  1. bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)  
  2. {  
  3. ......  
  4.     case WM_PAINT:  
  5.         {  
  6.  ......  
  7.             if( m_bUpdateNeeded )   
  8.             {  
  9.                 m_bUpdateNeeded = false;  
  10.                 RECT rcClient = { 0 };  
  11.                 ::GetClientRect( m_hWndPaint, &rcClient );  
  12.                 if( !::IsRectEmpty( &rcClient ) )   
  13.                 {  
  14.                     if( m_pRoot->IsUpdateNeeded() )   
  15.                     {  
  16.                         m_pRoot->SetPos( rcClient );  
  17.                         if( m_hDcOffscreen != NULL ) ::DeleteDC( m_hDcOffscreen );  
  18.                         if( m_hDcBackground != NULL ) ::DeleteDC( m_hDcBackground );  
  19.                         if( m_hbmpOffscreen != NULL ) ::DeleteObject( m_hbmpOffscreen );  
  20.                         if( m_hbmpBackground != NULL ) ::DeleteObject( m_hbmpBackground );  
  21.                         m_hDcOffscreen = NULL;  
  22.                         m_hDcBackground = NULL;  
  23.                         m_hbmpOffscreen = NULL;  
  24.                         m_hbmpBackground = NULL;  
  25.                     }  
  26.                     else   
  27.                     {  
  28.                         CControlUI* pControl = NULL;  
  29.                         while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) )   
  30.                         {  
  31.                             pControl->SetPos( pControl->GetPos() );  
  32.                         }  
  33.                     }  
  34. ......  
  35. }  
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
......
    case WM_PAINT:
        {
 ......
            if( m_bUpdateNeeded ) 
			{
                m_bUpdateNeeded = false;
                RECT rcClient = { 0 };
                ::GetClientRect( m_hWndPaint, &rcClient );
                if( !::IsRectEmpty( &rcClient ) ) 
				{
                    if( m_pRoot->IsUpdateNeeded() ) 
					{
                        m_pRoot->SetPos( rcClient );
                        if( m_hDcOffscreen != NULL ) ::DeleteDC( m_hDcOffscreen );
                        if( m_hDcBackground != NULL ) ::DeleteDC( m_hDcBackground );
                        if( m_hbmpOffscreen != NULL ) ::DeleteObject( m_hbmpOffscreen );
                        if( m_hbmpBackground != NULL ) ::DeleteObject( m_hbmpBackground );
                        m_hDcOffscreen = NULL;
                        m_hDcBackground = NULL;
                        m_hbmpOffscreen = NULL;
                        m_hbmpBackground = NULL;
                    }
 					else 
					{
 						CControlUI* pControl = NULL;
						while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) 
						{
							pControl->SetPos( pControl->GetPos() );
						}
					}
......
}
如果讓整個窗口的m_pRoot全佈局全部刷新一遍,肯定實現上述更新CTextUI控件尺寸的目的。但是這代價太高了,僅僅是爲了刷新一個CTextUI子控件的位置和尺寸,就讓窗口的整個佈局都重新刷新一遍,這肯定不可取的、不合理的。於是看其中的這段代碼,
  1. else   
  2.   
  3.     CControlUI* pControl = NULL;  
  4. while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) )   
  5. {  
  6.     pControl->SetPos( pControl->GetPos() );  
  7. }  
 					else 
					{
 						CControlUI* pControl = NULL;
						while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) 
						{
							pControl->SetPos( pControl->GetPos() );
						}
					}
繼續跟到__FindControlFromUpdate函數,如下所示。

  1. CControlUI* CALLBACK CPaintManagerUI::__FindControlFromUpdate( CControlUI* pThis, LPVOID pData )  
  2. {  
  3.     return pThis->IsUpdateNeeded() ? pThis : NULL;  
  4. }  
CControlUI* CALLBACK CPaintManagerUI::__FindControlFromUpdate( CControlUI* pThis, LPVOID pData )
{
    return pThis->IsUpdateNeeded() ? pThis : NULL;
}
所以,我們只要將CTextUI所依附的父佈局CVerticalLayoutUI或者CHorizontalLayoutUI等設置爲需要刷新即可調用到CVerticalLayoutUI或者CHorizontalLayoutUI等的SetPos函數。所以最終的解決辦法:在CTextUI中重寫父類CControlUI中的虛函數SetText,添加的代碼如下所示。

  1. void CTextUI::SetText( LPCTSTR pstrText )  
  2. {  
  3.     CControlUI::SetText( pstrText );  
  4.   
  5.     NeedParentUpdate();  
  6. }  
void CTextUI::SetText( LPCTSTR pstrText )
{
	CControlUI::SetText( pstrText );

	NeedParentUpdate();
}
爲了說明問題,將CControlUI::NeedParentUpdate的代碼也貼出來,
  1. void CControlUI::NeedParentUpdate()  
  2. {  
  3.     if( GetParent() )  
  4.     {  
  5.         GetParent()->NeedUpdate();  
  6.         GetParent()->Invalidate();  
  7.     }  
  8.     else  
  9.     {  
  10.         NeedUpdate();  
  11.     }  
  12.   
  13.     if( m_pManager != NULL )  
  14.     {  
  15.         m_pManager->NeedUpdate();  
  16.     }  
  17. }  
void CControlUI::NeedParentUpdate()
{
    if( GetParent() )
    {
        GetParent()->NeedUpdate();
        GetParent()->Invalidate();
    }
    else
    {
        NeedUpdate();
    }

    if( m_pManager != NULL )
    {
        m_pManager->NeedUpdate();
    }
}
所以CTextUI::SetText不僅將父佈局設置爲需要刷新狀態,而且調用CPaintManagerUI::NeedUpdate將m_bUpdateNeeded置爲TRUE,進而保證代碼能運行到CPaintManagerUI::MessageHandler中的WM_PAINT消息處理分支的如下代碼段,

  1. else   
  2.   
  3.     CControlUI* pControl = NULL;  
  4. while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) )   
  5. {  
  6.     pControl->SetPos( pControl->GetPos() );  
  7. }  
 					else 
					{
 						CControlUI* pControl = NULL;
						while( pControl = m_pRoot->FindControl(__FindControlFromUpdate, NULL, UIFIND_VISIBLE | UIFIND_ME_FIRST) ) 
						{
							pControl->SetPos( pControl->GetPos() );
						}
					}

最終解決本案例中CTextUI控件不刷新顯示的問題。

 

http://blog.csdn.net/chenlycly/article/details/17291765#

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章