程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 如何在狀態欄中實現進度指示器控制

如何在狀態欄中實現進度指示器控制

編輯:關於VC++

我最近作了一個C++/MFC程序,這個程序有時要加載大容量文件,為了讓文件加載過程不至於太單調,我想在UI中用進度指示器顯示文件的加載過程,而且我想在程序的狀態欄中使用這個指示器控制。經過一番研究和嘗試,我實現了自己的想法。本文將詳細介紹整個實現過程。希望大家在解決類似的問題時少走一些彎路......

盡管MFC提供了標准的進度指示器控件(progress control),但是不能在狀態欄裡直接使用這個控件,因此我創建了自己的可重用C++類來實現進度指示。這個類從CStatusBar派生。整個實現過程不是很難,思路是在狀態欄創建一個進度指示器控制,把它作為子窗口來對待,然後根據不同的狀態來顯示或者隱藏進度指示器。本文提供了一個范例程序pgrsbar,這個程序的框架使用了MFC的文檔/視圖結構,在編輯視圖裡顯示文本文件。打開文件的時候,pgrsbar仿真長時間的加載過程並在狀態欄裡顯示進度指示,如圖一所示。我將這個含有進度指示器的狀態欄封裝在了一個CStatusBar派生的類中——CProgStatusBar。

圖一 在狀態欄裡顯示進度指示

下面是這個類的詳細說明和使用方法:

CProgStatusBar是從標准的MFC類CStatusBar派生而來。我在CProgStatusBar派生類中加了一個CProgressCtrl類型的數據成員——m_wndProgBar,並且實現了三個重要的成員函數或方法:OnCreate、OnSize和OnProgress。下面是這三個函數的詳細說明:

OnCreate負責在狀態欄第一次被創建時接收控制,繼而創建進度指示器並將它初始化為一個子窗口,

int CProgStatusBar::OnCreate(LPCREATESTRUCT lpcs)
{
  lpcs->style |= WS_CLIPCHILDREN;
  VERIFY(CStatusBar::OnCreate(lpcs)==0);
  VERIFY(m_wndProgBar.Create(WS_CHILD, CRect(), this, 1));
  m_wndProgBar.SetRange(0,100);
  return 0;
}   

OnCreate在狀態欄的式樣中加了一個WS_CLIPCHILDREN,它告訴Windows不要繪制子窗口以下的狀態欄區域,這樣可以減少屏幕閃爍。接著OnCreate創建進度指示器控制並將它的范圍設置成[0,100]。注意在這裡創建進度指示器控制時沒有用WS_VISIBLE,因為我想在程序開始的時候隱藏它。

無論何時,只要你在某個窗口裡添加子窗口,那麼一定要負責管理它的大小尺寸,也就是說,當父窗口大小改變後,子窗口的大小也要跟著作相應的改變。一般來說,這個工作在父窗口的WM_SIZE/OnSize處理例程中完成: 

// 狀態欄大小改變以後,子窗口的尺寸跟著變
void CProgStatusBar::OnSize(...)
{
  CStatusBar::OnSize(...);
  CRect rc;
  GetItemRect(0, &rc);
  m_wndProgBar.MoveWindow(&rc,FALSE);
}   

CProgStatusBar::OnSize 負責移動進度指示器到你期望的位置:例子程序是把它放在了狀態欄的第一個窗格,這個窗格通常用來顯示程序的“就緒”信息和命令提示信息。注意這裡不論進度指示器時處於可見狀態還是隱藏狀態,MoveWindow都照樣起作用——所以即便是進度指示器處於隱藏狀態,其窗口大小同樣是可調的。

說完窗口大小的調整,下面我們來看看進度指示器的顯示,進度指示器狀態的顯示在CProgStatusBar::OnProgress中完成。它有一個類型為UINT的入口參數:參數值的范圍從0到100,表示進度百分比,0表示進度沒開始,100表示全部完成。如果這個參數的值大於0,則OnProgress顯示進度控制並設置指示器的位置;如果參數值等於0,則 OnProgress隱藏進度控制。

