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

VC++工程中加入SplashScreen原理釋解

編輯:關於VC++

摘要

本文旨在剖析VC++工程中加入 SplashScreen 的原理,並給出在VC++ MFC(exe)工程中加入 SplashScreen 的步驟。

關鍵字 SplashScreen,原理

環境:Windows 98SE/2000,VC++ 6.0

SplashScreen - 我們使用 Word 之類的軟件在啟動的短暫時間裡就會看到它的身影。它通常用以在程序啟動時顯示程序及用戶名稱,版權信息等。我也不知道它准確的名稱是什麼(是閃屏嗎?),就這樣稱呼吧。也許你也想在自己的工程裡加入這樣的特性,本文將以創建實際工程的方式逐步剖析其實現原理。

注意:為避免實際所使用工程名給類或對象名帶來的干擾,除非特別說明,在本文中將使用基類名如CWinApp、CMainFrame、CDialog來代替實際工程中的相應派生類名進行描述。

Visual C++是一個相當強大的C++開發工具,它內嵌了對SplashScreen的支持。但是在MFC EXE類型工程中只是對帶有主框架類的SDI或MDI工程提供了這一支持,基於對話框類的工程則被排除在外。現在讓我們開始吧。第一步是在SDI工程中加入SplashScreen。

首先利用AppWizard生成一個SDI工程,除了其中Docking ToolBar必須選擇外(我認為這是MFC的一個Bug,當然這與本文討論的SplashScreen沒有關系),其他的文檔-視圖支持、狀態條之類的都可以不要,這樣可以盡量減少無用的代碼。

通過IDE中的菜單Project->Add to Project->Components and Controls,我們就可以從Visual C++ Components中選擇Splash Screen這個組件插入工程。

在點擊了"Insert"後會彈出一個如下圖所示的對話框,這是設置插入該工程中的SplashScreen的類名、顯示用位圖的ID及文件名,采用缺省值即可。

通過以上幾步的操作,就會在工程目錄下生成Splash.CPP和Splash.H文件,這便是CSplashWnd類的實現文件與頭文件。同時工程中CWinApp與CMainFrame類中的部分代碼也會被修改,以實現CSplashWnd窗口的消息處理。

接著我們來看看 CSplashWnd 類的聲明與主要的代碼(已經過刪減):

//類的聲明

class CSplashWnd : public CWnd
{
  CSplashWnd();
  ~CSplashWnd();
  virtual void PostNcDestroy();
  static void EnableSplashScreen(BOOL bEnable = TRUE);
  static void ShowSplashScreen(CWnd* pParentWnd = NULL);
  static BOOL PreTranslateAppMessage(MSG* pMsg);
  BOOL Create(CWnd* pParentWnd = NULL);
  void HideSplashScreen();
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  afx_msg void OnPaint();
  afx_msg void OnTimer(UINT nIDEvent);

  CBitmap m_bitmap;   //SplashScreen窗口顯示用的位圖對象
  static BOOL c_bShowSplashWnd; //是否要顯示SplashScreen的標志量
  static CSplashWnd* c_pSplashWnd;
};

//是否使用SplashScreen

void CSplashWnd::EnableSplashScreen(BOOL bEnable)
{
  c_bShowSplashWnd = bEnable;
}

//創建CsplashWnd對象,並調用Create()創建窗口

void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd)
{
  //如果不要顯示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();
}

//裝入SplashScreen欲顯示位圖,通過CreateEx()激發OnCreate()完成窗口創建與設置

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()
{
  DestroyWindow();
  AfxGetMainWnd()->UpdateWindow();
}

//利用窗口創建結構創建窗口,並設置定時器在750ms後觸發OnTimer()

int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  CenterWindow(); //窗口居中顯示
  SetTimer(1, 750, NULL); //設置定時器

  return 0;
}

//將鍵盤和鼠標消息傳遞給CSplashWnd對象,以銷毀窗口

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::OnTimer(UINT nIDEvent)
{
  HideSplashScreen();
}

再看看CWinApp和CMainFrame類中發生了什麼樣的改變:

(1)在CWinApp::InitInstance()中調用CSplashWnd::EnableSplashScreen()設置c_bShowSplashWnd;

在PreTranslateMessage()中調用CSplashWnd::PreTranslateAppMessage(),將鍵盤和鼠標消息傳遞給CSplashWnd對象,從而進一步調用CSplashWnd::HideSplashScreen()實現SplashScreen窗口的自身銷毀。

(2)在CMainFrame對象的OnCreate()中調用CSplashWnd::ShowSplashScreen()創建一個靜態的SplashScreen窗口對象c_pSplashWnd,並設置其父窗口為CMainFrame。在這個過程中,CSplashWnd自身會通過創建來設置一個定時器,然後定時器在第一個周期觸發時便調用HideSplashScreen()銷毀自己。

(3) 而CMainFrame對象的窗口創建消息則是由CWinApp對象在InitInstance()中通過

m_pMainWnd->ShowWindow()調用觸發的。

整個過程可以用下圖表示,基本原理就是由CMainFrame來創建CSplashWnd,然後由CSplashWnd自己的定時器觸發定時消息來銷毀窗口。所以 CSplashWnd 的加入與SDI還是MDI都沒有關系。

第二步,我們再來看看如何在基於對話框的工程中加入 SplashScreen。

通過對以上SDI工程中加入SplashScreen原理的剖析,我想大家也想到如何在基於對話框的工程中加入這一特性了。其實質就是由CDialog類完成SDI工程中CMainFrame類的工作,實現步驟如下:

(1)利用ClassWizard為CMyDialog添加WM_CREATE消息的處理函數OnCreate();(這裡使用CMyDialog是為了與函數內的基類名CDialog區別。)

int CMyDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CDialog::OnCreate(lpCreateStruct) == -1)
    return -1;
  CSplashWnd::ShowSplashScreen(this);
  return 0;
}

(2)利用ClassWizard為CWinApp添加消息轉發處理函數PreTranslateMessage();

BOOL CWinApp::PreTranslateMessage(MSG* pMsg)
{
  if (CSplashWnd::PreTranslateAppMessage(pMsg))
    return TRUE;

  return CWinApp::PreTranslateMessage(pMsg);
}

(3)CWinApp::InitInstance()中加入如下調用: CSplashWnd::EnableSplashScreen(TRUE);

(4)當然你還需要將上一個SDI工程中生成的Splash.CPP與Splash.H文件拷貝到當前工程目錄下,並利用Project->Add to Project->Files將這兩個文件引入工程。同時還要在CWinApp與CMainFrame的實現文件中#include "Splash.H"。

(5)然後在資源管理器裡添加一個ID為IDB_SPLASH的位圖。由於VC++的IDE只能顯示256色以下的位圖,所以如果你想顯示一幅真彩色的位圖,就請用Import方式導入一幅預先制作好的位圖。當然VC++會提示位圖已經成功導入,只是無法在IDE的位圖編輯器中顯示,而在程序運行時就會顯示了。如果你想象Word那樣顯示用戶名等信息,可以在CSplashWnd::Create()中裝載位圖之後增加自己的代碼來修改位圖。

通過以上這幾步操作,我們就完成了在基於對話框的工程中加入SplashScreen的工作。清楚了嗎?

本文配套源碼

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