エクスプローラーのディレクトリー構成などツリーコントロールの活用場面は多いです。
ドラッグアンドドロップされたフォルダーに格納されている、フォルダー/ファイルを表示できれば、どのファイルにアクセスするかなども分かりやすいですね。
今回の記事では、ダイアログベースのアプリケーションにフォルダをドラッグアンドドロップすると、ツリーコントロールにフォルダ構成を表示するプログラムを作成します。
関連リンク:【MFC入門】ダイアログにファイルをドラッグアンドドロップしよう【Visual C++】
関連リンク:無料のVisual StudioをインストールしてMFCを動かそう
ツリーコントロールを配置しよう。
まずはリソースエディターからツリーコントロールをダイアログに配置しましょう。
- ダイアログベースのプロジェクトを作成。
関連リンク:無料のVisual StudioをインストールしてMFCを動かそう - ツールボックスからTree Controlを選択し、ダイアログに配置。
- 展開されているか、折りたたまれているか、親子関係の確認など外観を設定します。
Tree Controlのプロパティから以下の設定を変更しましょう。:展開されていれば[-]ボタンが、折りたたまれていれば[+]ボタンを表示します。
[Has Buttons] – True
:親から子にラインが表示されます。
[Has Lines] – True - 次にTree Controlの状態を保持するCTreeCtrlメンバー変数を宣言します。
Tree Controlを選択し、[右クリック] – [変数の追加]をクリックし、メンバー変数の追加ウィザードを表示します。[アクセス] – Private
[変数の種類] – CTreeCtrl
[変数名] – m_xcTree
[コントロール変数チェックボックス] – チェック
[カテゴリ] – Control
これでツリーコントロールを使用する準備が整いました。
ドラッグアンドドロップできるようにクラスウィザードを設定しよう。
次にドラッグアンドドロップを受け付けるようにダイアログのクラスウィザードを設定します。
- ダイアログが選択された状態で、[右クリック] – [クラスウィザード]をクリックします。
- メッセージタブから、[WM_DROPFILES]を選択し、[ハンドラーの追加]ボタンをクリックします。
- [OK]ボタンをクリックし、クラスウィザードを閉じます。
- ドラッグアンドドロップを受け付けるようにOnInitDialog()を編集します
1234567891011BOOL CTreeCtrlDlg::OnInitDialog(){CDialogEx::OnInitDialog();// ...// TODO: 初期化をここに追加します。DragAcceptFiles();return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。}DragAcceptFiles()を呼び出します。
ソースコードを編集しよう
では、いよいよコーディングしていきます。
ドラッグアンドドロップされたファイルパスの取得は以前のコードと同じです。
関連リンク:【MFC入門】ダイアログにファイルをドラッグアンドドロップしよう【Visual C++】
初めに、前項で追加したWM_DROPFILESイベントハンドラーを編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void CTreeCtrlDlg::OnDropFiles(HDROP hDropInfo) { m_xcTree.DeleteAllItems(); // D&Dされる度にツリーコントロールのアイテムを初期化 UINT length = DragQueryFile(hDropInfo, 0, NULL, 0); CString csfile; // ドロップされたファイルパス DragQueryFile(hDropInfo, 0, csfile.GetBuffer(length + 1), length + 1); csfile.ReleaseBuffer(); // ファイルパスからフォルダ名を取得、取得したフォルダ名をツリーコントロールのルートに追加する HTREEITEM hItem = m_xcTree.InsertItem(PathFindFileName(csfile), TVI_ROOT); DirectoryScanner(csfile, hItem); // フォルダ内を走査する CDialogEx::OnDropFiles(hDropInfo); } |
MSDN:CTreeCtrl クラス
MSDN:CTreeCtrl::InsertItem
CTreeCtrl::InsertItemには、第一引数にアイテム名、第二引数にどこに配置するかを指定します。
TVI_ROOTは最上位であるルートを指定しています。
続いて、DirectoryScannerは指定されたフォルダ内のフォルダー/ファイルを走査し、追加したルートに子アイテムを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
void CTreeCtrlDlg::DirectoryScanner(LPCTSTR pstr, HTREEITEM hItem) { CFileFind finder; // build a string with wildcards CString strWildcard(pstr); strWildcard += _T("\\*.*"); // start working for files BOOL bWorking = finder.FindFile(strWildcard); while (bWorking) { bWorking = finder.FindNextFile(); // skip [.] and [..] files; otherwise, we'd recur infinitely! if (finder.IsDots()) continue; m_xcTree.Expand(hItem, TVE_EXPAND); // ツリーのアイテムを展開する。 // if it's a directory, recursively search it if (finder.IsDirectory()) { CString strFilePath = finder.GetFilePath(); HTREEITEM hNewItem = m_xcTree.InsertItem(PathFindFileName(strFilePath), hItem); DirectoryScanner(strFilePath, hNewItem); } else if (finder.GetFileName().Right(4) == _T(".txt")) { CString strFileName = finder.GetFileName(); m_xcTree.InsertItem(PathFindFileName(strFileName), hItem); } } finder.Close(); } |
フォルダー内にフォルダーがあれば、再帰的に呼び出して、同じ走査を繰り返します。
第二引数でどのアイテムの子として追加するかを渡しているので、ツリーコントロール上にも正しく表示されるはずです。
さて、実行して動作を確認してみましょう。
まとめ
ファイルを走査する際にリストなどでファイルパスを取得しておけば、見つけたファイルに何か手を加える場合も簡単にできそうですね。
多数のファイルをチェックする場合など、アローキーだけでファイルを移動できるためツリーコントロールはとても便利に扱えます。