• <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++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            摘要

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

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

            記住CWnd派生類(lèi)對(duì)象與窗口本身(一個(gè)HWND)的差別是很重要的。你的C++CWnd-派生類(lèi)對(duì)象包含了一個(gè)指向HWND的成員函數(shù),并且包含了當(dāng)處理消息時(shí)HWND消息泵的響應(yīng)函數(shù)(比如WM_PAINT,WM_MOUSEMOVE)。但你用一個(gè)C++對(duì)象子類(lèi)化一個(gè)窗口時(shí),你就把HWND與C++對(duì)象關(guān)聯(lián)起來(lái),并且設(shè)置了處理消息時(shí)把自定義的回調(diào)函數(shù)提供給HWND消息使用。

            子類(lèi)化過(guò)程很簡(jiǎn)單,首先創(chuàng)建一個(gè)類(lèi)映射窗口的所有消息,然后把控件用作為這個(gè)類(lèi)的實(shí)例。例如,下面的例子中我們做一個(gè)按鈕的子類(lèi)化。

            新類(lèi)

            為了子類(lèi)化一個(gè)控件,我們需要?jiǎng)?chuàng)建一個(gè)新類(lèi),并映射所有我們感興趣的消息。為了簡(jiǎn)便,我們一般都從控件標(biāo)準(zhǔn)類(lèi)中派生自己的新類(lèi),這里與按鈕控件對(duì)應(yīng)的標(biāo)準(zhǔn)類(lèi)為CButton。

            下面假定我們要實(shí)現(xiàn)的效果是,當(dāng)鼠標(biāo)懸停在按鈕上方時(shí),按鈕顯示為黃色。首先我們使用ClassWizard創(chuàng)建一個(gè)CButton的派生類(lèi),叫做CMyButton

             

            在MFC框架中從CButton派生自己的類(lèi)有許多好處,最大的好處是我們不用手工添加任何一行代碼就可以創(chuàng)建了一個(gè)擁有全部默認(rèn)功能的Windows控件。因?yàn)镸FC實(shí)現(xiàn)了所有的默認(rèn)的消息映射,因此我們可以挑選我們感興趣的消息自己處理,而不用去管其他消息。

            這里我們要為按鈕設(shè)計(jì)的功能是,鼠標(biāo)懸停時(shí)變?yōu)辄S色。

            為了檢查鼠標(biāo)是否懸停于按鈕上,我們?cè)O(shè)置一個(gè)成員變量m_bOverControl ,TRUE表示鼠標(biāo)懸停,然后設(shè)置一個(gè)周期(使用定時(shí)器)跟蹤鼠標(biāo)是否已離開(kāi)控件,這是因?yàn)椋到y(tǒng)并沒(méi)有OnMouseEnterOnMouseLeave函數(shù)供我們調(diào)用,因此我們必須使用OnMouseMove。如果,在一個(gè)時(shí)間點(diǎn)上,發(fā)現(xiàn)鼠標(biāo)已離開(kāi)按鈕,我們關(guān)閉定時(shí)器并重畫(huà)控件。

            使用ClassWizard加入WM_MOUSEMOVE和WM_TIMER的消息映射,響應(yīng)函數(shù)分別是OnMouseMoveOnTimer

             

            ClassWizard將在你的按鈕類(lèi)文件中加入下面的代碼:

            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) 建立了窗口消息與響應(yīng)函數(shù)的對(duì)應(yīng)關(guān)系。ON_WM_MOUSEMOVE把WM_MOUSEMOVE消息與OnMouseMove函數(shù)建立響應(yīng)的關(guān)系,ON_WM_TIMER m把WM_TIMER消息與OnTimer函數(shù)建立了響應(yīng)的關(guān)系。這些宏定義在MFC的源文件中,我們不需要去看,只要按照約定來(lái)做就可以了。

            假設(shè)我們已經(jīng)聲明了兩個(gè)變量m_bOverControlm_nTimerID,類(lèi)型分別是BOOL和UINT, 并且在類(lèi)的構(gòu)造函數(shù)中把它們初始化,我們的消息處理應(yīng)使用下面的代碼:

            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);
            }

            最后我們來(lái)畫(huà)出我們需要的效果,我們不再進(jìn)行消息映射,而是重載CWnd::DrawItem虛函數(shù)。只有當(dāng)控件設(shè)置owner-drawn風(fēng)格時(shí)這個(gè)函數(shù)才能被調(diào)用,并且這個(gè)函數(shù)沒(méi)有默認(rèn)的實(shí)現(xiàn)代碼,虛函數(shù)的設(shè)計(jì)只為了在派生類(lèi)中進(jìn)行實(shí)現(xiàn)。

             

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

            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);
            }
            }

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

            這個(gè)函數(shù)將在子類(lèi)化窗口時(shí)被調(diào)用,次序是在CWnd::CreateDDX_Control之后,這就是說(shuō),無(wú)論是動(dòng)態(tài)的創(chuàng)建窗口實(shí)例還是使用對(duì)話(huà)框模板創(chuàng)建,這個(gè)函數(shù)都將被調(diào)用。PreSubclassWindow在窗口子類(lèi)化創(chuàng)建后和窗口被顯示前被調(diào)用,換句話(huà)說(shuō),這是我們來(lái)做窗口初始化的一個(gè)最好時(shí)機(jī)。

            一個(gè)重點(diǎn)要注意的地方是: 如果你是用對(duì)話(huà)框資源創(chuàng)建一個(gè)控件,那么你要子類(lèi)化的控件將不會(huì)響應(yīng)WM_CREATE消息,所以我們不能在OnCreate函數(shù)中做初始化的工作,因?yàn)樗⒉皇窃谒械那闆r下都被調(diào)用。

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

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

            祝賀 - 你的Cbutton派生類(lèi)已經(jīng)完成。

            子類(lèi)化

            在創(chuàng)建時(shí)使用DDX子類(lèi)化

            在這個(gè)例子中,我們使用對(duì)話(huà)框編輯器在對(duì)話(huà)框中加入了一個(gè)新的按鈕:

             

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

            ClassWizard g會(huì)在對(duì)話(huà)框的DoDataExchange函數(shù)中創(chuàng)建一個(gè)DDX_Control調(diào)用。DDX_Control啟動(dòng)了子類(lèi)化過(guò)程,使得按鈕控件使用CMyButton類(lèi)進(jìn)行消息映射,而不是使用通常的CButton

            使用沒(méi)有在ClassWizard中注冊(cè)的類(lèi)子類(lèi)化窗口

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

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

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

            子類(lèi)化一個(gè)存在的窗口

            使用DDX固然簡(jiǎn)單,但是不能幫助我們實(shí)現(xiàn)一個(gè)已存在窗口的子類(lèi)化。比如你想在combobox中子類(lèi)化一個(gè)Edit控件,那么在你子類(lèi)化Edit控件之前,你需要先創(chuàng)建combobox控件。

            這種情況下,我們使用SubclassDlgItem或者SubclassWindow函數(shù)。這兩個(gè)函數(shù)允許你動(dòng)態(tài)的子類(lèi)化一個(gè)窗口,換句話(huà)說(shuō),把一個(gè)新的窗口實(shí)例與已經(jīng)存在的窗口建立關(guān)聯(lián)。

            比如,假設(shè)有一個(gè)對(duì)話(huà)框中包含了一個(gè)按鈕IDIDC_BUTTON1。這個(gè)按鈕已經(jīng)被創(chuàng)建,我們想用一個(gè)CMyButton的實(shí)例來(lái)與之關(guān)聯(lián),以使得按鈕符合我們需要的行為。

            為了做到這些,我們需要有一個(gè)新類(lèi)型的實(shí)例,最后的方法是在對(duì)話(huà)框或視的頭文件中加入成員函數(shù)。

            CMyButton m_btnMyButton;

            然后在對(duì)話(huà)框的OnInitDialog (或任何適當(dāng)?shù)牡胤? 中調(diào)用:

            m_btnMyButton.SubclassDlgItem(IDC_BUTTON1,this);

            假設(shè)你已經(jīng)有了一個(gè)窗口的指針,或者你工作在一個(gè)CView或其他CWnd派生類(lèi)中里面的控件被動(dòng)態(tài)的創(chuàng)建,或者你不想使用SubclassDlgItem函數(shù),那么你可以使用下面的方法:

            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());

             

            畫(huà)按鈕是非常簡(jiǎn)單的,不需要考慮按鈕的風(fēng)格(比如flat風(fēng)格),也不需要考慮適應(yīng)文字,僅僅需要考慮你畫(huà)的范圍。如果你編譯運(yùn)行提供的演示代碼,那么你將看到,當(dāng)鼠標(biāo)懸停于按鈕上方時(shí),按鈕變?yōu)辄S色。

             

            注意,實(shí)際上我們只重載了畫(huà)的函數(shù),并截取了鼠標(biāo)移動(dòng)的函數(shù)。其余的功能都還是使默認(rèn)響應(yīng)的。

            結(jié)論

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


            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            精品久久久无码中文字幕天天| 国内精品久久久久影院亚洲| 久久免费精品视频| 久久黄色视频| 色综合久久中文字幕无码| 天天综合久久久网| 人妻无码αv中文字幕久久琪琪布 人妻无码精品久久亚瑟影视 | www亚洲欲色成人久久精品| 亚洲国产成人精品久久久国产成人一区二区三区综 | 国产精品欧美久久久久无广告| 亚洲欧美日韩久久精品| 久久99热只有频精品8| 久久亚洲色一区二区三区| 97久久香蕉国产线看观看| 久久五月精品中文字幕| 99久久超碰中文字幕伊人| 久久精品国产男包| 久久久久18| 中文字幕一区二区三区久久网站| 久久人人爽人人爽人人片AV麻烦| 88久久精品无码一区二区毛片| 人妻精品久久久久中文字幕一冢本 | 久久天天躁狠狠躁夜夜躁2014| 国产无套内射久久久国产| 欧美黑人又粗又大久久久| 亚洲欧美国产日韩综合久久| 久久久久久一区国产精品| 国产福利电影一区二区三区,免费久久久久久久精 | 久久91精品国产91久久户| 久久综合香蕉国产蜜臀AV| 武侠古典久久婷婷狼人伊人| 欧美一级久久久久久久大| 精品久久久久久99人妻| 日韩一区二区久久久久久| 国产精品18久久久久久vr| 狠狠色丁香婷综合久久| 日本一区精品久久久久影院| 久久婷婷五月综合国产尤物app | 久久综合久久久| 久久久久久久亚洲精品| 伊人久久五月天|