相信已经有很多人用过QQ2009了,自然也对其中的UI设计很感兴趣,觉得设计很是完美。但是这是怎么设计的呢?下面我介绍一个用来实现这种效果的设计库——DSkinLite(源于CodeProject)。下面通过一个演示程序来介绍使用过程:
程序中,我主要对主窗口和两个通用对话框(打开对话框和浏览对话框)进行了换肤,这里我所有的窗口都使用了同一个皮肤(定义在XML文件中),通常应该为每个对话框都定义自己的皮肤。下面我简要描述一下使用过程:
1、 将头文件和库文件放在你的工程目录下:
(1) 头文件路径
…\FileCutterTool\include,其中包含DskinDef.h和DSkinDLL.h两个文件
(2) 库文件路径
…\FileCutterTool\lib,其中dskinlite.lib为多字节编码,dskinliteu.lib为Unicode编码
(3) 在工程的属性中添加附加依赖项路径,保证.lib库导入到工程中。
2、 在stdafx.h中导入库文件:
#ifdef DSKINDLL_EXPORTS
#define DSKINDLL_API extern "C" __declspec(dllexport)
#else
#define DSKINDLL_API extern "C" __declspec(dllimport)
#endif
#include "..\include\dskindll.h"
#include "..\include\dskindef.h"
#include <afxdlgs.h>
3、 在FileCutterTool.cpp文件中添加初始化库和结束库的代码:
(1) 在InitInstance()中添加初始化库代码
//Load the skin theme
dsLoadSkin( _T("SkinQQ"));
(2) 在Existance()中添加结束库的代码(也可放在InitInstance()的最后)
dsExitSkin();
4、 在XML文件中定义皮肤(略)
5、 在对话框的OnInitDialog()中添加换肤的代码
dsSkinWindow( GetSafeHwnd(), SKIN_TYPE_DIALOG, _T("mfctestdialog"), FALSE);
dsSkinWindow( GetDlgItem( IDC_SOURCEBROWSER)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("button"), FALSE);
dsSkinWindow( GetDlgItem( IDC_DESTBROWSER)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("button"), FALSE);
dsSkinWindow( GetDlgItem( IDC_START)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("button"), FALSE);
dsSkinWindow( GetDlgItem( IDC_STOP)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("button"), FALSE);
dsSkinWindow( GetDlgItem( IDCANCEL)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("button"), FALSE);
dsSkinWindow( GetDlgItem( IDC_STATIC_1)->GetSafeHwnd(), SKIN_TYPE_STATIC, NULL, FALSE);
dsSkinWindow( GetDlgItem( IDC_STATIC_2)->GetSafeHwnd(), SKIN_TYPE_STATIC, NULL, FALSE);
dsSkinWindow( GetDlgItem( IDC_EDITSOURCE)->GetSafeHwnd(), SKIN_TYPE_EDIT, _T("edit"), FALSE);
dsSkinWindow( GetDlgItem( IDC_EDITDEST)->GetSafeHwnd(), SKIN_TYPE_EDIT, _T("edit"), FALSE);
dsSkinWindow( GetDlgItem( IDC_STATUSTEXT)->GetSafeHwnd(), SKIN_TYPE_EDIT, _T("edit"), FALSE);
dsSkinWindow( GetDlgItem( IDC_SELECTSPLIT)->GetSafeHwnd(), SKIN_TYPE_RADIOBUTTON, _T("radiobutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_SELECTMERGE)->GetSafeHwnd(), SKIN_TYPE_RADIOBUTTON, _T("radiobutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_COMBO1)->GetSafeHwnd(), SKIN_TYPE_COMBOBOX, _T("combobox"), FALSE);
dsSkinWindow( GetDlgItem( IDC_STATIC)->GetSafeHwnd(), SKIN_TYPE_GROUPBOX, _T("groupbox"), FALSE);
dsSkinWindow( GetDlgItem( IDC_PROGRESS1)->GetSafeHwnd(), SKIN_TYPE_PROGRESS, _T("progress"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON1)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("mainmenubutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON2)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("messagebutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON3)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("smsbutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON4)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("appcenterbutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON5)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("petbutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON6)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("gamebutton"), FALSE);
dsSkinWindow( GetDlgItem( IDC_BUTTON7)->GetSafeHwnd(), SKIN_TYPE_BUTTON, _T("chatbutton"), FALSE);
6、 为打开对话框添加换肤代码
要为打开对话框添加换肤代码,首先要自定义一个类,在类中实现,详细代码如下:
头文件:
#pragma once
// CMyFileDialog
class CMyFileDialog
{
// 接口
public:
// 构造函数
CMyFileDialog(void);
// 获取文件全名(含路径)
LPCTSTR GetFile() { return m_szFile; }
// 显示打开对话框
BOOL OpenFileDialog(HWND hWndParent);
// 内部实现
protected:
// 文件名
WCHAR m_szFile[MAX_PATH];
OPENFILENAME m_openFile;
// CALLBACK function
static UINT_PTR CALLBACK OFNHookProc(HWND hdlg,UINT uiMsg,WPARAM wParam,LPARAM lParam);
};
实现文件:
// MyFileDialog.cpp : 实现文件
//
#include "stdafx.h"
#include "FileCutterTool.h"
#include "MyFileDialog.h"
#include "windows.h"
// CMyFileDialog
CMyFileDialog::CMyFileDialog()
{
memset(m_szFile,0,MAX_PATH);
::ZeroMemory(&m_openFile,sizeof(OPENFILENAME));
m_openFile.lStructSize = sizeof(OPENFILENAME);
m_openFile.hwndOwner = NULL;
m_openFile.lpstrFile = m_szFile;
m_openFile.nMaxFile = MAX_PATH;
m_openFile.lpstrFilter = _T("All Files(*.*)\0*.*\0\0");
// m_openFile.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
m_openFile.Flags = OFN_ENABLEHOOK;
m_openFile.lpfnHook = OFNHookProc;
}
BOOL CMyFileDialog::OpenFileDialog(HWND hWndParent)
{
m_openFile.hwndOwner = hWndParent;
if(GetOpenFileName(&m_openFile))
{
return TRUE;
}
return FALSE;
}
UINT_PTR CALLBACK CMyFileDialog::OFNHookProc(HWND hdlg,UINT uiMsg,WPARAM wParam,LPARAM lParam)
{
if (uiMsg==WM_INITDIALOG)
{
dsSkinWindow( hdlg, SKIN_TYPE_DIALOG, _T("mfctestdialog"), TRUE);
}
return 0;
}
调用代码:
// 打开文件对话框
CMyFileDialog sourceDlg;
if (sourceDlg.OpenFileDialog(m_hWnd))
{
// 获取源文件名
strSourceText = sourceDlg.GetFile();
}
7、 为浏览对话框添加换肤代码
要实现为浏览对话框换肤,也需要自定义一个类,详细实现代码如下:
头文件:
#ifndef _DIRDIALOG_H_
#define _DIRDIALOG_H_
#include <shlobj.h>
class CDirDialog
{
// 接口
public:
CDirDialog(void);
// 显示浏览对话框
BOOL DoBrowser(HWND hWndParent,LPCTSTR pszTitle = NULL);
// 取得用户选择的目录名称
LPCTSTR GetPath() { return m_szPath; }
protected:
// 浏览对话框所需的结构体,详见MSDN
BROWSEINFO m_info;
// 用来接受用户选择目录的缓冲区
WCHAR m_szDisplay[MAX_PATH];
WCHAR m_szPath[MAX_PATH];
// CALLBACK function
static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData);
};
#endif // _DIRDIALOG_H_
实现文件:
#include "stdafx.h"
#include "windows.h"
#include "DirDialog.h"
CDirDialog::CDirDialog()
{
memset(&m_info,0,sizeof(m_info));
m_info.hwndOwner = NULL;
m_info.pidlRoot = NULL;
m_info.pszDisplayName = m_szDisplay;
m_info.lpszTitle = NULL;
m_info.ulFlags = BIF_RETURNONLYFSDIRS;
m_info.lpfn = BrowseCallbackProc;
m_szPath[0] = '\0';
}
BOOL CDirDialog::DoBrowser(HWND hWndParent, LPCTSTR pszTitle)
{
if (pszTitle == NULL)
{
m_info.lpszTitle = _T("选择目标文件夹");
}
else
{
m_info.lpszTitle = pszTitle;
}
m_info.hwndOwner = hWndParent;
LPITEMIDLIST pItem = ::SHBrowseForFolder(&m_info);
if (pItem != 0)
{
::SHGetPathFromIDList(pItem,m_szPath);
return TRUE;
}
return FALSE;
}
int CALLBACK CDirDialog::BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
{
if (uMsg==BFFM_INITIALIZED)
{
dsSkinWindow( hwnd, SKIN_TYPE_DIALOG, _T("mfctestdialog"), TRUE);
}
return 0;
}
调用代码:
// 浏览目录对话框
CDirDialog sourceDlg;
if (sourceDlg.DoBrowser(m_hWnd))
{
// 获取源文件目录
strSourceText = sourceDlg.GetPath();
}
上面的代码即可实现换肤,在这里我要强调一个函数dsSkinWindow(),当最后一个参数为TRUE时,则整个窗口可实现全部换肤;当为FALSE时,只是对窗口框架进行换肤,而其中的控件则不做修改,需要自己逐个换肤,详细代码可以参照上面代码。