第二十八篇:SOUI中自定義控件開發過程


在SOUI中已經提供了大部分常用的控件,但是內置控件不可能滿足用戶的所有要求,因此一個真實的應用少不得還要做一些自定義控件。

學習一個新東西,最簡單的辦法就是依葫蘆畫瓢。事實上在SOUI系統中內置控件和自定義控件的開發流程是完全一樣的,因此只需要打開SOUI的源代碼,隨便找一個控件看一下就大體差不多了。

下面我以controls.extend目錄下的的SRadioBox2控件為例對控件開發過程需要注意的地方做一點說明。

要開發一個控件,首先要確定的是應該從哪個控件來繼承。選擇一個合適的基類是正確開發自定義控件的前提。

之所以要開發一個SRadioBox2控件,我需要解決的問題很簡單:SRadioBox控件總是在左邊顯示一個圓圈,這個圓圈有時候不是我想要的。

因此我需要做的就是繼承SRadioBox控件的行為,重寫WM_PAINT的處理。

因此就有了下面的代碼:

 class SRadioBox2 : public SRadioBox
    {
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);
    }

需要注意的是,所有SOUI控件都是在namespace SOUI中,因此自定義控件也最好是放在SOUI這個namespace里。

有了上面的骨架,下面來逐步添加內容。

首先我們需要給自定義控件定義一個在XML中可以識別的標簽。

只需要在類的最開始增加一行:

 class SRadioBox2 : public SRadioBox
    {
    SOUI_CLASS_NAME(SRadioBox2,L"radio2")
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);
    }

SOUI_CLASS_NAME告訴XML解析器,碰到radio2時自動創建SRadioBox2對象。

實際上這一行更重要的作用是用來做對象類型運行時識別(RTTI),有了這個機制,在編譯器關閉C++的RTTI時仍然可以安全的進行類型轉換。

然后我們需要處理控件的WM_PAINT消息。

為了處理這個消息,我們需要加入消息映射表及消息處理函數:

class SRadioBox2 : public SRadioBox
    {
    SOUI_CLASS_NAME(SRadioBox2,L"radio2")
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);
        

    protected:       
        void OnPaint(IRenderTarget *pRT); SOUI_MSG_MAP_BEGIN() MSG_WM_PAINT_EX(OnPaint) SOUI_MSG_MAP_END()
    };

SOUI控件的消息處理機制是和WTL中抄過來的,和MFC也很相似。只是對於部分消息,由於對於消息的參數的解釋不一樣,消息映射的宏會有一點變化。

如這里的WM_PAINT消息,在SOUI里wparam傳遞的是一個IRenderTarget指針,而傳統的Win32傳遞的是一個HDC。因此我們需要使用MSG_WM_PAINT_EX代替WTL中使用的MSG_WM_PAINT。

大家可能會問有哪些消息映射宏SOUI和WLT不一樣?

實際上對於一個有經驗的程序員,他應該可以找到MSG_WM_PAINT_EX的宏定義,並且在定義附近就可以找到所有的SOUI和WLT不同的映射宏。

下面是到目前為止SOUI中所有和WTL不同的宏:

// BOOL OnEraseBkgnd(IRenderTarget * pRT)
#define MSG_WM_ERASEBKGND_EX(func) \
    if (uMsg == WM_ERASEBKGND) \
    { \
    SetMsgHandled(TRUE); \
    lResult = (LRESULT)func((IRenderTarget *)wParam); \
    if(IsMsgHandled()) \
    return TRUE; \
    }

// void OnPaint(IRenderTarget * pRT)
#define MSG_WM_PAINT_EX(func) \
    if (uMsg == WM_PAINT) \
    { \
    SetMsgHandled(TRUE); \
    func((IRenderTarget *)wParam); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
    }

// void OnNcPaint(IRenderTarget * pRT)
#define MSG_WM_NCPAINT_EX(func) \
    if (uMsg == WM_NCPAINT) \
{ \
    SetMsgHandled(TRUE); \
    func((IRenderTarget *)wParam); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

// void OnSetFont(IFont *pFont, BOOL bRedraw)
#define MSG_WM_SETFONT_EX(func) \
    if (uMsg == WM_SETFONT) \
    { \
    SetMsgHandled(TRUE); \
    func((IFont*)wParam, (BOOL)LOWORD(lParam)); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
    }

// void OnSetFocus()
#define MSG_WM_SETFOCUS_EX(func) \
    if (uMsg == WM_SETFOCUS) \
{ \
    SetMsgHandled(TRUE); \
    func(); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

// void OnKillFocus()
#define MSG_WM_KILLFOCUS_EX(func) \
    if (uMsg == WM_KILLFOCUS) \
{ \
    SetMsgHandled(TRUE); \
    func(); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

// void OnNcMouseHover(int nFlag,CPoint pt)
#define MSG_WM_NCMOUSEHOVER(func) \
    if(uMsg==WM_NCMOUSEHOVER)\
{\
    SetMsgHandled(TRUE); \
    func(wParam,CPoint(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam))); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

// void OnNcMouseLeave()
#define MSG_WM_NCMOUSELEAVE(func) \
    if(uMsg==WM_NCMOUSELEAVE)\
{\
    SetMsgHandled(TRUE); \
    func(); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}


// void OnTimer(char cTimerID)
#define MSG_WM_TIMER_EX(func) \
    if (uMsg == WM_TIMER) \
{ \
    SetMsgHandled(TRUE); \
    func((char)wParam); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

#define WM_TIMER2    (WM_USER+5432)    //定義一個與HWND定時器兼容的SOUI定時器

#define MSG_WM_TIMER2(func) \
    if (uMsg == WM_TIMER2) \
{ \
    SetMsgHandled(TRUE); \
    func(wParam); \
    lResult = 0; \
    if(IsMsgHandled()) \
    return TRUE; \
}

通常情況下,自定義控件還需要處理一些自定義的屬性,這時我需要還需要增加一個屬性映射表(如SGifPlayer):

        SOUI_ATTRS_BEGIN()        
            ATTR_CUSTOM(L"skin", OnAttrSkin) //為控件提供一個skin屬性,用來接收SSkinObj對象的name
        SOUI_ATTRS_END()

完成上面幾步,一個自定義控件基本上就完成了。

可能還需要實現幾個修改基類行為的虛函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM