プログレスバーと聞くと馴染みが無いかもしれませんが、見れば一目瞭然でしょう。
何かの処理が終了するまで待つ間、状況をビジュアル的に表すものです。
横長の長方形の枠の中に、左から青い四角が埋まっていって、全部埋まれば終了!
プログレスバーはテキストボックスやボタンと同様、ダイアログに表示して使います。
リソースのダイアログを開き、コントロールの一覧の中から
「プログレスバー」を選んで貼り付けるだけで簡単に準備できます。

ダイアログを最初から作るのが面倒なので(笑)、 「バージョン情報」のダイアログにプログレスバーを付けてみました。
ちなみにCopyrightの表示が1902になっていますが、このサンプルを作成したのは2002年なので、間違いですね。 『2000年問題』が対応されていないのでしょう(苦笑) もっとも、「西暦2000年を過ぎてるのにVC++4.0なんていう古いものを使い続けるなよ」って話もありそうですが(汗)
さて、プログレスバーをダイアログに貼り付けただけでは
当然何も動きません。
まずは、ClassWizardでCAboutDlgクラスの変数を割り当てておきます。
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はイベントの処理を行う関数なので、全イベントがこの関数を経由します。
つまり、一番よく呼ばれる関数だということです。
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を始めるような処理を加えます。
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は余り呼ばれない様です。
試しにダイアログの上でマウスを動かしてみると、とても早く
プログレスバーが進みます(苦笑)
ちなみに、プログレスバーで状況を表示する計算処理ですが、普通は別スレッドを起こして そちらで行います。 そのやり方はマルチスレッドを参照して下さい。