作者信息:罗树鹏 http://www.cnblogs.com/luoshupeng
由于笔者在实践过程中走了一些弯路,所以把这些情况记录下来,希望为后来者提供一些经验。
在VC6.0时代,可以通过组件为工程加入SplashScreen,具体方法是通过IDE中的菜单Project->Add to Project->Components and Controls,就可以从Visual C++ Components中选择Splash Screen这个组件插入工程。
但进入到VC. NET时代,这种方法就不行了,需要程序作者自己加入SplashScreen类,自己处理一些系统消息。下面笔者就把实践过程记录下来,并指出需要注意的地方。
一、新建一个SplashScreen类,并声明成员和方法
新建基类为CWnd的CSplashWnd类(当然类名可以自由书写),并声明如下成员:
CBitmap m_bitmap; //加载SplashScreen图片用
static CSplashWnd* c_pSplashWnd; //CSplashWnd类的句柄,可以用来判定CSplashWnd是否已经创建或消毁
static BOOL c_bShowSplashWnd; //标识是否显示显示用来,静态成员需要在类外进行初始化
类成员一般声明为保护类型(protected)。接下来声明几个静态方法来处理这些成员:
static BOOL c_bShowSplashWnd; //标识是否显示显示用来,静态成员需要在类外进行初始化
static void ShowSplashScreen(CWnd* pParentWnd = NULL); //用来显示SplashScreen窗口
static BOOL PreTranslateAppMessage(MSG* pMsg); //用来处理一些消息 注意这里不是继承于CWnd类的PreTranslateMessage方法
这些方法一定要声明成公开类型的(public),因为要在外部调用这些方法。接下来再声明两个自定义方法:
BOOL Create(CWnd* pParentWnd = NULL); //创建窗口
void HideSplashScreen(void); //隐藏窗口
我把这些方法声明为保护类型(protected)。接下来再声明一些处理系统消息的函数:
virtual void PostNcDestroy();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnPaint();
至此,类已经设计完毕。完整的头文件如下:
#pragma once
#include "afxwin.h"
// CSplashWnd
class CSplashWnd : public CWnd
{
DECLARE_DYNAMIC(CSplashWnd)
protected:
CSplashWnd();
public:
virtual ~CSplashWnd();
protected:
CBitmap m_bitmap; //加载SplashScreen图片用
static CSplashWnd* c_pSplashWnd; //CSplashWnd类的句柄,可以用来判定CSplashWnd是否已经创建或消毁
static BOOL c_bShowSplashWnd; //标识是否显示显示用来,静态成员需要在类外进行初始化
public:
static void EnableSplashScreen(BOOL bEnable = TRUE); //用来设定c_bShowSplashWnd
static void ShowSplashScreen(CWnd* pParentWnd = NULL); //用来显示SplashScreen窗口
static BOOL PreTranslateAppMessage(MSG* pMsg); //用来处理一些消息 注意这里不是继承于CWnd类的PreTranslateMessage方法
protected:
virtual void PostNcDestroy();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnPaint();
BOOL Create(CWnd* pParentWnd = NULL); //创建窗口
void HideSplashScreen(void); //隐藏窗口
DECLARE_MESSAGE_MAP()
};
二、CSplashWnd类方法的实现
这里需要注意的事项是在析构函数中一定要把c_pSplashWnd成员置为NULL类型,否则程序收到消息后会出现异常。其它没有什么了,直接看代码吧。
// Splash.cpp : implementation file
//
#include "stdafx.h"
#include "Splash.h"
CSplashWnd* CSplashWnd::c_pSplashWnd;
BOOL CSplashWnd::c_bShowSplashWnd;
// CSplashWnd
IMPLEMENT_DYNAMIC(CSplashWnd, CWnd)
CSplashWnd::CSplashWnd()
{
}
CSplashWnd::~CSplashWnd()
{
ASSERT(c_pSplashWnd == this);
c_pSplashWnd = NULL;
}
BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
ON_WM_CREATE()
ON_WM_TIMER()
ON_WM_PAINT()
END_MESSAGE_MAP()
// CSplashWnd message handlers
void CSplashWnd::EnableSplashScreen(BOOL bEnable/* = TRUE*/)
{
c_bShowSplashWnd = bEnable;
}
void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd/* = NULL*/)
{
//如果不要显示SplashScreen或SplashWnd对象已经被创建则返回
if ( !c_bShowSplashWnd || c_pSplashWnd!=NULL)
{
return;
}
c_pSplashWnd = new CSplashWnd;
if ( ! c_pSplashWnd->Create(pParentWnd))
{
delete c_pSplashWnd;
}
else
{
c_pSplashWnd->UpdateWindow();
}
}
BOOL CSplashWnd::PreTranslateAppMessage(MSG* pMsg)
{
if (c_pSplashWnd == NULL)
return FALSE;
if (pMsg->message == WM_KEYDOWN
|| pMsg->message == WM_SYSKEYDOWN
|| pMsg->message == WM_LBUTTONDOWN
|| pMsg->message == WM_RBUTTONDOWN
|| pMsg->message == WM_MBUTTONDOWN
|| pMsg->message == WM_NCLBUTTONDOWN
|| pMsg->message == WM_NCRBUTTONDOWN
|| pMsg->message == WM_NCMBUTTONDOWN)
{
c_pSplashWnd->HideSplashScreen();
return TRUE;
}
return FALSE;
}
void CSplashWnd::PostNcDestroy()
{
delete this;
}
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
CenterWindow();
SetTimer(1,3000,NULL);
return 0;
}
void CSplashWnd::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if (nIDEvent == 1)
{
HideSplashScreen();
}
/*CWnd::OnTimer(nIDEvent);*/
}
void CSplashWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CWnd::OnPaint() for painting messages
CDC dcImg;
if ( ! dcImg.CreateCompatibleDC(&dc))
{
return ;
}
BITMAP bm;
m_bitmap.GetBitmap(&bm);
// paint the image
CBitmap* pOldBit = dcImg.SelectObject(&m_bitmap);
dc.BitBlt(0,0,bm.bmWidth,bm.bmHeight,&dcImg,0,0,SRCCOPY);
dcImg.SelectObject(pOldBit);
}
BOOL CSplashWnd::Create(CWnd* pParentWnd)
{
if ( ! m_bitmap.LoadBitmap(IDB_SPLASH))
{
return FALSE;
}
BITMAP bm;
m_bitmap.GetBitmap(&bm);
return CreateEx(0,
AfxRegisterWndClass(0,AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL,
WS_POPUP|WS_VISIBLE,
0,
0,
bm.bmWidth,
bm.bmHeight,
pParentWnd->GetSafeHwnd(),
NULL);
}
void CSplashWnd::HideSplashScreen(void)
{
DestroyWindow();
AfxGetMainWnd()->UpdateWindow();
}
三、在SDI或者MDI中使用CSplashWnd类
(1) 在CWinApp::InitInstance()中调用CSplashWnd::EnableSplashScreen()设置c_bShowSplashWnd;
在PreTranslateMessage()中调用CSplashWnd::PreTranslateAppMessage(),将键盘和鼠标消息传递给CSplashWnd对象
(2) (2)在CMainFrame对象的OnCreate()中调用CSplashWnd::ShowSplashScreen()创建一个静态的SplashScreen窗口对象c_pSplashWnd,并设置其父窗口为CMainFrame.
代码如下:
BOOL CDrawApp::InitInstance()
{
//用来处理是否显示SplashScreen
{
CCommandLineInfo cmdinfo;
ProcessShellCommand(cmdinfo);
CSplashWnd::EnableSplashScreen(cmdinfo.m_bShowSplash);
}
………..
}
BOOL CDrawApp::PreTranslateMessage(MSG* pMsg)
{
if (CSplashWnd::PreTranslateAppMessage(pMsg))
return TRUE;
return CWinAppEx::PreTranslateMessage(pMsg);
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
return -1;
………..
CSplashWnd::ShowSplashScreen(this);
return 0;
}
在此过程中不会出现什么错误,但编译时会提示:IDB_SPLASH没有定义。这需要在资源中加入个位图资源,并将”resource.h” 头文件包含到CSlashWnd.cpp文件中。
四、在对话框中使用CSplashWnd类
在对话框中使用和在SDI或MDI中使用基本相似,首先在CWinApp的继承类中处理InitInstance()和PreTranslateMessage(MSG* pMsg)两个消息函数,然后在对话框类的WM_CREATE响应函数中显示SplashScreen。
BOOL CDLGApp::InitInstance()
{
//用来处理是否显示SplashScreen
{
CCommandLineInfo cmdinfo;
ProcessShellCommand(cmdinfo);
CSplashWnd::EnableSplashScreen(cmdinfo.m_bShowSplash);
}
……….
}
BOOL CDLGApp::PreTranslateMessage(MSG* pMsg)
{
if ( CSplashWnd::PreTranslateAppMessage(pMsg) )
{
return TRUE;
}
return CWinApp::PreTranslateMessage(pMsg);
}
int CDLGDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialogEx::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
CSplashWnd::ShowSplashScreen(this);
return 0;
}