天天看點

如何使用MFC編寫自定義UI界面【附高仿QQ 2014登陸界面範例程式】1. 概述2. 編寫自定義UI界面3. 小結

位址: http://blog.csdn.net/hujkay
作者:Jekkay Hu([email protected])
關鍵詞:MFC, 編寫異行窗體,自定義UI控件,VC++,異形控件,高仿QQ登陸界面, 截取QQ密碼,QQ釣魚
時間: 2014/4/12      

1. 概述

    在開發用戶端程式的時候,稍微有點正常審美觀的人都會對微軟的預設UI界面表示深惡痛絕,連最簡單的預設程式ICON都用那麼低的像素的ICON,品質差的令人發指啊。是以,但凡好一點的軟體,都會去找一些界面庫,或者幹脆自己寫個界面庫來美化一下使用者互動界面,免得使用者吐血暴斃。

    網絡上有許多界面庫,其中不少是免費的,這裡推薦一款我經常用的界面庫:Skinsharp。這個界面庫,動态編譯版本是免費的,靜态編譯版本是收費的,當然網上肯定有不少破解版本,但是為了中國軟體的前途,大家還是稍微掏點錢買正版吧,有點扯遠了,拽回來!使用第三方提供的界面庫固然能美化程式界面,但有的程式需要一些比較特殊的異形界面,那麼這些界面庫就很難滿足需求了,這樣我們就必須自己去編寫UI插件了。

2. 編寫自定義UI界面

     編寫自定義UI插件,涉及到三方面:形狀,界面繪制和事件響應。下面分别對着三個方面進行詳細闡述一下。

    2.1 形狀

        我們先随便找個軟體,比如金山毒霸吧,如下:

如何使用MFC編寫自定義UI界面【附高仿QQ 2014登陸界面範例程式】1. 概述2. 編寫自定義UI界面3. 小結

      界面是不是很酷,相比Windows自身的界面是不是感覺是天上天下之别。而我們看到這些好看的界面形狀都不是方方正正,全是不規則的形狀,是以我們第一步要做的就是設定視窗的形狀。微軟提供了一個API函數,可以設定窗體的形狀:

int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);

 其中HRGN是一個用于表示形狀的對象,我們可以用另外一個封裝好的類CRGN來進行操作。該CRGN提供了許多非常友好的接口函數,比如添加一塊區域,減少一塊區域等,具體的可以檢視一下官網的MSDN,下面列舉一些常用的操作。    

a. 建立一個矩形的區域
          CRgn	rgn;
          rgn.CreateRectRgn(0, 0, nWidth,nHeight);
    b. 在目前的區域中添加一塊的區域
        rcXor.SetRect(0, 0, 1, 2);
        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);
        rgn.CombineRgn(&rgn, &rgn_xor, RGN_OR);
    c. 在目前的區域中隻取共同區域
        rcXor.SetRect(0, 0, 1, 2);
        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);
        rgn.CombineRgn(&rgn, &rgn_xor, RGN_AND);
    d. 在目前的區域中異或一塊的區域
        rcXor.SetRect(0, 0, 1, 2);
        rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);
        rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
           

   區域建立了好了之後,就設定一下窗體的區域:      

SetWindowRgn((HRGN)rgn, TRUE);
           

下面一段完整的代碼,供大家參考一下:  

