<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    MFC自繪控件學習總結

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注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中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全