翻譯|其它|編輯:郝浩|2008-01-21 09:34:51.000|閱讀 1385 次
概述:
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
首先說明。既然是原創,很多東西就是自己想出來了。雖然參考過一些網站和書籍,但不能保證全部正確,如果哪位讀了在下的文章之后能夠指出其中的問題,在下感激不盡。特設金幣500報答將幫助我找出錯誤的網友。對我這個窮人已經是很大的數目了。
首先說為什么使用控件,可能這是一句廢話。只要你用MFC開一個窗口幾乎就一定用到控件,它幫助你顯示信息,處理數據,傳遞消息。
平常大家見的到的控件無非是以下三類:1是VC自帶的。VC的工具欄里包含了大部份,如果嫌不夠,打開MSDN看CObject的Hierarchy Chart。CWnd下面的Control類就是你用的上的全部了。還不夠用或者這些控件功能過于簡單就得尋找其它途經了。這些控件用我的感覺來說就是作的很好,有的功能很少,是因為不想多作,以限制了適用者將來擴展功能的機會。同時十分結實。至少知道的問題以及解決方法四處都能查的到。是作一般界面的首選。
十分可惜首選能選的東西不多。很多功能不夠強大。第二類方法就是在網上找別人的自制控件。這類控件很多都是2000年左右寫出來的,經過很多人測試的。同時大部份控件都是基于一個已有的MFC控件改編而來。代碼不很復雜。是比較可靠同時比較容易維護的。
第三類就是其它軟件公司出品的要錢的控件。很多是以ActiveX或者library的形式銷售的。如果是ActiveX的話可能還是別的語言寫的。公開代碼的不多。同時這些公司規模都不很大(至少和微軟比),也就是說測試的深度不一定夠,所以如果抱著全部信任去用而到要交活前一瞬間這些控件忽然發現它沒聽說的那么好用了不要驚訝。不是說這些控件不好,而是用的時候要小心,先檢驗其可靠性。
在以上三類控件的基礎上你可以根據自己的需要增加控件的功能以滿足自己的特殊需要。這就是本文要寫的自制控件了。當你看網上別人寫的控件的時候一般會有一些說明為什么這么設計。通過這些設計思想你可以了解自己怎么下手。下面簡要的說兩個小例子:
1。CListCtrl幾乎是用的最廣泛的控件了,但它提供的基本功能很少。如果我希望我的列表在用戶每次點擊一列的頭的時候自動按這列排序,或者右擊列表時彈出一個菜單。我可以作一個自制的控件,是CListCtrl控件derive出來的。在這個控件中管理LVN_COLUMNCLICK和ON_WM_CONTEXTMENU來實現這兩個功能。這幾乎是最簡單的自制控件了。
2。稍微復雜一點的。Tootip。MFC提供tootip,當你的鼠標從一個控件移到另一個控件,并且停留500毫秒的以后。如果實現這個控件已經被加了tooltip那么一個小tip的黃色窗口就會跳出來顯示一下tip的一行字,你的鼠標再移動一下它就沒了。這個功能好像有點太簡單了。首先我希望我的tip不只一行,同時即使同一個控件,用戶把鼠標放在不同的地方我也希望有不同的tip。例如我的控件是一張地圖,鼠標放在北京上tip就顯示“政治中心”,放在上海上就顯示“經濟中心”。這個tip怎么作呢?首先window本身的tool tip類只能顯示一行,這個沒法解決,所以自制的控件不該用CToolTipCtrl作base class。可以選擇直接用CWnd。在當前的Dialog的PreTranslateMessage中加上判斷:如果當前的message是鼠標的動作,就叫這個控件處理。好控件接到這個消息。進一步判斷:如果是鼠標移動的消息,說明已經不在原來的位置上了,hide這個小CWnd,記下當前鼠標器的位置,然后SetTime(500)。當然如果此前已經有Timer了需要把那個timer殺掉。這個message就處理完了。以后有兩種可能性:1。沒到500毫秒,鼠標器又移動了,這個函數就又被叫了一次,前面的信息被沖掉,等待新的信息。2。500毫秒內鼠標沒有移動,于是show當前的CWnd,有什么text都填進去,想寫幾行就幾行。這就是這個多行動態的ToolTip的原理了。以后可以發上來給大家看看。
以上就是自制MFC控件的基本思想。如果你一上來對自制控件還沒有什么概念,不妨下載幾個別人寫好的先讀一讀,同時要對MFC基本控件有一定的了解。
再說說哪些功能應該作到自制控件中。VC不把所有功能都作進控件就是不希望一些太特殊的行為限制了控件應用的廣泛性。自制控件的時候也要切忌這一點。如果你在工作中寫了這么一個控件,最好你們單位的所有人都用這個控件,以保持一個產品的風格統一。例如前面所說的ListCtrl的排序和右擊菜單,我個人認為就十分不適合放到自制控件中,也就是說這是個很壞的例子。為什么呢?如果一個List不希望一點列頭就排序,或者希望右擊時跳出不同的菜單。用這個控件就沒辦法disable自己的功能了。MFC消息傳遞的機制是這樣的。如果你在控件中什么都不加,讓他的parent,也就是Dialog或者FormView之類的替它處理,那么這個信息會傳給Dialog或者FormView。有他們的LVN_COLUMNCLICK和ON_WM_CONTEXTMENU來找到對應的控件處理。如果控件自己處理了這些信息,那么它們的parent就接不到這個信息了。也就沒有辦法輕易的改回來,讓已經處理的結果復原了。總而言之,處理的信息應該是普遍適用于各種情況的。如果只是個特殊情況的話,這個信息最好由控件的parent來處理。
最后談一下控件和其parent的信息交換。這幾乎是作復雜一點控件時免不了的。控件完成自己當前要處理阿信息之后如何讓其父親窗口處理剩下它該處理的部份呢?
方法1是用CALLBACK函數。書上說這是比較建議的方法,因為這比起后面講的Post message而言傳遞的參數都有類型,也就是說信息更準確。CALLBACK function在MFC中用的很多,可以隨便找個差不多的函數參考一下其格式,例如SetTimer()。
方法2就是post message,最好不要用send message,如果真的需要發出的信息立刻就受到結果也有別的解決方法。post message 的劣勢就是傳遞的參數沒有類型,需要在WPARAM wParam, LPARAM lParam中自行提取信息。不過我倒覺得post message沒那么差,或者說因為整個windows都是這么運作的,你的控件不post messageVC其它的地方post message 的地方多的是。這一個地方提高了可靠性沒什么意義。干脆都post,用的時候小心點就是了。 post message的好處是程序寫的很干凈。控件把消息post 出去了就完成任務了。寫parent class的人愛接不接,不需要處理的時候可以不管這個message。沒必要一定預備這一個接受函數等著處理。這是我比較喜歡的方法。
方法3比較土,干脆再建立一個class 里面寫一堆空函數,讓parent class來從它這里繼承。
例如我的控件叫CMyList,需要管理列表的scroll。處理完之后通知其parent我處理完了。
那么就再建立一個class 叫 CMyListHolder. 里面就一個空函數:
class CMyListHolder
{
public:
virtual void ListScrollDone(){};
}
我用到這個list的時候我的Dialog就從這個CMyListHolder繼承
class CMyDialog : public CDialog, public CMyListHolder。
在CMyList中處理完OnScroll的信息后去call m_pParentWnd->ListScrollDone()。
在CMyDialog中如果愿意處理點什么就把自己的這個override函數填上就是了。這方法整體而言不是很干凈,但變量還算有類型。是個屆于方法1與方法2之間的方法。
說了這么多,還沒有實例,過一陣貼一些控件的實例具體說明一下。
歡迎大家多多指教。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:編程中國