• <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>

            創造你自己的控件

            介紹

            作為一個程序員有許多普通的windows控件可以用在應用程序的外觀上。許多的控件的從列表到按鈕再到進程條都是可以現成的用。盡管如此,在如此多的控件中我們還是會碰到那些標準的控件不夠用的時候。歡迎進入子分類控件的藝術。

             

            子分類一個windows控件不像子分類一個C++類。子分類一個控件意味這你用你自己的消息處理函數取代了改控件的一些或者所有的消息處理函數。你可以有效的截獲改控件的消息并使它按照你的意愿行事,而非windows的默認方式。這可以讓改控件實現大多數而非全部的你想得到的行為,并且使它表現得很完美。有兩種類型的子分類,局部子分類和全局子分類。局部子分類就是子分類一個實體,全局子分類就是將一個特定類型的控件全部子分成你的類型。

             

            記住一個從CWnd類派生的類對象和一個與它相聯的窗口(hwnd)的區別是很重要的。CWnd的派生類對象包含一個成員變量指向hwnd,而且包含那些通過hwnd作為參數的處理消息的函數(比如,WM_PAINT, WM_MOUSEMOVE)。當你子分類一個控件通過你的C++對象時,你就是將相應的hwnd連接到你的C++對象上并把改控件將激發的消息回調函數改成你的。

             

            子分類是很容易的。首先,你創建一個處理了你感興趣的所以消息的類,然后將該類來子分一個已經存在的控件使它按照你的新類的行事。某中方面上改控件已經變成了你所擁有的了。在這個例子中我們將子分一個按鈕控件并且使它做一些它從來都沒能夠做的事。

             

            一個新類

             

             

            子分一個控件我們需要創建一個新類改類應該處理了所有我們感興趣的消息。由于我們很懶,最好是使我們處理的消息最少,而且最好的方式是從你將要子分的控件派生你的新類,我們這里選擇的是CButton。

             

            我們設想的是使按鈕在鼠標每次經過時顯示出高亮的黃色。奇怪的事已經產生了。首先我們通過向導創建一個從CButton派生的新類CMyButton。

             

             

             

             

             

            通過MFC框架派生CButton類有許多優點,最大的就是我們不需要實際的為我們的類添加一行控件的代碼。假如我們愿意我們可以通過我們的新類在下一步子分一個按鈕控件,盡管有些煩瑣。這是因為MFC實現了所有缺省的消息處理函數,所以我們可以簡單的選擇一個我們感興趣的消息忽略其他的。

            However for this example we have loftier plans for our control - making it bright yellow.

            無論怎樣在這個例子中我們將使我們的控件表面高亮黃色顯示。

             

            為了檢查鼠標是否是經過了控件我們將設置一個布爾變量m_bOverControlTRUE當鼠標進入控件邊界時,并且通過定時器實時的檢查跟蹤鼠標什么時候離開了控件。不幸的是我們沒有平臺提供的OnMouseEnter OnMouseLeave函數可用,我們將通過OnMouseMove。加入我么在某個時候發現鼠標不再在控件上,我們將關閉定時器并重畫該控件。

            通過類向導添加WM_MOUSEMOVEWM_TIMER消息處理函數OnMouseMoveOnTimer 。

            類向導將會添加如下代碼到你的新button類:

            BEGIN_MESSAGE_MAP(CMyButton, CButton)

                //{{AFX_MSG_MAP(CMyButton)

                ON_WM_MOUSEMOVE()

                ON_WM_TIMER()

                //}}AFX_MSG_MAP

            END_MESSAGE_MAP()

             

            /////////////////////////////////////////////////////////////////////////////

            // CMyButton message handlers

             

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

            }

            消息映像的入口將消息映射成函數。ON_WM_MOUSEMOVE映射成函數OnMouseMove,ON_WM_TIMER映射成OnTimer。這些宏在MFC源代碼中定義,但是它們不需要閱讀。這個練習僅僅知道它們這樣處理就可以了。

            我們定義了兩個布爾變量m_bOverControlm_nTimer,一個UNIT變量,在構造函數里面初始化它們,我們的消息處理函數如下:

            IDvoid CMyButton::OnMouseMove(UINT nFlags, CPoint point)

            {

                if (!m_bOverControl)                    // Cursor has just moved over control

                {

                    TRACE0("Entering control\n");

             

                    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 control

                if (!rect.PtInRect(p))

                {

                    TRACE0("Leaving control\n");

             

                    // 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。這個方法僅僅是在自繪控件時調用,而且沒有一個缺省的實現可以讓你調用。(可以通過ASSERT'試試)這個方法被設計成僅被重載以及被派生類使用。

            通過向導添加一個DrawItem方法并且添加如下代碼:

            Collapse

            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 necessary
                if (m_bOverControl)
                    pDC->FillSolidRect(rect, RGB(255, 255, 0)); // yellow
             
                // Draw the text
                if (!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);
                }
            }

            所有的都已經做好了-進缺最后一步。DrawItem方法需要被繪制控件可以自繪。這可以通過在對話框編輯器里選中相應的選項實現-但是更好的方式是通過類自己設置樣式使得該類成為真正替代CButton的“drop-in”。為了實現這點我們需要重寫最后一個方法:PreSubclassWindow.

             

            這個方法被SubclassWindow調用,并依次被CWnd::Create 或者DDX_Control調用,這意味著加入你動態或者通過對話框模板創建一個新類的對象,PreSubclassWindow仍然會被調用。PreSubclassWindow將在你子分的控件已經產生但是還為顯示之前調用。換句話說這就是控件的一個完美的初始化時刻。

            需要重點注意的一點是:假如你的控件是通過對話框編輯器創建,那么你子分的控件將不會有WM_CREATE消息,因此我們不能通過使用OnCreate來初始化,因為它根本不會被調用。

            通過向導重載PreSubclassWindow并添加如下代碼:

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

            祝賀你-你現在已經有一個CButton的派生類了!

            子類

            通過DDX在創建的時候子分一個窗口

            在這個例子中我們要子分的控件是在對話框上放置的:

            我們讓正常的對話框創建流程創建一個帶有控件的對話框,然后通過DDX和我們的新類子分改控件。為了做到這點,我們僅僅需要通過向導添加一個控件變量使其成為對話框類的成員(在這里它的IDIDC_BUTTON1),類名為CMyButton。

            向導在你的對話框成員方法DoDataExchange中產生了一個DDX_Control調用。DDX_Control會調用SubclassWindow使得該按鈕使用CMyButton代替常規的CButton的處理方法。此按鈕已經被劫持而且會按照你的設想形式了。

             

            子分類一個窗口但是不被向導識別

            加入你添加一個窗口類到你的工程并且希望通過這個類的一個對象子分一個窗口,但是向導并不允許你的新類作為一個類型選項,這時你也許需要重新構建類向導文件了。

            先備份工程中的.clw文件,然后刪除之,接著在Visual Studio上按CTRL+W。你將會看到一個提示你哪些文件將被包含到類掃描過程中。確信你的新類文件在其中。

            現在你的新類將可以作為一個類選項了,如果不是,你仍然可以通過類向導產生一個控件(比如說CButton),然后在頭文件中手動修改其類名(比如CMyButton)。

            子分一個存在的窗口

            使用DDX很簡單,但是它不能幫助我們子分一個已經存在的控件。比如說,你想子分一個組合框中的編輯框控件。你需要在你子分編輯框控件之前已經創建了組合框(因此它的子編輯框窗口也就創建了)。

            在這種情況下你可以使用非常好用的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或者兩端對齊的文本外其他任何你想要的樣式都可以。當你編譯運行改程序時你會看見一個簡單的按鈕當你的鼠標經過它時會呈現出亮黃色。

            注意到我們僅僅真正的重載了自繪的方法,以及鼠標移動消息的處理函數,這意味這該控件仍然是下按式的按鈕。給對話框類添加一個單擊的處理函數你會發現它仍然可以被調用。

            結尾

            子分類并不難-你僅僅需要仔細選擇你需要子分的類,而且要意識到你需要處理的消息函數。仔細研究你需要子分的控件學習相關消息的處理函數和該類實現的虛成員方法。一旦你深入一個控件并且掌握了它的內部工作機制,你會發現一切都是那么容易!

            posted on 2008-03-17 21:33 弱水一瓢 閱讀(287) 評論(0)  編輯 收藏 引用 所屬分類: MFC

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導航

            統計

            文章分類

            最新評論

            久久经典免费视频| 亚洲日本va中文字幕久久| 97久久精品无码一区二区| 漂亮人妻被黑人久久精品| 9999国产精品欧美久久久久久| 久久被窝电影亚洲爽爽爽| 国产无套内射久久久国产| 7777精品伊人久久久大香线蕉| 久久久精品人妻一区二区三区蜜桃| 日韩精品久久久久久久电影蜜臀| 久久不射电影网| 久久亚洲国产成人影院| 久久精品视频网| 久久乐国产综合亚洲精品| 国内精品久久久久| 色婷婷久久久SWAG精品| MM131亚洲国产美女久久| 日韩影院久久| 国产成人香蕉久久久久| 伊人久久综合成人网| 久久99精品国产麻豆婷婷| 久久夜色精品国产网站| 久久人妻少妇嫩草AV无码蜜桃| 色婷婷综合久久久久中文一区二区 | 久久九九久精品国产| 久久精品99久久香蕉国产色戒| 久久久久国产亚洲AV麻豆| 久久精品国产亚洲网站| 亚洲国产精品高清久久久| 欧美日韩成人精品久久久免费看| 国产午夜久久影院| 久久亚洲精品无码AV红樱桃| 亚洲第一永久AV网站久久精品男人的天堂AV | 久久中文骚妇内射| 欧美日韩精品久久免费| 久久久久国产视频电影| 久久久免费观成人影院| 久久综合久久鬼色| 久久婷婷是五月综合色狠狠| 久久精品无码av| 区久久AAA片69亚洲|