Chinaunix首页 | 论坛 | 博客
  • 博客访问: 589015
  • 博文数量: 752
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5005
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(752)

文章存档

2011年(1)

2008年(751)

我的朋友

分类:

2008-10-13 16:47:00

为你的程序换个皮肤


作者/
 



(C)CopyRight, 2003-2004, IRS, All Rights Reserved


  目前这方面的小软件很多,我一直就想做这么一个东东,但是一直苦于时间有限,一直都没有做。最近一段时间,我发现这些方面的东西越来越多,而且都没有源代码,一些家伙在网站上给出这样那样的示例,其实都是在为自己的产品做广告,实在有违开源的思想。
  最近终于有了一段假期,反正没什么事做就来试试,经过一段时间的学习和摸索,终于实现了一段简单的程序。现在我就给出一个简单的例子和解释,让大家明白这是一个怎么回事,教你如何利用这个技术给一个按钮换肤?
  以前我们一直利用重载一个类的办法来实现丰富多彩的个性化控件,如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();	
  这样就实现了对按钮的换肤,是不是很简单啊,我只是做了一个简单的实现,还有很多工作需要大家一起来做,希望有兴趣的朋友一起来做,有什么问题请到 上发问,同时也希望大家把你们的作品公布出来,SkinDemo的开发网站就在,发布你的后继开发,贴出你的心得。
--------------------next---------------------

很赞,感谢 ( boyofbit 发表于 2007-9-14 13:58:00)
 
very good, thank you. ( kongwe 发表于 2007-4-23 11:39:00)
 

有个六色换皮肤DLL(46.5KB)
VC,VB,WIN32,
调用很方便!
/////////////

有个六色换皮肤DLL(46.5KB)
VC,VB,WIN32,
调用很方便! ( konny 发表于 2006-9-18 1:27:00)
 
AppFace控件好象不是开源的,我记得VCKBASE里面有一个开源的换夫代码,可惜仅仅绘的很差劲,不过可以研究一下,现在打广告的太多,很另人讨厌... ( zhaiht 发表于 2006-9-12 18:38:00)
 
maplehook这厮是谁啊?总他妈的在给appface打广告,你那个好,硬是好,好得不得了了,屎吃多了吧? ( exyeuer 发表于 2006-8-27 4:54:00)
 
画个按钮能和这比吗?这个可是把程序中所有的按钮都换了(有些人不识货了)..
好东西..如果能实现其它控件的换肤就更好了... ( hxzb11 发表于 2006-5-5 13:03:00)
 
我都被你害俩回了,每回都是因为要找换肤的程序把你这个鸟程序下下来了,结果一看“被骗了”就一个按钮也叫skin???按钮谁不会画啊。 ( chrys 发表于 2006-3-13 21:50:00)
 
(菜鸟)请教:换其他skin该改哪些地方?对话框的形状怎样才能随意变换?谢谢 ( xiami_1013 发表于 2005-4-2 22:37:00)
 

这个网站是个什么东西啊?
哪里有什么评论阿? ( zhliu 发表于 2005-3-9 20:03:00)
 
不错啊
谢谢! ( erwin1984 发表于 2004-11-12 18:47:00)
 
.......................................................

--------------------next---------------------

阅读(222) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~