使用子類化窗口的方法來實現對Static控件進行背景色修改

 

最近遇到了一個問題,duilib工程中使用了Static控件,然後需要更改Static控件的背景色。

最開始的想法是在CreateWindowEx中能否指定畫刷之類的信息,發現沒有。

 

然後在網上找了一些信息,發現這篇文章可以解決。https://blog.csdn.net/softn/article/details/51718352

不過這個需要依賴向父窗口發送的WM_CTLCOLORSTATIC消息來解決。那實現起來就不夠通用,需要和父窗口耦合了。

所以也否決了這個做法。

 

再繼續找了下,沒有什麼好方法,不過找到了一些子類化的資料,想想之前也項目中也有使用子類化的方法,所以就採用了這個方法來解決了。

代碼很簡單,通過CreateWindowEx創建Static控件,然後通過SetWindowLong設置新的窗口過程,在新的窗口過程中響應WM_PAINT消息進行繪製即可。關鍵代碼如下: 


    WNDPROC OldProc = NULL;
    LRESULT CALLBACK StaticControlProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        if (msg == WM_PAINT)
        {
            PAINTSTRUCT ps = { 0 };
            HDC hdc = BeginPaint(hWnd, &ps);

            RECT rect;
            ::GetClientRect(hWnd, &rect);
            DWORD dwColor = RGB(255, 0, 0);
            HBRUSH hBrush = ::CreateSolidBrush(dwColor);
            HPEN hPen = ::CreatePen(PS_SOLID, 1, dwColor);
            HBRUSH hOldBrush = (HBRUSH)::SelectObject(hdc, hBrush);
            HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);

            ::Rectangle(hdc, 0, 0, rect.right, rect.bottom);

            ::SelectObject(hdc, hOldBrush);
            ::SelectObject(hdc, hOldPen);
            ::DeleteObject(hBrush);
            ::DeleteObject(hPen);

            EndPaint(hWnd, &ps);
            return 0;
        }
        else
        {
            return ::CallWindowProc(OldProc, hWnd, msg, wParam, lParam);
        }
    }




    HWND hwnd = ::CreateWindowExW(dwStyleEx, L"Static", NULL, dwStyle, left, top, width, height,
        hParentWnd, NULL, hInstance, NULL);
    OldProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (LONG)StaticControlProc);

 

子類化有兩種實現方式,具體參考下面msdn的文章介紹。

https://docs.microsoft.com/en-us/windows/win32/controls/subclassing-overview

(1)使用SetWindowLong設置新的窗口過程。

(2)使用SetWindowSubclass來設置子類化窗口過程。

需要注意的是子類化這些函數調用都不能跨線程!!!
Warning  You cannot use the subclassing helper functions to subclass a window across threads.

第一種方式就是前面的代碼所示。

第二種方式也很簡單,同樣的邏輯代碼調整如下:


	LRESULT CALLBACK StaticControlProc(HWND hWnd, UINT uMsg, WPARAM wParam,
		LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
	{
		if (uMsg == WM_PAINT)
		{
			PAINTSTRUCT ps = { 0 };
			HDC hdc = BeginPaint(hWnd, &ps);

			RECT rect;
			::GetClientRect(hWnd, &rect);
			DWORD dwColor = RGB(255, 0, 0);
			HBRUSH hBrush = ::CreateSolidBrush(dwColor);
			HPEN hPen = ::CreatePen(PS_SOLID, 1, dwColor);
			HBRUSH hOldBrush = (HBRUSH)::SelectObject(hdc, hBrush);
			HPEN hOldPen = (HPEN)::SelectObject(hdc, hPen);

			::Rectangle(hdc, 0, 0, rect.right, rect.bottom);

			::SelectObject(hdc, hOldBrush);
			::SelectObject(hdc, hOldPen);
			::DeleteObject(hBrush);
			::DeleteObject(hPen);

			EndPaint(hWnd, &ps);
			return 0;
		}
		else
		{
			return DefSubclassProc(hWnd, uMsg, wParam, lParam);
		}
	}


    HWND hwnd = ::CreateWindowExW(dwStyleEx, L"Static", NULL, dwStyle, left, top, width, height,
        hParentWnd, NULL, hInstance, NULL);
	SetWindowSubclass(hwnd, StaticControlProc, 0, 0);

相對來說更簡單一些了。

 

雖然msdn說ComCtl32.dll在版本6之前使用第一種方式。版本6以及之後使用第二種方式。

Subclassing Controls Prior to ComCtl32.dll version 6

Subclassing Controls Using ComCtl32.dll version 6

不過目前兩種方式從msdn上來看xp都已經支持了。建議使用第二種方式。

 

第二種方式可以解決第一種方式的一些缺點:

Disadvantages of the Old Subclassing Approach

The following list points out some of the disadvantages of using the previously described approach to subclassing a control.

  • The window procedure can only be replaced once.
  • It is difficult to remove a subclass after it is created.
  • Associating private data with a window is inefficient.
  • To call the next procedure in a subclass chain, you cannot cast the old window procedure and call it, you must call it by using the CallWindowProc function.

 

順便提一下,使用第一種方式的時候msdn提過可以使用SetProp函數給窗口設置數據,這是一個好的方法,第二種方式,或者其他場景都是可以使用的。

msdn文檔:

https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setpropa

https://docs.microsoft.com/zh-cn/windows/win32/winmsg/using-window-properties

函數原型:

BOOL SetPropA(
  HWND   hWnd,
  LPCSTR lpString,
  HANDLE hData
);

 

最後使用msdn介紹子類化的文檔來總結吧。

如果一個控件的特性能爲你所用,但是你還想添加一點特性,你可以通過子類化來添加這些特性。子類化可以使你使用控件的所有特性,並且包含你給他添加的特性。

 

突然發現子類化是個好東西,之前一直沒有這麼深刻的理解。有一個面試的時候面試官還問我了子類化,我還沒有答上來,還在和他確認是不是繼承一個控件類,想起來真實尷尬。所以還是要多寫博客總結做過的事情,影響纔會深刻。加油body!

 

 

 

補充:

MFC子類化參考資料:https://www.cnblogs.com/just-bg/p/3929044.html

WTL子類化參考資料:https://www.cnblogs.com/wdhust/archive/2010/09/18/1830097.html

 

順便提一下窗口超類化:

超類化根據已有的(windows系統中已經註冊過的)窗口類,比如“Edit”,”Button”等,複製其WNDCLASS(EX)結構,構造一個新類,並提供額外的功能和行爲。

換句話說就是註冊一個新的窗口類,然後也需要更改窗口過程來定製一些特性。

下面這段代碼就是超類化的實現。

BOOL CMyEdit2::RegisterMe()
{
    WNDCLASS wc;
    if (!GetClassInfo(NULL,_T("Edit"),&wc)) {
        return FALSE;
    }

    wc.lpszClassName = CLASS_NAME;
    m_oldWndProc = wc.lpfnWndProc;
    wc.lpfnWndProc = MyWndProc;
    return RegisterClass(&wc);
}

MFC超類化參考資料:https://www.cnblogs.com/just-bg/p/3929044.html

WTL超類化參考資料:https://www.cnblogs.com/wdhust/archive/2010/09/18/1830097.html

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