程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 為你的程序換個皮膚

為你的程序換個皮膚

編輯:關於VC++

目前這方面的小軟件很多,我一直就想做這麼一個東東,但是一直苦於時間有限,一直都沒有做。最近一段時間,我發現這些方面的東西越來越多,而且都沒有源代碼,一些家伙在網站上給出這樣那樣的示例,其實都是在為自己的產品做廣告,實在有違開源的思想。

最近終於有了一段假期,反正沒什麼事做就來試試,經過一段時間的學習和摸索,終於實現了一段簡單的程序。現在我就給出一個簡單的例子和解釋,讓大家明白這是一個怎麼回事,教你如何利用這個技術給一個按鈕換膚?

以前我們一直利用重載一個類的辦法來實現豐富多彩的個性化控件,如GuiToolkit、CJ60LIB,都是這樣的工具,使用起來還是要在程序中插入大量的語句,這樣做一方面增加了程序的復雜性,另一方面也增加了程序高度的難度。當然現在也有像SkinMagic、EasySkin這樣的工具,只需要在你的程序裡增加兩行代碼就可以實現對常用控件的換膚,但是這些工具都沒有源代碼,對於想學習開發的人來說實在沒什麼幫助。為了讓大家都了解這項技術,我決定開發一個這樣的程序,並公布源程序,希望有興趣的朋友都來看看,動手做做,同時歡迎大家公開你的源程序,和大家一起分享你的成功和快樂。

首先,來給一個程序換膚,我們必須得到程序的句柄,然後給程序掛鉤。下面的一段代碼就實現了掛鉤功能。

BOOL IRStartup( HINSTANCE hModule, DWORD dwThreadID )
{
  globalWndHookEx = SetWindowsHookEx(
    WH_CALLWNDPROC, (HOOKPROC) IRCallWndProc, hModule, dwThreadID );
  return TRUE;
}

這也是像SkinMagic一類工具的初始化函數。當然在退出時也要釋放鉤子的。

BOOL IRComplete( void )
{
  UnhookWindowsHookEx( globalWndHookEx );
  return TRUE;
}

接下來,就是IRCallWndProc這個回調函數的編寫,這是至關重要的一個環節,這個函數就是對所要換膚的類對象進行了監視,並改變其消息處理函數,實現換膚的目的。

LRESULT CALLBACK IRCallWndProc( int nCode, WPARAM wParam, LPARAM lParam )
{
  PCWPSTRUCT  pcs = (PCWPSTRUCT) lParam;
  HWND    hWnd = pcs->hwnd;
  if( hWnd ) {
    char    sClassName[201] = "\0";
    GetClassName( hWnd, sClassName, 200 );
    if( strcmp( sClassName, "Button" ) == 0 ) {
      CWnd  *pWnd = CWnd::FromHandle( hWnd );
      DWORD  dwStyle = pWnd->GetStyle();
      if( dwStyle == 0x50010000 ) {
        WNDPROC  WndProc;
        WndProc = (WNDPROC) GetWindowLong( hWnd, GWL_WNDPROC );
        if( CButtonExt::m_cWndProc != NULL && \
          WndProc != CButtonExt::m_cWndProc )
        {
          return CallNextHookEx( globalWndHookEx,
                      nCode,
                      wParam,
                      lParam );
        }
        if( WndProc != (WNDPROC) CButtonExt::DefWindowProc ) {
          WndProc = (WNDPROC) SetWindowLong(
               hWnd,
               GWL_WNDPROC,
               (LONG) CButtonExt::DefWindowProc );
          CButtonExt::m_cWndProc = WndProc;
        }
      }
    }
  }
  return CallNextHookEx( globalWndHookEx, nCode, wParam, lParam );
}

這樣就對按鈕的消息進行了掛鉤處理了,就可以重新來繪制按鈕了。緊接著就是給出按鈕控件的繪制方法,我是用一個類來實現的,都是使用的靜態函數直接調用的。

