宇宙仮面の
C# Programming

 
Image

フォルダーを選択するダイアログ(TreeViewとListView)


開発環境: Visual Studio 2003
  • TreeViewとListViewによるディレクトリとファイルの一覧
  • ★入門コードで作るエクスプローラの雛形

1.目次

2.目的

.NET Framework V1.0 では、フォルダを選択するダイアログがサポートされておらず、
以下の用の自分で作るしかありませんでした。
.Net Framework V1.1 以降では、FolderBrowserDialog
が標準でサポートされるようになり、楽になっています。

以下の内容は、.NET Framework V1.0時代にフォルダを
選択するダイアログを作ろうと試行錯誤していたころ、
Dkさんが開発されたTreeView と ListView によるディレクトリとファイルの
一覧の Windows.Forms アプリケーションのソースを送っていただきました。
Dkさんのご了解のもと、ソースコードを公開させていただきます。

3.参考

(1) 

4.TreeView by Dk


次の図が、Dkさん提供の TreeView by Dk です。
左側に TreeView によるディレクトリ表示、
右側に ListView によるファイル表示があり、スプリッターで分割しています。
また ListView の下には StatusBarによる選択したディレクトリ、
ファイルの絶対パスの表示をしています。
さらに、ダブルクリックにより、選択ファイルを起動できます。

Image

5.Directory Tree View サンプルからの改良点


Directory Tree View サンプルからの改良点 (Dkさんのコメント)
        /// ★入門コードで作るエクスプローラの雛形
        /// TreeViewとListViewによるディレクトリとファイルの一覧のサンプル
        /// 
        /// サンプルからの改良点
        /// ・WinXPでCドライブの「System Volume Infomation」フォルダ以降が表示されない
        /// (私の環境固有の不具合か?そもそも「System Volume Infomation」って??)
        /// 
        /// ・ファルダアイコンに開くアイコンを追加
        ///      ImageListの設定
        ///             0 = フロッピーディスクアイコン
        ///             1 = ドライブアイコン
        ///             2 = 閉じたフォルダアイコン
        ///             3 = 開いたフォルダアイコン
        ///             ※アイコンはshell32.dllから抽出
        ///     
        /// ・Splitterの追加(TreeViewのDockをLeft、ListViewのDockをFillに設定)
        /// 
        /// ・選択フォルダ内のファイル一覧をListViewに表示
        /// 
        /// ・選択フォルダの絶対パスをStatusBarに表示
        /// 
        /// ・選択ファイルの絶対パスをStatusBarに表示
        /// 
        /// ・選択ファイルの起動(指定アプリケーションにてProcess.Start())
        /// 
        /// ・動作に影響のない(と思われる)コードを極力排除(ええんかなぁ?)

コメント
Dkさん >> サンプルで、アクセスエラーフォルダ以降が表示されない。
Dkさん >> 原因:「System Volume Infomation」のアクセスエラーでcatchされるとforeachから抜けてしまう
Dkさん >> (私の環境固有の現象?)
Dkさん >> 解決:この特定フォルダを回避するのではなく、try,catchをforeach内で処理

>> 自分の環境だとアクセスできてしまうので、気がつきませんでした。^^;
>> Dk さんのように try catch でステータスバーに表示するほうがスマートですね。
Dkさん >> 動作に影響のない(と思われる)コードを極力排除(ええんかなぁ?)

>> BeginUpdate(), EndUpdate() は、自分の試した範囲でもほとんど効果がないので、
>> なしでOKだと思います。
>> Directory Tree Viewの selectedDir.Refresh(); も不要ですね ^ ^; 汗。

