做MFC編程,Static控制項是會經常用到的了,而使Static控制項背景透明,以及改變文本的內容、字型、顏色等屬性,都是會比較容易碰到的情況。
王道做法當然是繼承CStatic然後重載OnPaint(),完全自己來畫,這樣能夠獲得最大的靈活性,但就是比較麻煩,像我這種比較懶的,就更喜歡用下面的懶方法了。
同樣建立一個CStatic的衍生類別,處理父視窗的反射訊息WM_CTLCOLOR,即添加HBRUSH CtlColor(CDC *pDC, UINT nCtlColor)這個訊息映射函數。注意,不是HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)!我也不知道具體原理,反正我用後者從來沒成功過,甚至程式都不會運行到裡面。。。
其實還有一個方法,就是處理父視窗的OnCtlColor(),更簡單一點,但是不符合封裝的原則,所以這裡就不提了。
C++代碼
- HBRUSH CSample::CtlColor(CDC* pDC,
UINT nCtlColor)
- {
- // TODO: Change any attributes of the DC here
- pDC->SetBkMode(TRANSPARENT); // 設定透明背景
- pDC->SetTextColor(RGB(0, 0, 255)); // 設定文本顏色
- // TODO: Return a non-NULL brush if the parent's handler should not be called
- return (HBRUSH)GetStockObject(HOLLOW_BRUSH);
// 返回透明畫刷
- }
通過上述代碼,就可以得到彩色的文本以及透明的背景,但是,還存在一個問題,當該Static控制項的常值內容或者屬性,在運行過程中發生變化的時候,由於背景一直沒有擦除(為了實現透明),會出現重影,導致文本模糊成一團。
解決方案是,讓父視窗進行重繪更新,對,不要看錯了,是控制項所屬的父視窗,而不是控制項本身,讓控制項本身重繪也不會解決問題的,同樣我也不太清楚原理。。。
這裡還會引出一個問題,如果重繪整個父視窗,由於GDI並不內嵌雙緩衝,勢必造成嚴重的閃爍問題,解決辦法當然是只讓父視窗重繪控制項所佔的部分,其他部分不進行重繪,代碼如下:
C++代碼
- void CSample::SetText(const
TCHAR *pszText)
- {
- this->SetWindowText(pszText);
- RECT stRect;
- // 擷取控制項位置
- this->GetWindowRect(&stRect);
- // 重要!調用父視窗的S2C函數進行座標轉換
- this->GetParent()->ScreenToClient(&stRect);
- // 重繪控制項所在地區,在這裡擦除背景
- this->GetParent()->InvalidateRect(&stRect,
true);
- }
這樣就能夠實現動態改變文字屬性而不出現重影現象,注意這裡調用了父視窗的ScreenToClient()函數來進行座標的轉換,調用控制項本身的S2C函數的話,得到的座標無法用來進行下一步的重繪工作。
現在還有一個比較隱形問題,就是文本字串的長度,如果新的字串的長度比原來的長,而之前拖放Static控制項長度又不足的時候,就會造成超出的部分無法顯示,當然你大可以在拖放的時候就盡量弄得長一點,但是如果能隨著常值內容而自動調整控制項長度,那不是會好得多麼。
為了實現這樣的效果,上面的代碼要修改如下:
C++代碼
- void CSample::SetText(const
TCHAR *pszText)
- {
- CDC *pDC = this->GetDC();
- // 擷取文本在當前繪圖環境下所佔的寬度和高度
- CSize clSize = pDC->GetTextExtent(pszText, _tcslen(pszText));
- RECT stRect;
- // 擷取控制項當前矩形地區
- this->GetWindowRect(&stRect);
- // 調整寬度為新文本所佔寬度
- stRect.right = stRect.left + clSize.cx;
- // 重要!調用父視窗S2C函數轉換座標
- this->GetParent()->ScreenToClient(&stRect);
- // 調整控制項大小以適應新文本
- this->MoveWindow(&stRect);
- // 重繪控制項以避免重影
- this->GetWindowRect(&stRect);
- this->GetParent()->ScreenToClient(&stRect);
- this->GetParent()->InvalidateRect(&stRect,
true);
- }
同樣,這裡也是調用父視窗的S2C函數,這樣得到的座標才能正確使用。代碼經過上述修改,就實現了控制項隨文本動態調整寬度的效果。
以上只是實現Static背景透明、更改文本顏色以及動態調整控制項大小的簡單示範,實際的應用中可能還需要考慮很多情況,適當修改代碼,但基本原理是不變的。當然要獲得最大的靈活性,還是得自己來繪製了 - -