S-JIS[2002-11-24]

プログレスバー

プログレスバーと聞くと馴染みが無いかもしれませんが、見れば一目瞭然でしょう。
何かの処理が終了するまで待つ間、状況をビジュアル的に表すものです。 横長の長方形の枠の中に、左から青い四角が埋まっていって、全部埋まれば終了!


プログレスバーはテキストボックスやボタンと同様、ダイアログに表示して使います。
リソースのダイアログを開き、コントロールの一覧の中から 「プログレスバー」を選んで貼り付けるだけで簡単に準備できます。

ダイアログを最初から作るのが面倒なので(笑)、 「バージョン情報」のダイアログにプログレスバーを付けてみました。

ちなみにCopyrightの表示が1902になっていますが、このサンプルを作成したのは2002年なので、間違いですね。 『2000年問題』が対応されていないのでしょう(苦笑) もっとも、「西暦2000年を過ぎてるのにVC++4.0なんていう古いものを使い続けるなよ」って話もありそうですが(汗)


さて、プログレスバーをダイアログに貼り付けただけでは 当然何も動きません。
まずは、ClassWizardでCAboutDlgクラスの変数を割り当てておきます。

test.cpp:
class CAboutDlg : public CDialog
{
〜
// ダイアログ データ
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	CProgressCtrl	m_Progress1;
	//}}AFX_DATA
〜
};

プログレスバーは CProgressCtrlクラスで処理されます。
基本的な使い方は、まずSetRangeで全体の範囲を指定しておき、状況の進展をその範囲内の数値でSetPosを使って指定するというものです。


プログレスバーの範囲をSetRangeを使って設定します。初回に1回設定すればいいので、 WM_INITDIALOGのハンドラで行います。

test.cpp:
class CAboutDlg : public CDialog
{
〜
// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	virtual BOOL OnInitDialog();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
〜
};
〜
const int MAX=1000;

BOOL CAboutDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// TODO: この位置に初期化の補足処理を追加してください
	m_Progress1.SetRange(0,MAX);

	return TRUE;  // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
	              // 例外: OCX プロパティ ページの戻り値は FALSE となります
}

次は状況の進展を表示する場所ですが、 タイマー割り込みを使って一定周期で描き換えるなり、 描き換え用のイベントを作って連続して呼び出すなり、 自由です。
ここでは大分手抜きをして、WindowProcの中で描き換えることにします。 WindowProcはイベントの処理を行う関数なので、全イベントがこの関数を経由します。 つまり、一番よく呼ばれる関数だということです。

test.cpp:
class CAboutDlg : public CDialog
{
〜
  	// ClassWizard は仮想関数を生成しオーバーライドします。
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV のサポート
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	//}}AFX_VIRTUAL
〜
};
〜
LRESULT CAboutDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
	if(m_Progress1.m_hWnd){
		m_Progress1.SetPos(状況の進展に伴う値);
		UpdateData(FALSE);
	}

	return CDialog::WindowProc(message, wParam, lParam);
}

SetPosに渡す値がここでは未定ですが、サンプルとして 0から始まってMAXまで1ずつ増加していく様子をプログレスバーで表すことにします。 この増加用の変数として、CAboutDlgにm_nValueを追加します。

test.cpp:
class CAboutDlg : public CDialog
{
private:
	int m_nValue;
};
〜
BOOL CAboutDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// TODO: この位置に初期化の補足処理を追加してください
	m_Progress1.SetRange(0,max);
	m_nValue=0;

	return TRUE;  // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
	              // 例外: OCX プロパティ ページの戻り値は FALSE となります
}
〜
LRESULT CAboutDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
	if(m_Progress1.m_hWnd){
		if(m_nValue<MAX){
			m_nValue++;
		}
		m_Progress1.SetPos(m_nValue);
		UpdateData(FALSE);
	}

	return CDialog::WindowProc(message, wParam, lParam);
}

やあ、表示された表示された…と喜んでいましたが、よく見ると プログレスバー全体の枠線が表示されていません。
どうやら枠線が表示される前にSetPosとUpdateDataを実行しているのがまずいようです。 仕方がないので、枠線が表示されてからSetPosを始めるような処理を加えます。

test.cpp:
class CAboutDlg : public CDialog
{
〜
private:
	int m_nValue;
	BOOL m_bDraw;
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
	m_bDraw=FALSE;
}
〜
LRESULT CAboutDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
	if(message==WM_PAINT) m_bDraw=TRUE;

	if(m_Progress1.m_hWnd && m_bDraw){
		if(m_nValue<MAX){
			m_nValue++;
		}
		m_Progress1.SetPos(m_nValue);
		UpdateData(FALSE);
	}

	return CDialog::WindowProc(message, wParam, lParam);
}

これにより、WM_PAINTの後だけSetPosが実行されるようになりました。 そして、ダイアログバーの枠線もちゃんと出るようになりました。
ちなみに何故WM_PAINTなのかと問われれば、答は「試行錯誤の結果」です… だから、本当はWM_PAINTではまずいかもしれません。

ところで、WindowProcでm_nValueを増加させる処理を行ってきましたが、 何のイベントも無いと WindowProcは余り呼ばれない様です。
試しにダイアログの上でマウスを動かしてみると、とても早く プログレスバーが進みます(苦笑)


ちなみに、プログレスバーで状況を表示する計算処理ですが、普通は別スレッドを起こして そちらで行います。 そのやり方はマルチスレッドを参照して下さい。


VC++ページへ戻る / 技術メモへ戻る
メールの送信先:ひしだま