2002/9/22 version の障害(Dkさんからのレポート)
現在の 2002/9/22 version では、Cドライブ直下のファイルを選択する場合に
\マークがダブってしまう障害があるという報告がありました。
以下、Dk さんからのメイル(2002/11/13受信)の抜粋です。
-----------------------------------------
Cドライブ直下のファイルを選択する場合に\マークがダブってます(笑
listView1_Clickは↓ですね。

private void listView1_Click(object sender, System.EventArgs e)
{

        string s1,s2,s3;

        if( treeView1.SelectedNode.FullPath.Length <= 3 )
        {
                s1 = treeView1.SelectedNode.FullPath.Substring(0,3);
                s2 = treeView1.SelectedNode.FullPath.Substring(3,(treeView1.SelectedNode.FullPath.Length - 3));
                s3 = listView1.SelectedItems[0].Text;
                //★Cドライブ直下のファイルを選択する場合は\を付けなくてよい。

        }
        else
        {
                s1 = treeView1.SelectedNode.FullPath.Substring(0,3);
                s2 = treeView1.SelectedNode.FullPath.Substring(4,(treeView1.SelectedNode.FullPath.Length - 4));
                s3 = "\\" + listView1.SelectedItems[0].Text;
                //★上記以外のファイルを選択する場合は\を付ける。

        }

        statusBar1.Text = s1 + s2 + s3;
}

6.TreeView by Dk ソースコードダウンロード

Dkさんからご提供いただいたソースコード一式(TreeView_by_Dk.lzh) 42.5 KB

7.TreeView by Dk ソースコード


Version 情報
2002/9/22 version


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using System.IO;
using System.Diagnostics;

namespace TreeView_by_Dk
{
        /// <summary>
        /// 
        /// ★入門コードで作るエクスプローラの雛形
        /// TreeViewとListViewによるディレクトリとファイルの一覧のサンプル
        /// 
        /// サンプルからの改良点
        /// ・WinXPでCドライブの「System Volume Infomation」フォルダ以降が表示されない
        /// (私の環境固有の不具合か?そもそも「System Volume Infomation」って??)
        /// 
        /// ・ファルダアイコンに開くアイコンを追加
        ///      ImageListの設定
        ///             0 = フロッピーディスクアイコン
        ///             1 = ドライブアイコン
        ///             2 = 閉じたフォルダアイコン
        ///             3 = 開いたフォルダアイコン
        ///             ※アイコンはshell32.dllから抽出
        ///     
        /// ・Splitterの追加(TreeViewのDockをLeft、ListViewのDockをFillに設定)
        /// 
        /// ・選択フォルダ内のファイル一覧をListViewに表示
        /// 
        /// ・選択フォルダの絶対パスをStatusBarに表示
        /// 
        /// ・選択ファイルの絶対パスをStatusBarに表示
        /// 
        /// ・選択ファイルの起動(指定アプリケーションにてProcess.Start())
        /// 
        /// ・動作に影響のない(と思われる)コードを極力排除(ええんかなぁ?)
        /// 
        /// </summary>
        public class Form1 : System.Windows.Forms.Form
        {
                private System.Windows.Forms.ImageList imageList1;
                private System.Windows.Forms.TreeView treeView1;
                private System.Windows.Forms.Splitter splitter1;
                private System.Windows.Forms.ListView listView1;
                private System.Windows.Forms.StatusBar statusBar1;
                private System.ComponentModel.IContainer components;

                public Form1()
                {
                        //
                        // Windows フォーム デザイナ サポートに必要です。
                        //
                        InitializeComponent();

                        //
                        // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
                        //
                }

                /// <summary>
                /// 使用されているリソースに後処理を実行します。
                /// </summary>
                protected override void Dispose( bool disposing )
                {
                        if( disposing )
                        {
                                if (components != null) 
                                {
                                        components.Dispose();
                                }
                        }
                        base.Dispose( disposing );
                }

                #region Windows Form Designer generated code
                /// <summary>
                /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
                /// コード エディタで変更しないでください。
                /// </summary>
                private void InitializeComponent()
                {
                        this.components = new System.ComponentModel.Container();
                        System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
                        this.imageList1 = new System.Windows.Forms.ImageList(this.components);
                        this.treeView1 = new System.Windows.Forms.TreeView();
                        this.splitter1 = new System.Windows.Forms.Splitter();
                        this.listView1 = new System.Windows.Forms.ListView();
                        this.statusBar1 = new System.Windows.Forms.StatusBar();
                        this.SuspendLayout();
                        // 
                        // imageList1
                        // 
                        this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
                        this.imageList1.ImageSize = new System.Drawing.Size(16, 16);
                        this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
                        this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
                        // 
                        // treeView1
                        // 
                        this.treeView1.Dock = System.Windows.Forms.DockStyle.Left;
                        this.treeView1.ImageIndex = -1;
                        this.treeView1.Name = "treeView1";
                        this.treeView1.SelectedImageIndex = -1;
                        this.treeView1.Size = new System.Drawing.Size(121, 273);
                        this.treeView1.TabIndex = 0;
                        this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView1_AfterSelect);
                        this.treeView1.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView1_BeforeExpand);
                        // 
                        // splitter1
                        // 
                        this.splitter1.Location = new System.Drawing.Point(121, 0);
                        this.splitter1.Name = "splitter1";
                        this.splitter1.Size = new System.Drawing.Size(3, 273);
                        this.splitter1.TabIndex = 1;
                        this.splitter1.TabStop = false;
                        // 
                        // listView1
                        // 
                        this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
                        this.listView1.Location = new System.Drawing.Point(124, 0);
                        this.listView1.Name = "listView1";
                        this.listView1.Size = new System.Drawing.Size(168, 273);
                        this.listView1.TabIndex = 2;
                        this.listView1.View = System.Windows.Forms.View.List;
                        this.listView1.Click += new System.EventHandler(this.listView1_Click);
                        this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick);
                        // 
                        // statusBar1
                        // 
                        this.statusBar1.Location = new System.Drawing.Point(124, 251);
                        this.statusBar1.Name = "statusBar1";
                        this.statusBar1.Size = new System.Drawing.Size(168, 22);
                        this.statusBar1.TabIndex = 3;
                        // 
                        // Form1
                        // 
                        this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
                        this.ClientSize = new System.Drawing.Size(292, 273);
                        this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                                                                                                  this.statusBar1,
                                                                                                                                                  this.listView1,
                                                                                                                                                  this.splitter1,
                                                                                                                                                  this.treeView1});
                        this.Name = "Form1";
                        this.Text = "TreeView_by_Dk";
                        this.Load += new System.EventHandler(this.Form1_Load);
                        this.ResumeLayout(false);

                }
                #endregion

                /// <summary>
                /// アプリケーションのメイン エントリ ポイントです。
                /// </summary>
                [STAThread]
                static void Main() 
                {
                        Application.Run(new Form1());
                }

                private void Form1_Load(object sender, System.EventArgs e)
                {
                        treeView1.ImageList = imageList1;//使用するImageListを指定

                        string[] drives = Environment.GetLogicalDrives();//ドライブ取得

                        foreach(string drive in drives)
                        {
                                TreeNode tn = new TreeNode(drive);

                                if (drive == "A:\\") //ドライブに対しImageListの画像を指定
                                {
                                        tn.ImageIndex = 0;
                                        tn.SelectedImageIndex = 0;
                                }
                                else 
                                {
                                        tn.ImageIndex = 1;
                                        tn.SelectedImageIndex = 1;
                                }

                                treeView1.Nodes.Add(tn);
                                tn.Nodes.Add("dummy");

                                //Cドライブを初期展開する場合このコードを追加
                                //if(tn.Text == "C:\\")
                                //      tn.Expand();
                        }
                }

                private void treeView1_BeforeExpand(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
                {
                        //サンプルで、アクセスエラーフォルダ以降が表示されない。
                        //原因:「System Volume Infomation」のアクセスエラーでcatchされるとforeachから抜けてしまう
                        //(私の環境固有の現象?)
                        //解決:この特定フォルダを回避するのではなく、try,catchをforeach内で処理
                        //
                        //BeginUpdate(),EndUpdate()はdummyフォルダを作成するこの方法では不要?
                        //表示するとき下位フォルダがdummyの1つしか追加されないので負担がかからない?

                        TreeNode selectedNode = e.Node;         //引数のイベント発生ノードに対し追加・削除・複製
                        selectedNode.Nodes.Clear();                     //ダミーノードをクリアするので必要

                        DirectoryInfo selectedDir = new DirectoryInfo(selectedNode.FullPath);//using System.IO

                        if (selectedDir.Exists) //ディレクトリが存在すればノードに追加
                        {
                                DirectoryInfo[] subDirInfo = selectedDir.GetDirectories();

                                foreach(DirectoryInfo di in subDirInfo)
                                {
                                        try
                                        {
                                                TreeNode nd = selectedNode.Nodes.Add(di.Name);//サブディレクトリをノードに追加
                                                nd.ImageIndex = 2;                      //閉じたフォルダアイコン指定
                                                nd.SelectedImageIndex = 3;      //開いたフォルダアイコン指定

                                                DirectoryInfo[] subSubInfo = di.GetDirectories();
                                                if(subSubInfo.Length > 0)       //下位にディレクトリがあれば
                                                {
                                                        nd.Nodes.Add("dummy");  //+を表示するためにダミーノード(フォルダ)追加
                                                }
                                        }
                                        catch (Exception exc)
                                        {
                                                string s = exc.Message.ToString();
                                                statusBar1.Text = s;            //statusBarにエラーを表示
                                        }
                                }
                        }
                        else
                        {
                                MessageBox.Show( selectedDir.Root.Name + "にディスクを挿入してください。",
                                        this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        }
                }

                private void treeView1_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
                {
                        //ListViewにファイル一覧を表示

                        listView1.Clear();//これがないとファイル表示が累積
                        DirectoryInfo di = new DirectoryInfo(e.Node.FullPath);

                        if(e.Node.Text != "A:\\")//+マークによるノード展開でデフォルトのA:\アクセス回避
                        {
                                if(di.Exists)
                                {
                                        try
                                        {
                                                foreach(FileInfo file in di.GetFiles())
                                                {
                                                        listView1.Items.Add(file.Name);
                                                }
                                        }
                                        catch (Exception exc)
                                        {
                                                MessageBox.Show( exc.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                                        }
                                }
                        }

                        statusBarTextShow(e);//★statusBarにFullPathを表示する下記メソッド

                }

                private void statusBarTextShow(System.Windows.Forms.TreeViewEventArgs e)
                {
                        //★statusBarにフォルダ名までのFullPathを表示
                        //
                        //statusBar1.Text = e.Node.FullPath;とすると、
                        //Environment.GetLogicalDrives()を使うと取得ドライブ名に\マークが付き(例:C:\)、
                        //FullPathを取得すると\マークが付く(例:\WINDOWS)ので、
                        //statusBar1.TextにFullPathを入れると「C:\\WINDOWS」となってしまう。。。
                        //ちなみに、
                        //string s1 = e.Node.FullPath.Substring(0,2);
                        //string s2 = e.Node.FullPath.Substring(3,(e.Node.FullPath.Length - 3));
                        //statusBar1.Text = s1 + s2;
                        //これではドライブ初期表示に\マークが表示されない(例:C:)

                        string s1,s2;

            if( e.Node.FullPath.Length <= 3 )
                        {
                                s1 = e.Node.FullPath.Substring(0,3);
                                s2 = e.Node.FullPath.Substring(3,(e.Node.FullPath.Length - 3));
                                //s2は、s2 = ""; のことです。
                        }
                        else
                        {
                                s1 = e.Node.FullPath.Substring(0,3);
                                s2 = e.Node.FullPath.Substring(4,(e.Node.FullPath.Length - 4));
                        }

                        statusBar1.Text = s1 + s2;
                }

                private void listView1_Click(object sender, System.EventArgs e)
                {
                        //statusBarにファイル名までのFullPathを表示

                        string s1,s2,s3;

                        if( treeView1.SelectedNode.FullPath.Length <= 3 )
                        {
                                s1 = treeView1.SelectedNode.FullPath.Substring(0,3);
                                s2 = treeView1.SelectedNode.FullPath.Substring(3,(treeView1.SelectedNode.FullPath.Length - 3));
                                //s2は、s2 = ""; のことです。
                        }
                        else
                        {
                                s1 = treeView1.SelectedNode.FullPath.Substring(0,3);
                                s2 = treeView1.SelectedNode.FullPath.Substring(4,(treeView1.SelectedNode.FullPath.Length - 4));
                        }

                        s3 = "\\" + listView1.SelectedItems[0].Text;

                        statusBar1.Text = s1 + s2 + s3;
                }

                private void listView1_DoubleClick(object sender, System.EventArgs e)
                {
                        //listView1の選択ファイルのダブルクリックで関連付けられたアプリ起動
                        //using System.Diagnosticsが必要

                        Process.Start(statusBar1.Text);
                }
        }
}