雖然我們常常都把子窗口控制放在父窗口能繪制的區域的最上面,但這樣做在繪制方面是有一定風險的。在你隱藏/顯示進度控制時尤其如此,你會發現有兩個問題:第一,因為進度指示器顯示在狀態欄的第一個窗格位置,所以如果指示器顯示時已經顯示有狀態信息,那麼進度指示器和狀態信息文本就會有沖突,相互干擾。如圖二所示。之所以會這樣,是因為進度控制假設其繪制背景是干淨的,並且只繪制進度控制的著色部分。解決這個問題最簡單的方法是調用CStatusBar::SetWindowText(NULL)函數在顯示進度指示器之前打掃一下環境衛生,清除以前的文本。對於狀態欄來說,SetWindowText函數的作用是設置狀態欄第一個窗格的文本。

圖二 繪制問題

反之,當調用OnProgress(0)清除進度控制時也存在類似的問題,CProgStatusBar::OnProgress 隱藏進度控制後,狀態欄第一個窗格該顯示什麼信息呢?一般顯示“就緒”或其它的提示信息。當應用程序不做任何事情時,MFC程序總是在這個位置顯示資源串AFX_IDS_IDLEMESSAGE表示的文本,其缺省值為“就緒”,但你可以在RC文件中任意修改這個值。以我個人的觀點,我總是認為在這裡顯示就緒信息有點兒土。不管怎樣,在MFC程序的狀態欄中顯示就緒信息很容易:

// 在 CProgStatusBar::OnProgress函數中
// WM_SETMESSAGESTRING 的定義在<afxpriv.h>文件
GetParent()->PostMessage(WM_SETMESSAGESTRING,AFX_IDS_IDLEMESSAGE);   

如果你願意,完全可以創建不同的ID和消息,如用ID_DONE_LOADING表示“加載完成”,以此取代“就緒”。

CProgStatusBar實現了含有進度控制的狀態欄。它的使用方法很簡單:用CProgStatusBar代替CStatusBar聲明實例,然後在任何想要顯示進度控制指示的地方調用CProgStatusBar::OnProgress。詳細代碼請參考本文例子程序。創建CProgStatusBar很容易,它的原理與標准的狀態欄CStatusBar一樣——你只要明白什麼時候以及在哪裡調用OnProgress即可。

實際應用中要根據你的應用而定。我建議你按照例子程序的方法來使用它。不要直接暴露CProgStatusBar::OnProgress函數,例子程序的主窗口實現了一個特殊的消息——MYWM_PROGRESS,它將WPARAM傳遞到CProgStatusBar::OnProgress:

// MYWM_PROGRESS 在resource.h文件中定義
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_MESSAGE(MYWM_PROGRESS,OnProgress)
END_MESSAGE_MAP()
// 處理MYWM_PROGRESS消息
LRESULT CMainFrame::OnProgress(WPARAM wp, LPARAM lp)
{
  m_wndStatusBar.OnProgress(wp);
  return 0;
}   

這樣想要報告進度指示的任何對象都可以通過發送一個消息到主框架來代替直接對狀態欄進行調用。例如,在例子程序中,文檔的Serialize函數利用Sleep函數仿真耗時加載,每隔150毫秒報告一次進度狀態。通常,你肯定想讓文檔這樣的低級對象盡可能少地包含UI代碼。雖然在實踐中很少有程序員遵守這一原則,但最好不用UI操作你的文檔類,因為你很可能有一天想在某個服務中或命令行程序中使用它。不管怎麼說,發送消息到框架總比暴露框架的內部成員要好得多。為了安全起見,文檔的Serialize函數在發送消息前最好檢查一下框架是否存在。如果你不想從文檔發送Windows消息,可以用MFC的視圖更新機制來做。你可以發明一個“暗示”代碼以及一個小結構來保存進度百分比數據,並通過向框架發送MYWM_PROGRESS消息調用暗示信息。這是從文檔到視圖/框架傳遞進度控制信息的最省事的方式。

CProgStatusBar假設你報告的進度指示數據總是0到100的整數,並且假設你中途可以中斷進度指示器。以便你在想要改變設置的時候,可以修改CProgStatusBar或者調用CProgStatusBar::GetProgressCtrl來直接存取進度控制狀態。

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