• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            微塵--KeepMoving

            為了忘卻的記憶
            posts - 3, comments - 2, trackbacks - 0, articles - 13
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            摘要

            作為一個程序員,我們經常會在程序中用到Windows通用控件。比如按鈕控件,進度條控件等等。但是有時我們需要給控件更多的特色,這就需要做控件的子類化(subclassing).

            子類化一個Windows控件與子類化一個C++類不同,子類化一個控件要求你把一個窗口的一些或所有的消息映射都替換成自己的函數來響應,這樣你就有效的阻止了控件去做系統默認的行為,而按自己的想法去做。子類化有兩種類型:實例子類化(instance subclassing)和全局子類化(global subclassing)。實例子類化是子類化一個窗口中的單一實例,全局子類化是把整個窗口子類化為一個特殊的類型。這里我們僅討論單一實例子類化。

            記住CWnd派生類對象與窗口本身(一個HWND)的差別是很重要的。你的C++CWnd-派生類對象包含了一個指向HWND的成員函數,并且包含了當處理消息時HWND消息泵的響應函數(比如WM_PAINT,WM_MOUSEMOVE)。但你用一個C++對象子類化一個窗口時,你就把HWND與C++對象關聯起來,并且設置了處理消息時把自定義的回調函數提供給HWND消息使用。

            子類化過程很簡單,首先創建一個類映射窗口的所有消息,然后把控件用作為這個類的實例。例如,下面的例子中我們做一個按鈕的子類化。

            新類

            為了子類化一個控件,我們需要創建一個新類,并映射所有我們感興趣的消息。為了簡便,我們一般都從控件標準類中派生自己的新類,這里與按鈕控件對應的標準類為CButton。

            下面假定我們要實現的效果是,當鼠標懸停在按鈕上方時,按鈕顯示為黃色。首先我們使用ClassWizard創建一個CButton的派生類,叫做CMyButton

             

            在MFC框架中從CButton派生自己的類有許多好處,最大的好處是我們不用手工添加任何一行代碼就可以創建了一個擁有全部默認功能的Windows控件。因為MFC實現了所有的默認的消息映射,因此我們可以挑選我們感興趣的消息自己處理,而不用去管其他消息。

            這里我們要為按鈕設計的功能是,鼠標懸停時變為黃色。

            為了檢查鼠標是否懸停于按鈕上,我們設置一個成員變量m_bOverControl ,TRUE表示鼠標懸停,然后設置一個周期(使用定時器)跟蹤鼠標是否已離開控件,這是因為,系統并沒有OnMouseEnterOnMouseLeave函數供我們調用,因此我們必須使用OnMouseMove。如果,在一個時間點上,發現鼠標已離開按鈕,我們關閉定時器并重畫控件。

            使用ClassWizard加入WM_MOUSEMOVE和WM_TIMER的消息映射,響應函數分別是OnMouseMoveOnTimer。

             

            ClassWizard將在你的按鈕類文件中加入下面的代碼:

            BEGIN_MESSAGE_MAP(CMyButton, CButton)//{{AFX_MSG_MAP(CMyButton)
            ON_WM_MOUSEMOVE()
            ON_WM_TIMER()//}}AFX_MSG_MAP
            END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CMyButton message handlersvoid CMyButton::OnMouseMove(UINT nFlags, CPoint point)
            {// TODO: Add your message handler code here and/or call default
            CButton::OnMouseMove(nFlags, point);
            }void CMyButton::OnTimer(UINT nIDEvent)
            {// TODO: Add your message handler code here and/or call default
            CButton::OnTimer(nIDEvent);
            }

            消息映射的入口(即BEGIN_MESSAGE_MAP) 建立了窗口消息與響應函數的對應關系。ON_WM_MOUSEMOVE把WM_MOUSEMOVE消息與OnMouseMove函數建立響應的關系,ON_WM_TIMER m把WM_TIMER消息與OnTimer函數建立了響應的關系。這些宏定義在MFC的源文件中,我們不需要去看,只要按照約定來做就可以了。

            假設我們已經聲明了兩個變量m_bOverControlm_nTimerID,類型分別是BOOL和UINT, 并且在類的構造函數中把它們初始化,我們的消息處理應使用下面的代碼:

            void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
            {if (!m_bOverControl)// Cursor has just moved over control
            {
            TRACE0("Entering controln");
            m_bOverControl = TRUE;// Set flag telling us the mouse is in
            Invalidate();// Force a redraw
            SetTimer(m_nTimerID,100, NULL);// Keep checking back every 1/10 sec
            }
            CButton::OnMouseMove(nFlags, point);// drop through to default handler
            }void CMyButton::OnTimer(UINT nIDEvent)
            {// Where is the mouse?
            CPoint p(GetMessagePos());
            ScreenToClient(&p);// Get the bounds of the control (just the client area)
            CRect rect;
            GetClientRect(rect);// Check the mouse is inside the controlif (!rect.PtInRect(p))
            {
            TRACE0("Leaving controln");// if not then stop looking...
            m_bOverControl = FALSE;
            KillTimer(m_nTimerID);// ...and redraw the control
            Invalidate();
            }// drop through to default handler
            CButton::OnTimer(nIDEvent);
            }

            最后我們來畫出我們需要的效果,我們不再進行消息映射,而是重載CWnd::DrawItem虛函數。只有當控件設置owner-drawn風格時這個函數才能被調用,并且這個函數沒有默認的實現代碼,虛函數的設計只為了在派生類中進行實現。

             

            使用ClassWizard重載DrawItem函數,并加入下面的代碼

            void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
            {
            CDC* pDC   = CDC::FromHandle(lpDrawItemStruct->hDC);
            CRect rect = lpDrawItemStruct->rcItem;
            UINT state = lpDrawItemStruct->itemState;
            CString strText;
            GetWindowText(strText);// draw the control edges (DrawFrameControl is handy!)if (state & ODS_SELECTED)
            pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);else
            pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH);// Deflate the drawing rect by the size of the button’s edges
            rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));// Fill the interior color if necessaryif (m_bOverControl)
            pDC->FillSolidRect(rect, RGB(255,255,0));// yellow// Draw the textif (!strText.IsEmpty())
            {
            CSize Extent = pDC->GetTextExtent(strText);
            CPoint pt( rect.CenterPoint().x - Extent.cx/2,
            rect.CenterPoint().y - Extent.cy/2 );if (state & ODS_SELECTED)
            pt.Offset(1,1);int nMode = pDC->SetBkMode(TRANSPARENT);if (state & ODS_DISABLED)
            pDC->DrawState(pt, Extent, strText, DSS_DISABLED, TRUE,0, (HBRUSH)NULL);else
            pDC->TextOut(pt.x, pt.y, strText);
            pDC->SetBkMode(nMode);
            }
            }

            接下來,我們剩下最后一步。為控件設置owner drawn風格。我們可以在對話框的資源編輯器中,右鍵單擊按鈕控件,選擇“屬性”,然后在Style中選中owner drawn風格。但是有一種更好的方法,使得使用新建類子類化的按鈕自動的設置owner drawn風格。為了完成這個功能,我們重載最后一個函數:PreSubclassWindow。

            這個函數將在子類化窗口時被調用,次序是在CWnd::CreateDDX_Control之后,這就是說,無論是動態的創建窗口實例還是使用對話框模板創建,這個函數都將被調用。PreSubclassWindow在窗口子類化創建后和窗口被顯示前被調用,換句話說,這是我們來做窗口初始化的一個最好時機。

            一個重點要注意的地方是: 如果你是用對話框資源創建一個控件,那么你要子類化的控件將不會響應WM_CREATE消息,所以我們不能在OnCreate函數中做初始化的工作,因為它并不是在所有的情況下都被調用。

            使用ClassWizard重載PreSubclassWindow函數并加入下面的代碼

            void CMyButton::PreSubclassWindow()
            {
            CButton::PreSubclassWindow();
            ModifyStyle(0, BS_OWNERDRAW);// make the button owner drawn
            }

            祝賀 - 你的Cbutton派生類已經完成。

            子類化

            在創建時使用DDX子類化

            在這個例子中,我們使用對話框編輯器在對話框中加入了一個新的按鈕:

             

            然后,使用ClassWizard為你的按鈕控件添加成員變量,變量類型選擇我們剛剛建立的類CMyButton

            ClassWizard g會在對話框的DoDataExchange函數中創建一個DDX_Control調用。DDX_Control啟動了子類化過程,使得按鈕控件使用CMyButton類進行消息映射,而不是使用通常的CButton。

            使用沒有在ClassWizard中注冊的類子類化窗口

            如果你在工程中加入了一個新的窗口類,并且希望使用這個新類類型子類化你的窗口,但是ClassWizard中并沒有提供新類的選項,那么你需要重新生成class wizard文件。

            先備份以下工程中的.clw文件,然后刪除它。接下來在Visual Studio中按Ctrl+W。你將看到一個提示框,要求你加入ClassWizard中包含類的文件,確認選擇的文件中包含了新類的文件(soarlove注:一般情況下,選擇“add all”即可。

            現在你的新類已經可以供選擇。如果不想這樣做,你還有一個通用的方法,就是在選擇類型的時候使用通用的類(比如CButton),然后在頭文件中手工把通用類(CButton)改為你的新類(CMyButton)。

            子類化一個存在的窗口

            使用DDX固然簡單,但是不能幫助我們實現一個已存在窗口的子類化。比如你想在combobox中子類化一個Edit控件,那么在你子類化Edit控件之前,你需要先創建combobox控件。

            這種情況下,我們使用SubclassDlgItem或者SubclassWindow函數。這兩個函數允許你動態的子類化一個窗口,換句話說,把一個新的窗口實例與已經存在的窗口建立關聯。

            比如,假設有一個對話框中包含了一個按鈕IDIDC_BUTTON1。這個按鈕已經被創建,我們想用一個CMyButton的實例來與之關聯,以使得按鈕符合我們需要的行為。

            為了做到這些,我們需要有一個新類型的實例,最后的方法是在對話框或視的頭文件中加入成員函數。

            CMyButton m_btnMyButton;

            然后在對話框的OnInitDialog (或任何適當的地方) 中調用:

            m_btnMyButton.SubclassDlgItem(IDC_BUTTON1,this);

            假設你已經有了一個窗口的指針,或者你工作在一個CView或其他CWnd派生類中里面的控件被動態的創建,或者你不想使用SubclassDlgItem函數,那么你可以使用下面的方法:

            CWnd* pWnd = GetDlgItem(IDC_BUTTON1);// or use some other method to get// a pointer to the window you wish// to subclass
            ASSERT( pWnd && pWnd->GetSafeHwnd() );
            m_btnMyButton.SubclassWindow(pWnd->GetSafeHwnd());

             

            畫按鈕是非常簡單的,不需要考慮按鈕的風格(比如flat風格),也不需要考慮適應文字,僅僅需要考慮你畫的范圍。如果你編譯運行提供的演示代碼,那么你將看到,當鼠標懸停于按鈕上方時,按鈕變為黃色。

             

            注意,實際上我們只重載了畫的函數,并截取了鼠標移動的函數。其余的功能都還是使默認響應的。

            結論

            子類化并不難 - 你只要認真的選擇你要子類化的類并且知道你要映射那些消息。要熟悉你要子類化的類,了解提供的消息和類中的虛函數。

            久久精品国产只有精品66| 色欲综合久久中文字幕网| 免费观看成人久久网免费观看| 精品久久久久香蕉网| 久久婷婷五月综合97色直播| 亚洲精品午夜国产VA久久成人| 国产成人久久精品区一区二区| 久久精品一区二区三区中文字幕| 久久综合亚洲色一区二区三区| 精品少妇人妻av无码久久| 久久久精品无码专区不卡| 久久久久久毛片免费播放| 日本精品久久久久中文字幕| 久久精品国产AV一区二区三区| 久久久青草久久久青草| 久久精品人妻中文系列| 国产精品美女久久久网AV| 国产精品对白刺激久久久| 亚洲欧美日韩精品久久亚洲区| 91久久精品无码一区二区毛片| 色综合久久久久无码专区| 亚洲午夜久久久| 老司机午夜网站国内精品久久久久久久久| 东方aⅴ免费观看久久av| 久久久受www免费人成| 久久最新精品国产| 97超级碰碰碰久久久久| 亚洲欧美成人综合久久久| 久久WWW免费人成一看片| 久久综合色老色| 久久久久国产精品人妻| 怡红院日本一道日本久久 | 亚洲欧美精品一区久久中文字幕 | 精品久久久久中文字幕日本| 日韩人妻无码精品久久久不卡 | 久久久午夜精品福利内容| 青青久久精品国产免费看| 欧美日韩成人精品久久久免费看 | 国产午夜福利精品久久2021| 久久亚洲私人国产精品vA | 久久久久人妻精品一区|