Top > プログラミング関連 > ツリーコントロールでタブコントロールを代用
2006/3/28 更新
2006/2/17 記述
設定画面を作るとき、設定項目が増えてくると系統別に設定項目を分けるためにタブコントロールとプロパティシートを使うことがよくあります。しかし、系統が増えるにつれタブが多くなり、分かりにくく、また使いにくくなります。
そこでツリーコントロールを使い、この問題を改善することにします。ダイアログリソースは多くがそのまま流用できるため、タブコントロールからの移行は比較的簡単です。
ここではWTLを使います。ダイアログベースで実装します。
TABキーによるコントロール間のフォーカスの移動に対応しました。
ここで出てきたスクロールコンテナは、様々な大きさのダイアログボックスをプロパティシートとして使えるようにするために導入しました。スクロールコンテナの詳細はhttp://home.att.ne.jp/banana/akatsuki/doc/atlwtl/atlwtl20-04/index.htmlを参照してください。
実装のためにまずはリソースを用意します。
ツリーコントロールのIDは[IDC_TREE]とし、「スタイル」は[ボタンあり][最上位にも線を表示][ドラッグドロップを無効][境界線][選択を常に表示]、「その他のスタイル」は[全行選択][スクロール]にチェックを入れました。
また、グループボックスがありますが、これはスクロールコンテナを貼り付ける位置を指定するためのものです。IDは[IDC_STATIC_FRAME]です。
2006/3/28追記:グループボックスは場所だけ分かればいいので、[無効]にチェックを入れ、[可視]のチェックを外しておきました。
ここでプロパティページとして使うダイアログは「スタイル」に[チャイルド]を選択し、「その他のスタイル」で[無効]のチェックを外しておく必要があります。[システムメニュー]は外しておいた方がいいでしょう。
2006/3/28追記:「その他のスタイル」で[コントロール]にチェックを入れておきます。これによりTABキーでコントロール間のフォーカスが移動できるようになります。
この条件を満たすダイアログならプロパティページとして使うことができますので、テスト用に以下の3種類のリソースを用意しました。
なお、リソースエディタで作ったプロパティページは標準で[無効]にチェックが入っているのですが、そのままではクリックに反応しないダイアログが出来るため使い物にならなくなります。
ダイアログボックスのIDと同じIDでストリングリソースを作成しておきます。このリソースがツリーコントロールに表示されるアイテム名になります。
本来はダイアログのウィンドウタイトルから項目名を取得できるのがよかったのですが、タイトルバーがないダイアログにはキャプションを設定できませんので、ストリングテーブルを参照する方式にしました。
基本的なモードレスダイアログボックスのコードです。CDialog1/CDialog2/CDialog3とも中身はほぼ同じです。
親ダイアログのクラスはCDialog1/CDialog2/CDialog3をメンバに持っています。また、アクティブになっている(現在可視化されている)プロパティページのウィンドウハンドルもメンバに持っています。
このウィンドウはWM_INITDIALOメッセージとWM_NOTIFYメッセージのTVN_SELCHANGEDに反応します。
OnInitDialog()内の概略です。
LRESULT CMainDialog::OnInitDialog(HWND hWnd, LPARAM lParam)
{
//2006/3/28更新 ここから
//プロパティシートを貼り付けるためのスクロールコンテナの配置場所を取得
CStatic StaticFrame;
StaticFrame=GetDlgItem(IDC_STATIC_FRAME);
RECT rect;
StaticFrame.GetWindowRect(&rect);
ScreenToClient(&rect);
//スクロールコンテナを配置
ScrollWindow.Create(m_hWnd,rect,NULL,WS_CHILD|WS_VISIBLE , WS_EX_CLIENTEDGE|WS_EX_CONTROLPARENT);
//2006/3/28更新 ここまで
//スクロールコンテナを親としてダイアログを作成
Dlg1.Create(ScrollWindow);
...
//ツリービュー
TreeView=GetDlgItem(IDC_TREE);
//ダイアログ1の情報
HTREEITEM hItemDlg1=TreeView.InsertItem(CString(MAKEINTRESOURCE(Dlg1.IDD)), TVI_ROOT, TVI_LAST);
TreeView.SetItemData(hItemDlg1,(DWORD)Dlg1.m_hWnd);
...
//最初はDlg1を表示
Dlg1.ShowWindow(SW_SHOW);
ScrollWindow.SetClient(Dlg1);
ActiveDialog=Dlg1;
OnTreeSelect内は次のようになっています。
LRESULT CMainDialog::OnTreeSelect(LPNMHDR pnmh)
{
if(pnmh->hwndFrom==TreeView){
HTREEITEM hItem=TreeView.GetSelectedItem();
if(!hItem)return 0;
HWND SelectedDialog=(HWND)TreeView.GetItemData(hItem);
if(NULL!=SelectedDialog && SelectedDialog!=ActiveDialog){
::ShowWindow(ActiveDialog,SW_HIDE);
::ShowWindow(SelectedDialog,SW_SHOW);
ActiveDialog=SelectedDialog;
ScrollWindow.SetClient(NULL);
ScrollWindow.SetClient(SelectedDialog);
}
}
return 0;
}
実行すると分かりますが、タイトルバー付きのダイアログは見た目がよくありません。実際に使う時はタイトルバーなしで使うことになるでしょう。
2006/3/28追記:タイトルバー付きでもスクロールコンテナに貼り付けるとタイトルバーが消えるようです。ということはダイアログにキャプションを持たせることが出来るので、別にストリングテーブルを使わなくても良かったような気はします。
このコードにはNYSL 0.9982を適用します。