最近のアプリケーションの力の入れどころの1つに「カスタマイズ」があります。ユーザーの要求が多様になりわがままになってくると、それに応えるために様々な設定を用意し、その結果ダイアログがかなり増えることになります。
そういうときに非常に有効なのが、タブストリップを使ったダイアログです。
では、こういった形のダイアログを作成する方法を見ていきましょう。
では、いちばん簡単なタブストリップ型ダイアログを作成しましょう(難しいことは「プログラミングMFC:百科」−「プロパティシート」に載っています)。 (注:上に書いたオンラインヘルプには「境界線」を「なし」にすると書いてありますが、それと「タイトルバー」を「オン」にすることは同時にできません。プロパティページを作る時に変更すればいいのかもしれませんが、ここでは触れません)
最後に「その他のスタイル」タブをクリックして、「無効」を「オン」にしておきます。これで、準備が完了しました。
次に、このダイアログのクラスを作成します。ダイアログを選択している状態で、クラスウィザードを開きます。そうすると、新規にクラスを作成する段階になるでしょう。
さて、とりあえずタブストリップですから、ページは複数あった方がいいでしょう。今回は簡単に実装するだけですので、2ページだけ作成します。
さて、ここまでで下準備は終わりました。実際にダイアログを作成してみましょう。
さて、実際にダイアログを作成してみましょう。まず、このビュークラスの上の方にあるインクルードファイルの一覧の中に「#include "Tab1.h"」と「#include "Tab2.h"」を加えてください。 |
void CKABView::OnDlgOpen()
{
CTab1 cTab1; //タブ1のオブジェクトを作成します。
CTab2 cTab2; //タブ2のオブジェクトを作成します。
// ダイアログそのものを作成します。
CPropertySheet cPropSht( _T( "タブストリップ型ダイアログ" ) );
cPropSht.AddPage( &cTab1 ); //タブ1をダイアログに加えます。
cPropSht.AddPage( &cTab2 ); //タブ2をダイアログに加えます。
if( cPropSht.DoModal() ) //ダイアログを表示します。
{
// ここに「OK」ボタンが押されたときの反応を書き込みます。
}
}
この時点でとりあえずビルドしてみましょう。うまく行けば、先ほど作ったメニューのコマンドを選択すると、「いちぺぇじめ」「にぺぇじめ」と書かれたタブが並ぶダイアログが表示されるでしょう。
では、さらに細かく見ていきましょう。
まず、2つのプロパティページのオブジェクトを最初に作成します。次に、プロパティシートのオブジェクトも作成します。ここでは、同時にダイアログのタイトル(タブストリップ型ダイアログ)を設定しておきます。
ここで注意をひとつ。CPropertySheetクラスは、実際コモンダイアログに非常に近い存在です。そのため、ダイアログの形状を変更したりするのは結構大変です。最初の例のように、デフォルトで「更新」ボタンが付いていたりします。この辺の処理は前述のオンラインヘルプに載っている(なんか名称が変わってる。訳の関係?)ので、そちらを参考にしてください。
さて、最後にメンバ変数への代入の問題点を見てみましょう。最初のCTab1のエディットボックスに文字を格納し、また、その文字を受けるには次のようにします。 |
void CKABView::OnDlgOpen()
{
CTab1 cTab1; //タブ1のオブジェクトを作成します。
cTab1.m_cEditStr = _T( "あうあう!" ); //エディットボックスに文字を表示します。
CTab2 cTab2; //タブ2のオブジェクトを作成します。
// ダイアログそのものを作成します。
CPropertySheet cPropSht( _T( "タブストリップ型ダイアログ" ) );
cPropSht.AddPage( &cTab1 ); //タブ1をダイアログに加えます。
cPropSht.AddPage( &cTab2 ); //タブ2をダイアログに加えます。
if( cPropSht.DoModal() ) //ダイアログを表示します。
{
//「OK」が押されたなら、メンバ変数の内容を表示します。
MessageBox( cTab1.m_strEdit, _T( "エディットボックスの中身" ) );
}
}
まずタブ1からです。このエディットコントロールへの文字列の格納の方法は、普通にダイアログを使用するときと同じですね。これは、CPropatyPageクラスが、通常のダイアログとほぼ同じように振る舞うことを示しています。CPropertySheetクラスは単なるまとめ役でしかありません。具体的な操作はここのプロパティページへと直接アクセスします。
さて、次にタブ2のリストボックスへと文字列を格納してみましょう。 |
//〜〜略〜〜
CTab2 cTab2; //タブ2のオブジェクトを作成します。
cTab2.m_cListBox.AddString( _T( "テスト" ) ); //エラーが出ます。
//〜〜略〜〜
if( cPropSht.DoModal() ) //ダイアログを表示します。
{
//「OK」が押されたなら、メンバ変数の内容を表示します。
MessageBox( cTab1.m_strEdit, _T( "エディットボックスの中身" ) );
CString str;
cTab2.m_cListBox.GetText( 0, str );
MessageBox( str, _T( "一番目の文字列" ) );
}
}
さて、これを実際にビルドして実行すると、ASSERTに引っかかってプログラムが終了します。つまり、このプログラムにはバグがあるわけです。
その原因は、リストボックスがまだ構築されていないうちに操作をしようとしたからです。
確かにm_cListBoxメンバ変数は、ただの変数のように見えますが、れっきとしたコントロールです。そのため、実際にリストボックスが表示されていないこの段階では、リストボックスに項目を加えることはできないのです。
では、いつするのでしょうか? ダイアログが作成されるときにはOnInitDialogメンバ関数が呼び出されます。ダイアログが表示されるときにはWM_INITDIALOGメッセージが呼び出されるからです。 |
BOOL CTab2::OnInitDialog()
{
CPropertyPage::OnInitDialog();
m_cListBox.AddString( _T( "テスト" ) ); //リストボックスに加えます。
return TRUE; // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
// 例外: OCX プロパティ ページの戻り値は FALSE となります
}
m_cListBoxメンバ変数はCTab2のメンバなので、最初にCTab2と書く必要はありません。
さて、これでアサートもしないし大丈夫……と思ったらまだ甘いです。何度か使ってるうちに不具合が出るかもしれませんね。なぜでしょうか?
さて最後になりました。ここで問題なのは、WM_INITDIALOGメッセージの呼ばれるタイミングです。このメッセージは、プロパティページの場合にはそのページが表示されたときに呼び出されます。リストボックスに項目を加えたときにそれは解りましたね? それがなぜ問題なのでしょうか。
この問題は特にエディットコントロールに整数値を設定するときに大きな問題になるようです。エディットコントロールのメンバ変数に整数値を設定する前にデータを得ようとすると、とんでもない値が返ってきてしまいます。
この解決法は、リストボックスとは別に変数を作っておき、アクセスはそこにすることで解決します。例えば、CTab1クラスにCStringArrayクラスのm_cListStrAryメンバ変数を作ってしまいます。そして、データのアクセスはそのメンバ変数に任せてしまい、データが変更された時にだけリストボックスの中身をそのメンバ変数を元に作成するというものです。実際にしてみると…… |
BOOL CTab2::OnInitDialog()
{
CPropertyPage::OnInitDialog();
// コンストラクタかCTab2を構築した直後に、m_cListStrAryに値を加えておきます。
ListUpDate();
// リストボックスを更新するための関数を別に作り、その関数の中で
// m_cListStrAryを元にリストボックスの中身を埋めるようにします。
return TRUE; // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
// 例外: OCX プロパティ ページの戻り値は FALSE となります
}
void CTab2::ListUpDate()
{
m_cListBox.ResetContent(); //リストボックスの中身をクリアします。
for( int i = 0; i <= m_cListStrAry.GetSize() - 1; i++ )
{
//リストボックスの中身を埋めていきます。
m_cListBox.InsertString( i, m_cListStrAry.GetAt( i ) );
}
}
と、このような感じです(配列の中身を埋める部分は省略しました)。
以上でタブストリップ型ダイアログの簡単な作成の仕方と、その注意点を書いてみました。その他にも、CPropertySheetクラスにはウィザード方式のダイアログを作成することもできますし、更新ボタンやその他の機能も与えられているので、結構面白い使い方ができると思います。
この問題を解決する方法は、KnowledgeBaseのKB: Visual C++ How to Create a Property Sheet That Has Scrolling Tabs in MFCにありました。それは、まずCPropertySheetクラスを基底クラスとした新しいクラスを作成し、プロパティシートのクラスをその新しく作ったクラスで構築し、そしてその新しいクラスのOnCreateメンバ関数の最初に次のように記述するというものです |
EnableStackedTabs(FALSE);
EnableStackedTabsメンバ関数なるものの意味、はっきりいって解りません。おそらく表には出ていないCPropertySheetクラスのメンバなのでしょう。何れにせよ、この方法で解決します。
|
(C)KAB-studio 1997 ALL RIGHTS RESERVED. |