MFC自繪控件學習總結
睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接
前言:從這學期開始就一直在學習自繪控件(mfc),目標是做出一款播放器界面,主要是為了打好基礎,因為我基礎實在是很爛....說說我自己心得體會以及自繪控件的方法吧,算是吐槽吧,說的不對和不全的地方,或者有更好的方法,請不吝賜教。
我的機器環境是:Windows7旗艦版 Service Pack 1,Visual studio 2005
1).重繪某個控件時,強烈推薦使用子類化方法,比如想自繪Button控件, 首先添加自己的類CMYButton 繼承自 CButton ,聲明一個CMYButton 對象,然后使用 SubclassDlgItem(UINT nID, CWnd* pParent ); // 第一個參數表示控件ID,第二個參數表示指向父窗口對象指針,一般用this表示(如果不想用SubclassDlgItem。那么可以使用CMYButton自身提供的Create方法 動態創建一個Button),這樣子就可以在自己類中添加重寫WindowProc()這個窗口過程函數了,非常,非常,重要 ,其他控件自繪都參考這一條.
2).我入手的第一個控件是 Button,我終于知道我的基礎有多爛,很多基本的函數如GetDlgItem() , SubclassDlgItem() 都不知道,查資料,看源碼 ,費了不少時間才基本完成Button的自繪,另外自繪
的按鈕默認情況下是不能響應鍵盤按下Enter的,需要額外做一些處理。(關鍵詞:BS_OWNERDRAW ,DrawItem),(在后期我仿造qq登陸的Button加了個效果,Hover和Leave時是漸變的,只在設置
對話框里面的Button使用了)
3).然后是 RadioButton ,CheckBox 其實和Button異曲同工的,推薦了解3個API函數CheckRadioButton(),SetCheck(),GetCheck().
4).另外對于不規則按鈕實現需要掌握 SetWindowRgn(),CombineRgn(),SelectClipRgn() 3個API函數,其他不規則窗口,控件也可以參考這個方法。
5).然后是Edit控件自繪,不算是完全自繪,只重繪了非客戶區(如果沒特殊需要也沒必要重繪客戶區),和改變背景顏色,改變字體,不過后期我加了個效果,鼠標在Edit上和離開Edit時邊框是漸變的(關
鍵詞:CtlColor,WM_NCPAINT),RichEdit也可以用這個方法
6).然后是ToolTip(氣泡提示控件),微軟提供了NM_CUSTOMDRAW這個通告消息,以WM_NOTIFY形式發送,可以用MFC類向導添加到自己的派生類中,不過我推薦重寫OnPaint函數,完全自繪(難點:
需要根據文本內容計算出控件的大小,顯示位置等)
在后期我實現了,淡入,點擊/超時 淡出的效果(需要映射TTN_POP 和TTN_SHOW兩個通告消息),不過挪開淡出效果沒能實現,求指導。
7).然后是Sliderctrl, 微軟提供了NM_CUSTOMDRAW這個通告消息,以WM_NOTIFY形式發送,可以用MFC類向導添加到自己的派生類中,前期我是用的這種方法,不過后期發現這種方法局限性很大,
推薦重寫OnPaint函數,完全自繪(關鍵點:在PreSubclassWindow 里面把 Thumb(拇指按鈕),Channel(凹槽),以及整個控件大小保存起來,以便在OnPaint里面繪制)
8).然后是Staic控件,這個比較簡單,重寫OnPaint函數 畫上文本,把DC設為透明模式就行了,有人會說直接在CtlColor直接SetBkMode(TRANSPARENT)就行了,不用在OnPaint處理,但這是有個問
題的, 如果要求文本一直變化,舊的文本沒有擦除,新設置的文本又蓋上了。所以根據這個控件的用途,自己選擇適合的方法吧。
9).然后是Menu,這個較難,嚴格來說Menu 并不算控件,他是派生自CObject類的,微軟提供了MeasureItem,DrawItem兩個虛函數類供自繪 ,MeasureItem作用是計算出菜單的高度和寬度,系統
會自動根據文本內容最長那項來作為Menu的寬度。DrawItem作用顧名思義就是畫了,但是有個致命的問題,自繪出來的Menu 有個系統默認的邊框,十分邪惡和難看,(ModifyStyle和
SetWindowLong去不掉邊界的)這時到自己派生的CMYMenu里面 發現微軟只給咱們提供了僅僅5個虛函數,沒有提供WindowProc()這個窗口過程函數,這不是坑爹嘛.......這時一般做法都是派生自
CWnd 自己實現菜單的功能.不過查了下資料任然可以自繪的:需要使用鉤子 替換菜單的窗口過程,在WM_CREATE時 去掉邊界Stytle 有興趣的朋友可以Google一下。(難點:替換菜單窗口過程)
10). 然后是Combobox控件,這個較難, 微軟提供了CompareItem,DeleteItem,DrawItem,MeasureItem 4個虛函數供自繪。我只用了后2個,(如果只加了 CBS_SORT 必須重寫CompareItem這個
函數,除非使用了CBS_HASSTRINGS | CBS_SORT就可以不重寫CompareItem()), 別以為這樣子就完了,運行后,打開Combobox 顯示的 List 有系統默認的邊框!!!ModifyStyle和SetWindowLong
去不掉邊界.老規矩查資料去,不看不知道,一看嚇一跳,Combobox 是由3個控件組合成成的(難怪叫組合框),分別是Edit,Listbox,和combo本身(除去Edit ,Listbox剩下那部分),當時我就震驚了,迷茫
了! 這時需要添加OnCtlColor這個函數,在里面 使用SubclassWindow()這個API函數子類化 ListBox 和 Edit(在這之前 你還需要準備自繪好的 ListBox控件 和Edit控件)Combobox 有3種樣式
CBS_SIMPLE, CBS_DROPDOWNLIST,CBS_DROPDOWN,第一種不說了不常用,第二種是不能輸入只能點擊選擇,第三種可以輸入可點擊選擇. 我的程序里面使用的是CBS_DROPDOWNLIST樣式
11).Combobox 在 Windows7 下疑惑:關閉滑動打開組合框特效 ,自繪Combobox是可以去掉邊界并進行窗口剪切的(圓角矩形) , 如果是 開啟滑動打開組合框特效,系統會加上邊框, 剪切的圓角又
變成直角了, 跟蹤調試發現是在WM_WINDOWPOSCHANGING 消息里面搞的鬼 ,有興趣的朋友可以對比看看,暫時為找到解決方案。。。
開啟/關閉 滑動打開組合框特效 :計算機-右鍵屬性-高級系統設置-高級-性能設置-視覺效果
12).然后是 TabCtrl ,微軟提供了DrawItem,MeasureItem 2 個虛函數供自繪,需要加 TCS_OWNERDRAWFIXED這個Stytle,表明這個控件需要自繪,不過我沒有用這種方式,我直接重寫了OnPaint函數
完全自繪(難點:需要自己計算每個標簽大小,位置,以及與之綁定的Dialog顯示位置)
13).最后是窗體框架繪制(非客戶區),這個較難,看了很多例子源碼,也花了不少時間,WM_MOVE,WM_PAINT ,WM_NCPAINT,WM_NCACTIVATE,這4個消息自繪成功的關鍵 , 在繪制時候還需要
計算出邊框/標題欄的大小和位置(Win7 和Xp 下 GetSystemMetrics()返回值是不同的)。
// 給出框架繪制不閃爍的關鍵代碼 ,完全原創。
if(message == WM_NCACTIVATE && !wParam) // wParam=0, deactive
{
return 1; // 必須返回1,處理默認消息(如果不返回1,一切彈出的窗口(模態,非模態)不能點擊)
}
if(message == WM_NCACTIVATE && wParam) // wParam =1, active
{
return 0; // 這個隨便返回(0和1都行)
}
if(message==WM_NCPAINT)
{
return 0; // 阻止默認框架繪制(An application returns zero if it processes this message 摘自MSDN)
}
這種方式是保留了邊界和標題欄,其實就是蓋住了原來的畫上自己的,只要不閃爍就是成功的。當然也可以去掉系統默認的邊界 和 標題欄,在客戶區算出一個邊界和標題欄,處理一些消息,能實現更好
的框架自繪,做出更漂亮的界面。
14)可能是我這個人比較蛋疼吧,想著既然做了個播放器界面為什么不給他實現一個播放的功能呢,微軟提供了 MCI—媒體控制接口,自己封裝了一個播放類,實現了一些播放基本功能。這樣子第一個
版本就算完成了吧。
---測試環境: 6臺Win7 和 2臺Xp
---界面測試:Win7 和 Xp 運行均正常(有個缺陷見第11條)
---播放測試: 我的電腦上可以 播放rmvb,RM ,AVI,MP4,WMV,FLV ,部分測試Win7電腦6個格式全部能播放,但部分測試的Win7電腦只能播放 AVI 和MP4,WMV格式(為什么?求解答...).
---另外WMV格式增加播放速度和減少播放速度,貌似是不行的。
---在XP系統 下能打開視頻文件但只能聽見聲音看不見圖像,求解答?
這結果確實比較蛋疼,大家可以測試下看看界面是否正常,播放功能是否正常
15)鑒于MCI版本播放測試不是很令人滿意,我又再一次蛋疼了,因為一個前輩說讓我用Activex 控件試試。好吧,花了2天時間,研究了下Activex 控件, 用OleView研究了Aplayer這個控件,
APlayer_001.dll 這個DLL文件就是Aplayer Activex控件(Activex 控件使用前必須先注冊,如果只是在代碼里面注冊了APlayer_001.dll,程序可以運行但是播放不了文件,因為在播放文件的時候
Aplayer控件 還會根據播放文件類型 再加載 一些DLL文件和AX文件,那些文件加起來有80多M,坑爹啊這是....)其實Aplaer 是迅雷看看播放器的一個組件, 如果安裝了迅雷的話,在 C:\
\Program Files\\Common Files\\Thunder Network 可以找到 Aplaer 這個文件夾,如果沒有安裝迅雷或者沒有Aplayer這個組件程序是不能運行的。
備注:這個APlayer本身有個缺陷,在播放 Rmvb和mkv文件時,點擊定位不準確。對于大的文件,很難發現這個缺陷,可以找個 一兩分鐘的短視頻文件用迅雷看看打開 點擊定位試試,缺陷非常明顯。
由于Aplayer播放過程 和MCI不太一樣,所以花了點時間做了第二個版本。(據說這個Aplaer 組件是國外一個組織開發的,貌似迅雷買了版權的)
---測試環境:6臺Win7 和 2臺Xp
---界面測試:Win7 和 Xp 運行均正常(有個缺陷見第11條)
---播放測試:Win7上可以播放RMVB,RM, AVI,MP4,WMV,FLV ,MKV, MP3, WMA, WAV
---在XP系統下 打開文件任然只能聽見聲音看不見圖像,對于XP系統這個播放問題,還未找到解決方案,求指導(難道是因為我注冊的是Win7的APlayer?)。
---另外程序有個缺陷 :在第一次 點擊Aplayer控件的時候會縮小,調試發現根本沒有進入 WM_LBUTTONDOWN,直接 WM_WINDOWPOSCHANGING,WM_WINDOWPOSCHANGED,WM_SIZE
,不知道這個消息從哪里發過來的....(我用了個不是很好的方法解決了,既然是在第一次點擊的時候才會縮小,我在PreSubclassWindow里面 :PostMessage(WM_LBUTTONDOWN,MK_LBUTTON,
(LPARAM)&UserDown);PostMessage(WM_LBUTTONUP,MK_LBUTTON,(LPARAM)&UserDown);)這2個消息,貌似沒再出現了縮小情況了,但昨天我運行的時候又出現這個問題了,出現的概率很低....
無語啊)
16)
程序的測試全部由自己完成,很多功能沒來得及測試,所以程序可能會出現這樣那樣的問題,一個人力不從心啊,希望拍磚溫柔點啊。
程序熱鍵,做的不好,不是全局和后臺的,必須窗口獲得焦點才能響應.
在后期增加了托盤功能。
增加了播放列表功能,最多支持10個文件,大于10個覆蓋第10個,雙擊列表中的文件名,就可以播放這個文件。不過播放列表我沒有單獨做個窗口是放在設置對話框第3個標簽的。
應同學的強烈要求增加了拖拽打開文件功能(我過濾了一些文件擴展名,不是每個文件類型拖拽都有效,mci版本和Activex版本支持的格式不同 ,過濾情況也不一樣)
程序最初叫IKAN Player 但是發現 PPLive 已經用了這個名字, 改成了ICAN Player....
帖子不知道怎么上傳附件,程序放在CSDN 資源區內,不需要資源分的,大家下下來看下吧。
給出鏈接: http://download.csdn.net/source/3428958
引用 53 樓 v100v100 的回復:
3).然后是 RadioButton ,CheckBox 其實和Button異曲同工的,推薦了解3個API函數CheckRadioButton(),SetCheck(),GetCheck().
/// 不明白這3個API函數和重繪有啥關系???
程序做得不錯,讓人嫉妒!但程序水平和文章內容很不匹配。
重繪RadioButton ,會用到CheckRadioButton() 因為Radio 是互斥的并且一般是成組(Group)出現的,所以還需要添加
ON_CONTROL_RANGE(BN_CLICKED,FirstID, LastID, OnBnClickedRadioGroup1) 消息宏
這樣子就可以在一個函數里處理 一組Radio的點擊,不用每個做判斷,
由于是自繪的CheckBox也是一樣的需要 SetCheck(),GetCheck().做額為的處理,當然這只是我的方法,說不定還有其他更好的方法呢,謝謝提建議
摟主你好,感謝你使用 APlayer 控件;在 XP 下無圖像是因為你設置的渲染模式是 EVR,請設置渲染模式為 Overlay 或 Renderless 即可,即在 WinXP 下調用 IPlayer2::SetParameter(1502, L"0") 即可,在 Win7 下設置為 L"1"。
另外不知道你是否調用了 ShowWindow 把 APlayer 控件窗口的子窗口,類名為 Static 的顯示出來了,它也會導致蓋住視頻而看不到圖像。
仔細檢查發現,XP 下沒有圖像的問題是主窗口(APlayer的父窗口)自繪的時候引起的,被涂成黑色,所以看不到圖像。
【LZ】謝謝你的提示,我試試看,現在我這里沒有XP系統的機器,所以做不了測試. 另外你說是主窗口重畫的時候,把Aplayer 涂黑的,這個也有可能,在win7下Aplayer是默認的具有WS_CHILD樣式,而主窗口使用了WS_CLIPCHILDREN樣式(也就是說主窗口重畫的時候,不會重畫子窗口,需要自己處理子窗口的重畫),現在就是不知道XP下Aplayer 是否具有WS_CHILD樣式.
============================================================================
正題:首先感謝大家對第一帖的支持,應一些網友烈要求下面我在添加關于上一貼的一些補充和說明(老鳥可以無視)
這一貼是實戰+理論不知道第一帖的先看第一帖:
1).補充個高級可重載函數PreSubclassWindow(),我的理解是允許用戶在子類化之前再做一額外些處理 ,這個重載函數也是非常重要的,要引起相當的注意。可以在這里改變控件的大小,位置,窗口樣式
,字體 ,等等.....你能想到的能改的,都可以在這里改.
2).關于Edit的補充說明:我最初的自繪方法是利用 WM_NCPAINT 里面處理的非客戶區只是自己畫了邊界,以實現Hover和Leave不同的邊界。不過我后來發現由于非客戶區太小了邊界也就2像素,如果
鼠標移動很快有時 系統不能檢測到鼠標當前的狀態,所以程序里面的Edit是在OnPaint里面做的繪制,不過有個核心API -Default() 下面看代碼
void CEditEx::OnPaint()
{
Default(); // 關鍵
if(!m_bHover)
DrawBoder(); // 畫自己的邊界
},這才是程序里面的自繪Edit使用的方法.
3).對于控件的Hover和Leave效果,簡單的說 Hover就是鼠標現在浮于控件上面,Leave就是鼠標離開了控件,那么這個效果要怎么實現呢?我直接給源碼吧
以Edit控件為列
頭文件中加入
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
CPP中加入:
BEGIN_MESSAGE_MAP(CEditEx, CEdit)
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
注:ON_WM_MOUSEMOVE() 可以用類向導添加,不過OnMouseLeave,和OnMouseHover是需要手動添加的
然后再CPP中定義:
void CEditEx::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bHover)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;
m_bHover= _TrackMouseEvent(&tme); // m_bHover: BOOL型成員變量
}
CEdit::OnMouseMove(nFlags, point);
}
LRESULT CEditEx::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
m_bHover = false;
//
做相應的操作
//
return 0;
}
LRESULT CEditEx::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
//
做相應的操作
//
return 0;
}
絕大多數控件可以用這種方法,不過某些控件,可能需要你在OnMouseMove里面完全模擬出 Hover和Leave的情況,比如程序里面的TabCtrl...
4).我把以前我看過的帖子整理了下供大家學習參考(由于時間久了,很多帖子都忘記了):
進度條自繪:http://www.codeproject.com/KB/miscctrl/cprogressctrlst.aspx(有項目源碼)
透明控件(多個控件)實現:http://www.codeguru.com/cpp/controls/buttonctrl/advancedbuttons/article.php/c15603/General-Solution-for-a-Transparent-Control.htm(有項目源碼)
透明窗體:http://msdn.microsoft.com/en-us/library/ms997507(Menu、窗體、Combobox 都可以參考這種方式實現任意透明度,我也是參考這種方法)
不規則按鈕實現:http://www.codeguru.com/cpp/controls/buttonctrl/non-rectangularbuttons/article.php/c2085/Universal-Button---beauty-of-HRGN.htm
自繪按鈕2篇帖子:
http://www.vckbase.com/document/viewdoc/?id=551
http://www.vckbase.com/document/viewdoc/?id=561
Custom draw 和 Owner draw 的區別(是全英文,不過要是讀懂了對你自繪的思想很有幫助,有時間幫大家翻譯下):
http://blog.csdn.net/xiexievv/article/details/6279219
WM_DRAWITEM與DrawItem()的討論,對控件自繪很有幫助:
http://blog.csdn.net/xiexievv/article/details/6259194
下面這幾篇帖子仔細閱讀定有意想不到的收獲,不只是自繪控件,完全可以讓我們對MFC的整體認識都會提升1個等級
MFC中OnDraw與OnPaint的區別:
http://blog.csdn.net/xiexievv/article/details/6271153
深度剖析消息反射機制:
http://blog.csdn.net/xiexievv/article/details/6282205
PreTranslateMessage和TranslateMessage區別:
http://blog.csdn.net/xiexievv/article/details/6299027
WindowProc和DefWindowProc的區別:
http://blog.csdn.net/xiexievv/article/details/6299016
CWnd中PreCreateWindow、PreSubclassWindow、SubclassWindow的區別:
http://blog.csdn.net/xiexievv/article/details/6233423
同時推薦幾個很好的學習網站:
http://www.codeproject.com/(英文)
http://www.codeguru.com/(英文)
http://www.pudn.com/
http://www.vckbase.com/document/index.asp
http://www.hackchina.com/
就寫這么多吧,個人覺得最有用的資料還是MSDN當然還有強大CSDN,每個控件的自繪都不是固定有規律可循的,不要硬搬亂套,要活學活用。也許你現在才開始學,完全看不懂,沒關系大家都是那么過來的啦。
引用 75 樓 weiqubo 的回復:
請問下浮動菜單是如何做的.
菜單自繪的, 利用CreatePopupMenu()創建, 利用鉤子替換菜單的窗口過程,在WM_CREATE 去掉了邊界,還處理了WM_NCPAINT,WM_WINDOWPOSCHANGING,WM_ERASEBKGND, 因為OnDrawItem 里面是一項一項的畫,所以邊界的處理以及剪切我建議都在WM_ERASEBKGND里面處理.
引用 144 樓 tajon1226 的回復:
呵呵,本來以為有源碼的。我自己最近也搞了個,說真的,挺辛苦,樓不愿意給源碼,能理解!
加油樓主,能在大三搞出這套東西,很不錯,至少哥哥我在大三的時候面對MFC兩眼發黑。至于某個面試官的講法,樓主大可不必在意。
這種狀態可以去看侯俊杰的《深入淺出MFC》了,看的時候,你會覺得很爽!
大學那些基礎課(英語,數學,數據結構,算法...)能搞扎實,再專一門計算機領域就更NB!
《深入淺出MFC》 只是翻看了幾個章節,確實很厲害的一本書,現在準備向Directx邁進...RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
我的機器環境是:Windows7旗艦版 Service Pack 1,Visual studio 2005
1).重繪某個控件時,強烈推薦使用子類化方法,比如想自繪Button控件, 首先添加自己的類CMYButton 繼承自 CButton ,聲明一個CMYButton 對象,然后使用 SubclassDlgItem(UINT nID, CWnd* pParent ); // 第一個參數表示控件ID,第二個參數表示指向父窗口對象指針,一般用this表示(如果不想用SubclassDlgItem。那么可以使用CMYButton自身提供的Create方法 動態創建一個Button),這樣子就可以在自己類中添加重寫WindowProc()這個窗口過程函數了,非常,非常,重要 ,其他控件自繪都參考這一條.
2).我入手的第一個控件是 Button,我終于知道我的基礎有多爛,很多基本的函數如GetDlgItem() , SubclassDlgItem() 都不知道,查資料,看源碼 ,費了不少時間才基本完成Button的自繪,另外自繪
的按鈕默認情況下是不能響應鍵盤按下Enter的,需要額外做一些處理。(關鍵詞:BS_OWNERDRAW ,DrawItem),(在后期我仿造qq登陸的Button加了個效果,Hover和Leave時是漸變的,只在設置
對話框里面的Button使用了)
3).然后是 RadioButton ,CheckBox 其實和Button異曲同工的,推薦了解3個API函數CheckRadioButton(),SetCheck(),GetCheck().
4).另外對于不規則按鈕實現需要掌握 SetWindowRgn(),CombineRgn(),SelectClipRgn() 3個API函數,其他不規則窗口,控件也可以參考這個方法。
5).然后是Edit控件自繪,不算是完全自繪,只重繪了非客戶區(如果沒特殊需要也沒必要重繪客戶區),和改變背景顏色,改變字體,不過后期我加了個效果,鼠標在Edit上和離開Edit時邊框是漸變的(關
鍵詞:CtlColor,WM_NCPAINT),RichEdit也可以用這個方法
6).然后是ToolTip(氣泡提示控件),微軟提供了NM_CUSTOMDRAW這個通告消息,以WM_NOTIFY形式發送,可以用MFC類向導添加到自己的派生類中,不過我推薦重寫OnPaint函數,完全自繪(難點:
需要根據文本內容計算出控件的大小,顯示位置等)
在后期我實現了,淡入,點擊/超時 淡出的效果(需要映射TTN_POP 和TTN_SHOW兩個通告消息),不過挪開淡出效果沒能實現,求指導。
7).然后是Sliderctrl, 微軟提供了NM_CUSTOMDRAW這個通告消息,以WM_NOTIFY形式發送,可以用MFC類向導添加到自己的派生類中,前期我是用的這種方法,不過后期發現這種方法局限性很大,
推薦重寫OnPaint函數,完全自繪(關鍵點:在PreSubclassWindow 里面把 Thumb(拇指按鈕),Channel(凹槽),以及整個控件大小保存起來,以便在OnPaint里面繪制)
8).然后是Staic控件,這個比較簡單,重寫OnPaint函數 畫上文本,把DC設為透明模式就行了,有人會說直接在CtlColor直接SetBkMode(TRANSPARENT)就行了,不用在OnPaint處理,但這是有個問
題的, 如果要求文本一直變化,舊的文本沒有擦除,新設置的文本又蓋上了。所以根據這個控件的用途,自己選擇適合的方法吧。
9).然后是Menu,這個較難,嚴格來說Menu 并不算控件,他是派生自CObject類的,微軟提供了MeasureItem,DrawItem兩個虛函數類供自繪 ,MeasureItem作用是計算出菜單的高度和寬度,系統
會自動根據文本內容最長那項來作為Menu的寬度。DrawItem作用顧名思義就是畫了,但是有個致命的問題,自繪出來的Menu 有個系統默認的邊框,十分邪惡和難看,(ModifyStyle和
SetWindowLong去不掉邊界的)這時到自己派生的CMYMenu里面 發現微軟只給咱們提供了僅僅5個虛函數,沒有提供WindowProc()這個窗口過程函數,這不是坑爹嘛.......這時一般做法都是派生自
CWnd 自己實現菜單的功能.不過查了下資料任然可以自繪的:需要使用鉤子 替換菜單的窗口過程,在WM_CREATE時 去掉邊界Stytle 有興趣的朋友可以Google一下。(難點:替換菜單窗口過程)
10). 然后是Combobox控件,這個較難, 微軟提供了CompareItem,DeleteItem,DrawItem,MeasureItem 4個虛函數供自繪。我只用了后2個,(如果只加了 CBS_SORT 必須重寫CompareItem這個
函數,除非使用了CBS_HASSTRINGS | CBS_SORT就可以不重寫CompareItem()), 別以為這樣子就完了,運行后,打開Combobox 顯示的 List 有系統默認的邊框!!!ModifyStyle和SetWindowLong
去不掉邊界.老規矩查資料去,不看不知道,一看嚇一跳,Combobox 是由3個控件組合成成的(難怪叫組合框),分別是Edit,Listbox,和combo本身(除去Edit ,Listbox剩下那部分),當時我就震驚了,迷茫
了! 這時需要添加OnCtlColor這個函數,在里面 使用SubclassWindow()這個API函數子類化 ListBox 和 Edit(在這之前 你還需要準備自繪好的 ListBox控件 和Edit控件)Combobox 有3種樣式
CBS_SIMPLE, CBS_DROPDOWNLIST,CBS_DROPDOWN,第一種不說了不常用,第二種是不能輸入只能點擊選擇,第三種可以輸入可點擊選擇. 我的程序里面使用的是CBS_DROPDOWNLIST樣式
11).Combobox 在 Windows7 下疑惑:關閉滑動打開組合框特效 ,自繪Combobox是可以去掉邊界并進行窗口剪切的(圓角矩形) , 如果是 開啟滑動打開組合框特效,系統會加上邊框, 剪切的圓角又
變成直角了, 跟蹤調試發現是在WM_WINDOWPOSCHANGING 消息里面搞的鬼 ,有興趣的朋友可以對比看看,暫時為找到解決方案。。。
開啟/關閉 滑動打開組合框特效 :計算機-右鍵屬性-高級系統設置-高級-性能設置-視覺效果
12).然后是 TabCtrl ,微軟提供了DrawItem,MeasureItem 2 個虛函數供自繪,需要加 TCS_OWNERDRAWFIXED這個Stytle,表明這個控件需要自繪,不過我沒有用這種方式,我直接重寫了OnPaint函數
完全自繪(難點:需要自己計算每個標簽大小,位置,以及與之綁定的Dialog顯示位置)
13).最后是窗體框架繪制(非客戶區),這個較難,看了很多例子源碼,也花了不少時間,WM_MOVE,WM_PAINT ,WM_NCPAINT,WM_NCACTIVATE,這4個消息自繪成功的關鍵 , 在繪制時候還需要
計算出邊框/標題欄的大小和位置(Win7 和Xp 下 GetSystemMetrics()返回值是不同的)。
// 給出框架繪制不閃爍的關鍵代碼 ,完全原創。
if(message == WM_NCACTIVATE && !wParam) // wParam=0, deactive
{
return 1; // 必須返回1,處理默認消息(如果不返回1,一切彈出的窗口(模態,非模態)不能點擊)
}
if(message == WM_NCACTIVATE && wParam) // wParam =1, active
{
return 0; // 這個隨便返回(0和1都行)
}
if(message==WM_NCPAINT)
{
return 0; // 阻止默認框架繪制(An application returns zero if it processes this message 摘自MSDN)
}
這種方式是保留了邊界和標題欄,其實就是蓋住了原來的畫上自己的,只要不閃爍就是成功的。當然也可以去掉系統默認的邊界 和 標題欄,在客戶區算出一個邊界和標題欄,處理一些消息,能實現更好
的框架自繪,做出更漂亮的界面。
14)可能是我這個人比較蛋疼吧,想著既然做了個播放器界面為什么不給他實現一個播放的功能呢,微軟提供了 MCI—媒體控制接口,自己封裝了一個播放類,實現了一些播放基本功能。這樣子第一個
版本就算完成了吧。
---測試環境: 6臺Win7 和 2臺Xp
---界面測試:Win7 和 Xp 運行均正常(有個缺陷見第11條)
---播放測試: 我的電腦上可以 播放rmvb,RM ,AVI,MP4,WMV,FLV ,部分測試Win7電腦6個格式全部能播放,但部分測試的Win7電腦只能播放 AVI 和MP4,WMV格式(為什么?求解答...).
---另外WMV格式增加播放速度和減少播放速度,貌似是不行的。
---在XP系統 下能打開視頻文件但只能聽見聲音看不見圖像,求解答?
這結果確實比較蛋疼,大家可以測試下看看界面是否正常,播放功能是否正常
15)鑒于MCI版本播放測試不是很令人滿意,我又再一次蛋疼了,因為一個前輩說讓我用Activex 控件試試。好吧,花了2天時間,研究了下Activex 控件, 用OleView研究了Aplayer這個控件,
APlayer_001.dll 這個DLL文件就是Aplayer Activex控件(Activex 控件使用前必須先注冊,如果只是在代碼里面注冊了APlayer_001.dll,程序可以運行但是播放不了文件,因為在播放文件的時候
Aplayer控件 還會根據播放文件類型 再加載 一些DLL文件和AX文件,那些文件加起來有80多M,坑爹啊這是....)其實Aplaer 是迅雷看看播放器的一個組件, 如果安裝了迅雷的話,在 C:\
\Program Files\\Common Files\\Thunder Network 可以找到 Aplaer 這個文件夾,如果沒有安裝迅雷或者沒有Aplayer這個組件程序是不能運行的。
備注:這個APlayer本身有個缺陷,在播放 Rmvb和mkv文件時,點擊定位不準確。對于大的文件,很難發現這個缺陷,可以找個 一兩分鐘的短視頻文件用迅雷看看打開 點擊定位試試,缺陷非常明顯。
由于Aplayer播放過程 和MCI不太一樣,所以花了點時間做了第二個版本。(據說這個Aplaer 組件是國外一個組織開發的,貌似迅雷買了版權的)
---測試環境:6臺Win7 和 2臺Xp
---界面測試:Win7 和 Xp 運行均正常(有個缺陷見第11條)
---播放測試:Win7上可以播放RMVB,RM, AVI,MP4,WMV,FLV ,MKV, MP3, WMA, WAV
---在XP系統下 打開文件任然只能聽見聲音看不見圖像,對于XP系統這個播放問題,還未找到解決方案,求指導(難道是因為我注冊的是Win7的APlayer?)。
---另外程序有個缺陷 :在第一次 點擊Aplayer控件的時候會縮小,調試發現根本沒有進入 WM_LBUTTONDOWN,直接 WM_WINDOWPOSCHANGING,WM_WINDOWPOSCHANGED,WM_SIZE
,不知道這個消息從哪里發過來的....(我用了個不是很好的方法解決了,既然是在第一次點擊的時候才會縮小,我在PreSubclassWindow里面 :PostMessage(WM_LBUTTONDOWN,MK_LBUTTON,
(LPARAM)&UserDown);PostMessage(WM_LBUTTONUP,MK_LBUTTON,(LPARAM)&UserDown);)這2個消息,貌似沒再出現了縮小情況了,但昨天我運行的時候又出現這個問題了,出現的概率很低....
無語啊)
16)
程序的測試全部由自己完成,很多功能沒來得及測試,所以程序可能會出現這樣那樣的問題,一個人力不從心啊,希望拍磚溫柔點啊。
程序熱鍵,做的不好,不是全局和后臺的,必須窗口獲得焦點才能響應.
在后期增加了托盤功能。
增加了播放列表功能,最多支持10個文件,大于10個覆蓋第10個,雙擊列表中的文件名,就可以播放這個文件。不過播放列表我沒有單獨做個窗口是放在設置對話框第3個標簽的。
應同學的強烈要求增加了拖拽打開文件功能(我過濾了一些文件擴展名,不是每個文件類型拖拽都有效,mci版本和Activex版本支持的格式不同 ,過濾情況也不一樣)
程序最初叫IKAN Player 但是發現 PPLive 已經用了這個名字, 改成了ICAN Player....
帖子不知道怎么上傳附件,程序放在CSDN 資源區內,不需要資源分的,大家下下來看下吧。
給出鏈接: http://download.csdn.net/source/3428958
引用 53 樓 v100v100 的回復:
3).然后是 RadioButton ,CheckBox 其實和Button異曲同工的,推薦了解3個API函數CheckRadioButton(),SetCheck(),GetCheck().
/// 不明白這3個API函數和重繪有啥關系???
程序做得不錯,讓人嫉妒!但程序水平和文章內容很不匹配。
重繪RadioButton ,會用到CheckRadioButton() 因為Radio 是互斥的并且一般是成組(Group)出現的,所以還需要添加
ON_CONTROL_RANGE(BN_CLICKED,FirstID, LastID, OnBnClickedRadioGroup1) 消息宏
這樣子就可以在一個函數里處理 一組Radio的點擊,不用每個做判斷,
由于是自繪的CheckBox也是一樣的需要 SetCheck(),GetCheck().做額為的處理,當然這只是我的方法,說不定還有其他更好的方法呢,謝謝提建議
摟主你好,感謝你使用 APlayer 控件;在 XP 下無圖像是因為你設置的渲染模式是 EVR,請設置渲染模式為 Overlay 或 Renderless 即可,即在 WinXP 下調用 IPlayer2::SetParameter(1502, L"0") 即可,在 Win7 下設置為 L"1"。
另外不知道你是否調用了 ShowWindow 把 APlayer 控件窗口的子窗口,類名為 Static 的顯示出來了,它也會導致蓋住視頻而看不到圖像。
仔細檢查發現,XP 下沒有圖像的問題是主窗口(APlayer的父窗口)自繪的時候引起的,被涂成黑色,所以看不到圖像。
【LZ】謝謝你的提示,我試試看,現在我這里沒有XP系統的機器,所以做不了測試. 另外你說是主窗口重畫的時候,把Aplayer 涂黑的,這個也有可能,在win7下Aplayer是默認的具有WS_CHILD樣式,而主窗口使用了WS_CLIPCHILDREN樣式(也就是說主窗口重畫的時候,不會重畫子窗口,需要自己處理子窗口的重畫),現在就是不知道XP下Aplayer 是否具有WS_CHILD樣式.
============================================================================
正題:首先感謝大家對第一帖的支持,應一些網友烈要求下面我在添加關于上一貼的一些補充和說明(老鳥可以無視)
這一貼是實戰+理論不知道第一帖的先看第一帖:
1).補充個高級可重載函數PreSubclassWindow(),我的理解是允許用戶在子類化之前再做一額外些處理 ,這個重載函數也是非常重要的,要引起相當的注意。可以在這里改變控件的大小,位置,窗口樣式
,字體 ,等等.....你能想到的能改的,都可以在這里改.
2).關于Edit的補充說明:我最初的自繪方法是利用 WM_NCPAINT 里面處理的非客戶區只是自己畫了邊界,以實現Hover和Leave不同的邊界。不過我后來發現由于非客戶區太小了邊界也就2像素,如果
鼠標移動很快有時 系統不能檢測到鼠標當前的狀態,所以程序里面的Edit是在OnPaint里面做的繪制,不過有個核心API -Default() 下面看代碼
void CEditEx::OnPaint()
{
Default(); // 關鍵
if(!m_bHover)
DrawBoder(); // 畫自己的邊界
},這才是程序里面的自繪Edit使用的方法.
3).對于控件的Hover和Leave效果,簡單的說 Hover就是鼠標現在浮于控件上面,Leave就是鼠標離開了控件,那么這個效果要怎么實現呢?我直接給源碼吧
以Edit控件為列
頭文件中加入
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
CPP中加入:
BEGIN_MESSAGE_MAP(CEditEx, CEdit)
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
注:ON_WM_MOUSEMOVE() 可以用類向導添加,不過OnMouseLeave,和OnMouseHover是需要手動添加的
然后再CPP中定義:
void CEditEx::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bHover)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;
m_bHover= _TrackMouseEvent(&tme); // m_bHover: BOOL型成員變量
}
CEdit::OnMouseMove(nFlags, point);
}
LRESULT CEditEx::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
m_bHover = false;
//
做相應的操作
//
return 0;
}
LRESULT CEditEx::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
//
做相應的操作
//
return 0;
}
絕大多數控件可以用這種方法,不過某些控件,可能需要你在OnMouseMove里面完全模擬出 Hover和Leave的情況,比如程序里面的TabCtrl...
4).我把以前我看過的帖子整理了下供大家學習參考(由于時間久了,很多帖子都忘記了):
進度條自繪:http://www.codeproject.com/KB/miscctrl/cprogressctrlst.aspx(有項目源碼)
透明控件(多個控件)實現:http://www.codeguru.com/cpp/controls/buttonctrl/advancedbuttons/article.php/c15603/General-Solution-for-a-Transparent-Control.htm(有項目源碼)
透明窗體:http://msdn.microsoft.com/en-us/library/ms997507(Menu、窗體、Combobox 都可以參考這種方式實現任意透明度,我也是參考這種方法)
不規則按鈕實現:http://www.codeguru.com/cpp/controls/buttonctrl/non-rectangularbuttons/article.php/c2085/Universal-Button---beauty-of-HRGN.htm
自繪按鈕2篇帖子:
http://www.vckbase.com/document/viewdoc/?id=551
http://www.vckbase.com/document/viewdoc/?id=561
Custom draw 和 Owner draw 的區別(是全英文,不過要是讀懂了對你自繪的思想很有幫助,有時間幫大家翻譯下):
http://blog.csdn.net/xiexievv/article/details/6279219
WM_DRAWITEM與DrawItem()的討論,對控件自繪很有幫助:
http://blog.csdn.net/xiexievv/article/details/6259194
下面這幾篇帖子仔細閱讀定有意想不到的收獲,不只是自繪控件,完全可以讓我們對MFC的整體認識都會提升1個等級
MFC中OnDraw與OnPaint的區別:
http://blog.csdn.net/xiexievv/article/details/6271153
深度剖析消息反射機制:
http://blog.csdn.net/xiexievv/article/details/6282205
PreTranslateMessage和TranslateMessage區別:
http://blog.csdn.net/xiexievv/article/details/6299027
WindowProc和DefWindowProc的區別:
http://blog.csdn.net/xiexievv/article/details/6299016
CWnd中PreCreateWindow、PreSubclassWindow、SubclassWindow的區別:
http://blog.csdn.net/xiexievv/article/details/6233423
同時推薦幾個很好的學習網站:
http://www.codeproject.com/(英文)
http://www.codeguru.com/(英文)
http://www.pudn.com/
http://www.vckbase.com/document/index.asp
http://www.hackchina.com/
就寫這么多吧,個人覺得最有用的資料還是MSDN當然還有強大CSDN,每個控件的自繪都不是固定有規律可循的,不要硬搬亂套,要活學活用。也許你現在才開始學,完全看不懂,沒關系大家都是那么過來的啦。
引用 75 樓 weiqubo 的回復:
請問下浮動菜單是如何做的.
菜單自繪的, 利用CreatePopupMenu()創建, 利用鉤子替換菜單的窗口過程,在WM_CREATE 去掉了邊界,還處理了WM_NCPAINT,WM_WINDOWPOSCHANGING,WM_ERASEBKGND, 因為OnDrawItem 里面是一項一項的畫,所以邊界的處理以及剪切我建議都在WM_ERASEBKGND里面處理.
引用 144 樓 tajon1226 的回復:
呵呵,本來以為有源碼的。我自己最近也搞了個,說真的,挺辛苦,樓不愿意給源碼,能理解!
加油樓主,能在大三搞出這套東西,很不錯,至少哥哥我在大三的時候面對MFC兩眼發黑。至于某個面試官的講法,樓主大可不必在意。
這種狀態可以去看侯俊杰的《深入淺出MFC》了,看的時候,你會覺得很爽!
大學那些基礎課(英語,數學,數據結構,算法...)能搞扎實,再專一門計算機領域就更NB!
《深入淺出MFC》 只是翻看了幾個章節,確實很厲害的一本書,現在準備向Directx邁進...RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成