CDC* pDC = GetDC();


 CRect	rc;
 GetWindowRect(rc);
 rc.OffsetRect(-rc.left, -rc.top);


 CRgn	rgn;
 rgn.CreateRectRgn(0, 0, rc.Width(), rc.Height());
 CRgn	rgn_xor;
 CRect	rcXor;
 
 for (int y = 0; y < nSize; ++y)
 {
  rcXor.SetRect(0, y, border_offset[y], y + 1);
  rgn_xor.CreateRectRgn(0, y, border_offset[y], y + 1);
  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
  rgn_xor.DeleteObject();
 }


 for (int y = 0; y < nSize; ++y)
 {
  rcXor.SetRect(rc.right - border_offset[y], y, rc.right, y + 1);
  rgn_xor.CreateRectRgn(rc.right - border_offset[y], y, rc.right, y + 1);
  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
  rgn_xor.DeleteObject();
 }


 for (int y = 0; y < nSize; ++y)
 {
  rcXor.SetRect(0, rc.bottom - y - 1, border_offset[y], rc.bottom - y);
  rgn_xor.CreateRectRgn(0, rc.bottom - y - 1, border_offset[y], rc.bottom - y);
  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
  rgn_xor.DeleteObject();
 }


 for (int y = 0; y < nSize; ++y)
 {
  rcXor.SetRect(rc.right - border_offset[y], rc.bottom - y - 1, rc.right, rc.bottom - y);
  rgn_xor.CreateRectRgn(rc.right - border_offset[y], rc.bottom - y - 1, rc.right,rc.bottom - y);
  rgn.CombineRgn(&rgn, &rgn_xor, RGN_XOR);
  rgn_xor.DeleteObject();
 }


 SetWindowRgn((HRGN)rgn, TRUE);
 m_Rgn.DeleteObject();
 m_Rgn.Attach(rgn.Detach());
 ReleaseDC(pDC);
           

   2.2 界面繪制

        界面繪制是繁瑣的工作,推薦使用Gdiplus庫來支援一下,在stdafx.h加入如下的代碼,将其包含進來即可:

#include <gdiplus.h> 
using namespace Gdiplus; 

#pragma comment(lib, "gdiplus.lib")
           

    自定義繪制界面需要重載一下主要下面的幾個函數:

繪制客戶區域的函數:ON_WM_PAINT()
繪制非客戶區域的函數:ON_WM_NCPAINT()
背景擦除函數:ON_WM_ERASEBKGND()
限定窗體大小的函數:ON_WM_GETMINMAXINFO()
           

    一般來說,自繪窗體控件最主要的就是ON_WM_PAINT()方法,大部分時間都耗在就是在裡面,這個活比較細緻,像素一個一個地調,沒有耐心的人估計會砸電腦。

    在這裡有技巧需要提一下,如果繪制的界面插件老一閃一閃的,可以先在記憶體裡面建立一塊畫闆,在裡面畫好了之後然後在顯示到界面上,可參考如下的代碼:     

CRect	rcClient;
   GetClientRect(&rcClient);


  CPaintDC	dc(this);
  CDC MemDC;
  MemDC.CreateCompatibleDC(&dc);
  CBitmap memBmp;
  memBmp.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
  CBitmap *pOldmap = MemDC.SelectObject(&memBmp);

  DrawImageStyle(MemDC, rcClient);

  dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &MemDC, 0, 0, SRCCOPY);

  MemDC.SelectObject(pOldmap);
  MemDC.DeleteDC();
           

  2.3 事件

     事件是指根據使用者的互動動作而觸發某些動作,主要是滑鼠和鍵盤兩種。我這裡主要介紹下滑鼠事件,滑鼠事件可分為:移動,按下,彈起,移入,移出,單擊,輕按兩下和滾動。在建立自定義建立視窗的時候,千萬不要重載單擊和輕按兩下的事件,這是非常不好的習慣,而且容易引發各種問題,最好的辦法是自己在按下按鈕的時候模拟單擊或者輕按兩下的資訊,是以一般來說,重載下面幾個方法即可:

ON_WM_MOUSEMOVE()
 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
 ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
 ON_WM_LBUTTONDOWN()
 ON_WM_LBUTTONUP()
 ON_WM_LBUTTONDBLCLK()
           

     事件和窗體繪制有着緊密的關系,比如一個按鈕,使用者滑鼠移入時是一種狀态,按下時又是一種狀态,彈起式又是一狀态。是以,在繪制窗體時,要根據目前的用于的互動狀态繪制不同的形态,非常考驗一個人的變成能力。

3. 小結

    繪制自定義UI插件是個細活,必須如加工藝術品一樣有耐心,是以我但凡見到做UI插件的同仁,都心生佩服。周末閑來無事,根據其他高手的一些代碼,高仿了一下QQ的登陸界面,很酷吧,呵呵。

如何使用MFC編寫自定義UI界面【附高仿QQ 2014登陸界面範例程式】1. 概述2. 編寫自定義UI界面3. 小結

    高仿的QQ 2014登陸程式的下載下傳位址:http://download.csdn.net/user/hujkay

    别幹壞事啊~~~

胡楊, Jekkay Hu

2014/4/12