#define STATUS_BUTTON_NORMAL  0x00000000
#define STATUS_BUTTON_HOVER    0x00000001
#define STATUS_BUTTON_DOWN    0x00000002
class CButtonExt
{
public:
  CButtonExt() {}
  ~CButtonExt() {}
  static UINT    m_nStatus;
  static WNDPROC  m_cWndProc;
  static LRESULT DefWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
  {
    CWnd  *pWnd = NULL;
    CPoint  point;
    pWnd = CWnd::FromHandle( hWnd );
    switch( message )
    {
    case WM_PAINT:
      return OnPaint( pWnd );
      break;
    case WM_LBUTTONDOWN:
      point.x = LOWORD(lParam);
      point.y = HIWORD(lParam);
      return OnLButtonDown( pWnd, 0, point );
      break;
    case WM_LBUTTONUP:
      point.x = LOWORD(lParam);
      point.y = HIWORD(lParam);
      return OnLButtonUp( pWnd, 0, point );
      break;
    case WM_LBUTTONDBLCLK:
      point.x = LOWORD(lParam);
      point.y = HIWORD(lParam);
      return OnLButtonDblClk( pWnd, 0, point );
      break;
    case WM_MOUSEMOVE:
      point.x = LOWORD(lParam);
      point.y = HIWORD(lParam);
      return OnMouseMove( pWnd, 0, point );
      break;
    default:
      break;
    }
    return CallWindowProc( m_cWndProc, hWnd, message, wParam, lParam );
  }
  static LRESULT OnLButtonDown( CWnd *pWnd, UINT nFlags, CPoint point ) {
    m_nStatus = STATUS_BUTTON_DOWN;
    pWnd->Invalidate();
    pWnd->UpdateWindow();
    return TRUE;
  }
  static LRESULT OnLButtonUp( CWnd *pWnd, UINT nFlags, CPoint point ) {
    if( m_nStatus != STATUS_BUTTON_NORMAL ) {
      m_nStatus = STATUS_BUTTON_NORMAL;
      pWnd->Invalidate();
      pWnd->UpdateWindow();
      SendMessage( pWnd->GetParent()->m_hWnd,
             WM_COMMAND,
             pWnd->GetDlgCtrlID(),
             (LPARAM) (pWnd->m_hWnd) );
    }
    return TRUE;
  }
  static LRESULT OnLButtonDblClk( CWnd *pWnd, UINT nFlags, CPoint point ) {
    return TRUE;
  }
  static LRESULT OnMouseMove( CWnd *pWnd, UINT nFlags, CPoint point ) {
    HRGN hRgn = CreateRectRgn( 0, 0, 0, 0 );
    pWnd->GetWindowRgn( hRgn );
    BOOL bIn = PtInRegion( hRgn, point.x, point.y );
    if( bIn ) {
      if( m_nStatus == STATUS_BUTTON_DOWN ) return TRUE;
      if( m_nStatus != STATUS_BUTTON_HOVER ) {
        m_nStatus = STATUS_BUTTON_HOVER;
        pWnd->Invalidate();
        pWnd->UpdateWindow();
        pWnd->SetCapture();
      }
    } else {
      if ( m_nStatus == STATUS_BUTTON_HOVER ) {
        m_nStatus = STATUS_BUTTON_NORMAL;
        pWnd->Invalidate();
        pWnd->UpdateWindow();
        ReleaseCapture();
      }
    }
    DeleteObject( hRgn );
    return TRUE;
  }
  static LRESULT OnPaint( CWnd *pWnd ) {
    CPaintDC  dc(pWnd);
    CString    cs;
    RECT    rc;
    CFont    Font;
    CFont    *pOldFont;
    CBrush    Brush;
    CBrush    *pOldBrush;
    CPen    Pen;
    CPen    *pOldPen;
    POINT    pt;
    pt.x = 2;
    pt.y = 2;
    dc.SetBkMode( TRANSPARENT );
    Font.CreateFont( 12, 0, 0, 0, FW_HEAVY, 0, 0, 0, ANSI_CHARSET, \
        OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
        VARIABLE_PITCH | FF_SWISS, "MS Sans Serif" );
    pOldFont = dc.SelectObject( &Font );
    if( m_nStatus == STATUS_BUTTON_DOWN ) {
      Brush.CreateSolidBrush( RGB( 160, 160, 160 ) );
      Pen.CreatePen( PS_SOLID, 1, RGB( 100, 100, 100 ) );
      dc.SetTextColor( RGB( 50, 50, 250 ) );
    } else if( m_nStatus == STATUS_BUTTON_HOVER ) {
      Brush.CreateSolidBrush( RGB( 60, 60, 180 ) );
      Pen.CreatePen( PS_SOLID, 1, RGB( 0, 0, 0 ) );
      dc.SetTextColor( RGB( 250, 250, 50 ) );
    } else if( m_nStatus == STATUS_BUTTON_NORMAL ) {
      Brush.CreateSolidBrush( RGB( 240, 240, 240 ) );
      Pen.CreatePen( PS_SOLID, 1, RGB( 120, 120, 120 ) );
      dc.SetTextColor( RGB( 50, 50, 50 ) );
    }
    pOldBrush = dc.SelectObject( &Brush );
    pOldPen = dc.SelectObject( &Pen );
    pWnd->GetClientRect( &rc );
    dc.RoundRect( &rc, pt );
    HRGN hRgn = CreateRectRgn( rc.left, rc.top, rc.right, rc.bottom );
    pWnd->SetWindowRgn( hRgn, TRUE );
    DeleteObject( hRgn );
    pWnd->GetWindowText( cs );
    dc.DrawText( cs, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
    dc.SelectObject( pOldFont );
    dc.SelectObject( pOldBrush );
    dc.SelectObject( pOldPen );
    return TRUE;
  }
  static LRESULT OnEraseBkgnd( CWnd *pWnd, CDC *pDC ) {
    return TRUE;
  }
};
UINT  CButtonExt::m_nStatus = STATUS_BUTTON_NORMAL;
WNDPROC  CButtonExt::m_cWndProc = NULL

程序結構還是很清楚的,這時做一些簡單的說明。m_nStatus用來標志按鈕的狀態,m_cWndProc用來保存系統的消息處理函數地址。其他就不用說了吧。 最後,就是如何在程序中使用的問題了。調用方法其實很簡單,在CSkinApp類的InitInstance()函數中加入這樣的一句話:

IRStartup( GetModuleHandle( NULL ), GetCurrentThreadId() );

在CSkinApp類的ExitInstance()中加入這樣的一句話:

IRComplete();

這樣就實現了對按鈕的換膚,是不是很簡單啊,我只是做了一個簡單的實現,還有很多工作需要大家一起來做,希望有興趣的朋友一起來做

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved