none
C#でフォルダにチェックボックスが付いているツリービューを作成したいです RRS feed

  • 質問

  • C#2005にて、フォルダ選択ダイアログのようなウィンドウを表示させ、フォルダの横にチェックボックスがありチェックしてOKボタンを押すと、チェックしたフォルダのフルパスなどの情報を取得できるような言うなれば、”チェックボックス付エクスプローラ”のようなウィンドウを作りたいと思っております。

    ↓こちらのサンプルプログラムを参考に作成してみましたが、ドライブが多かったり、階層が深いとすごく時間がかかってしまいます。

    http://www.apfield.jp/csgarden/treeview.html  // C#の庭園(ドライブのディレクトリ情報をツリービューとして表示する)

    何か良い方法をご存知方がいらっしゃいましたら、教えてください。

    よろしくお願いいたします。

    2011年7月21日 10:10

回答

  • ごめんなさい、VS2005なのを見落としてました。
    暗黙の型(var),ラムダ式,自動実装プロパティ,Func<T>は2008からしか使えませんでした。

     

    public partial class Form1 : Form
    {
     public Form1()
     {
      InitializeComponent();
    
      DirTreeViewNode.Set(this.treeView1);
     }
    }
    
    /// <summary>TreeViewにディレクトリ構造を表示させるクラス</summary>
    public class DirTreeViewNode : TreeNode
    {
     /// <summary>表示させたいTreeViewをSetする</summary>
     /// <param name="tv">表示させたいTreeView</param>
     public static void Set(TreeView tv)
     {
      tv.Nodes.Clear();
      foreach (System.IO.DriveInfo drv in System.IO.DriveInfo.GetDrives())
      {
       DirTreeViewNode node = new DirTreeViewNode(new DirectoryParent(drv));
       node.LoadDir();
       tv.Nodes.Add(node);
      }
    
      //ツリービューのノードを開くときに下位ディレクトリ構造を読み込ませる
      tv.AfterExpand += DirTreeViewNode.TreeeViewAfterExpand;
      tv.CheckBoxes = true;
     }
    
     static void tv_BeforeExpand(object sender, TreeViewCancelEventArgs e)
     {
      throw new Exception("The method or operation is not implemented.");
     }
    
     protected DirTreeViewNode(DirectoryParent directoryparent)
     {
      this.directory = directoryparent;
      this.Text = directoryparent.Name;
     }
    
     public DirectoryParent Directory
     {
      get { return directory; }
     }
     private DirectoryParent directory;
    
     public override string ToString()
     {
      return directory.ToString();
     }
    
     protected void LoadDir()
     {
      LoadDir(this);
     }
    
     private static void LoadDir(DirTreeViewNode node)
     {
      foreach (DirectoryParent child in node.Directory.ChildDirectories)
      {
       DirTreeViewNode childNode;
       childNode = new DirTreeViewNode(child);
    
       node.Nodes.Add(childNode);
    
      }
     }
    
     /// <summary>ツリービューのノードを開くときに下位ディレクトリ構造を読みとるクラス</summary>
     private static void TreeeViewAfterExpand(object sender, TreeViewEventArgs e)
     {
      if (e.Action == TreeViewAction.Expand)
      {
       foreach (TreeNode childNode in e.Node.Nodes)
       {
        if (childNode.Nodes.Count == 0)
        {//下位ノードがまだディレクトリを読み取ってなかったら読み取る
         DirTreeViewNode node = childNode as DirTreeViewNode;
         if (node != null)
         {
          node.LoadDir();
         }
        }
       }
      }
     }
    }
    
    /// <summary>ディレクトリを読み取り用</summary>
    public class DirectoryParent
    {
     public DirectoryParent(System.IO.DirectoryInfo dir)
     {
      this.dir = dir;
      this.Name = dir.Name;
     }
     public DirectoryParent(System.IO.DriveInfo drv)
      : this(drv.RootDirectory)
     {
      this.Name = drv.Name;
     }
    
     System.IO.DirectoryInfo dir;
    
     private System.IO.DirectoryInfo[] GetDirectories()
     {
      try
      {
       return dir.GetDirectories();
      }
      catch (Exception)
      {
       return new System.IO.DirectoryInfo[] { };
      }
     }
     private System.IO.FileInfo[] GetFiles()
     {
      try
      {
       return dir.GetFiles();
      }
      catch (Exception)
      {
       return new System.IO.FileInfo[] { };
      }
     }
    
     private void UpdateDir()
     {
      if (_Directories == null)
      {
       _Directories = new List<DirectoryParent>();
    
       foreach (System.IO.DirectoryInfo di in GetDirectories())
       {
        _Directories.Add(new DirectoryParent(di));
       }
      }
     }
    
     private void UpdateFiles()
     {
      if (_Directories == null)
      {
       List<string> files = new List<string>();
    
       foreach (System.IO.FileInfo fi in GetFiles())
       {
        files.Add(fi.Name);
       }
       _Files = files.ToArray();
      }
     }
    
     public IList<DirectoryParent> ChildDirectories
     {
      get
      {
       UpdateDir();
       return _Directories;
      }
     }
     List<DirectoryParent> _Directories = null;
    
     public string[] Files
     {
      get
      {
       UpdateFiles();
       return _Files;
      }
     }
     private string[] _Files;
    
     public string Name
     {
      get { return _Name; }
      private set { _Name = value; }
     }
     private string _Name;
    
     public override string ToString()
     {
      return Name;
     }
    }
    
    
    今度はVS2005でコンパイルできることを確認したので大丈夫のはず。

    • 回答としてマーク unlonghorn 2011年7月25日 4:55
    2011年7月22日 3:32

すべての返信

  • 一度にディレクトリ階層深くまで調べると時間がかかるので、必要になったらディレクトリ情報を取得しに行くと割と速くなりますよ。
    #バックグランドワーカーなどでスレッド別けするともっと速くできます。

    ようは、TreeViewだとノードを閉じていて見えない部分は読み込んでおく必要は無く、表示するときに初めてディレクトリ情報が必要になるので、それまで読み取りを遅延させるだけです。

    1階層先読を適当に書いてみた。

    public partial class Form1 : Form
    {
      public Form1()
      {
        InitializeComponent();
    
        DirTreeViewNode.Set(this.treeView1);
      }
    }
    
    /// <summary>TreeViewにディレクトリ構造を表示させるクラス</summary>
    public class DirTreeViewNode : TreeNode
    {
      /// <summary>表示させたいTreeViewをSetする</summary>
      /// <param name="tv">表示させたいTreeView</param>
      public static void Set(TreeView tv)
      {
        tv.Nodes.Clear();
        foreach (System.IO.DriveInfo drv in System.IO.DriveInfo.GetDrives())
        {
          var node = new DirTreeViewNode(new DirectoryParent(drv));
          node.LoadDir();
          tv.Nodes.Add(node);
        }
    
        //ツリービューのノードを開くときに下位ディレクトリ構造を読み込ませる
        tv.AfterExpand += DirTreeViewNode.TreeeViewBeforeExpand;
        tv.CheckBoxes = true;
      }
    
      protected DirTreeViewNode(DirectoryParent directoryparent)
      {
        this.directory = directoryparent;
        this.Text = directoryparent.Name;
      }
      
      public DirectoryParent Directory
      {
        get { return directory; }
      }
      private DirectoryParent directory;
    
      public override string ToString()
      {
        return directory.ToString();
      }
    
      protected void LoadDir()
      {
        LoadDir(this);
      }
    
      private static void LoadDir(DirTreeViewNode node)
      {
        foreach (DirectoryParent child in node.Directory.ChildDirectories)
        {
          DirTreeViewNode childNode;
          childNode = new DirTreeViewNode(child);
          
          node.Nodes.Add(childNode);
    
        }
      }
    
      /// <summary>ツリービューのノードを開くときに下位ディレクトリ構造を読みとるクラス</summary>
      private static void TreeeViewBeforeExpand(object sender, TreeViewEventArgs e)
      {
        if (e.Action == TreeViewAction.Expand)
        {
          foreach (TreeNode childNode in e.Node.Nodes)
          {
            if (childNode.Nodes.Count == 0)
            {//下位ノードがまだディレクトリを読み取ってなかったら読み取る
              DirTreeViewNode node = childNode as DirTreeViewNode;
              if (node != null)
              {
                node.LoadDir();
              }
            }
          }
    
        }
      }
    }
    
    /// <summary>ディレクトリを読み取り用</summary>
    public class DirectoryParent
    {
      public DirectoryParent(System.IO.DirectoryInfo dir)
      {
        parent = dir;
    
        GetDirectories = new Func<System.IO.DirectoryInfo[]>(() =>
        {
          try
          {
            return dir.GetDirectories();
          }
          catch (Exception)
          {
            return new System.IO.DirectoryInfo[] { };
          }
        });
    
        GetFiles = new Func<System.IO.FileInfo[]>(() =>
        {
          try
          {
            return dir.GetFiles();
          }
          catch (Exception)
          {
            return new System.IO.FileInfo[] { };
          }
        });
        this.Name = dir.Name;
      }
      public DirectoryParent(System.IO.DriveInfo drv)
        : this(drv.RootDirectory)
      {
        this.Name = drv.Name;
      }
    
      private object parent;
      private Func<System.IO.DirectoryInfo[]> GetDirectories;
      private Func<System.IO.FileInfo[]> GetFiles;
    
      private void UpdateDir()
      {
        if (_Directories == null)
        {
          _Directories = new List<DirectoryParent>();
    
          foreach (System.IO.DirectoryInfo di in GetDirectories())
          {
            _Directories.Add(new DirectoryParent(di));
          }
        }
      }
    
      private void UpdateFiles()
      {
        if (_Directories == null)
        {
          var files = new List<string>();
    
          foreach (System.IO.FileInfo fi in GetFiles())
          {
            files.Add(fi.Name);
          }
          _Files = files.ToArray();
        }
      }
    
      public IList<DirectoryParent> ChildDirectories
      {
        get
        {
          UpdateDir();
          return _Directories;
        }
      }
      List<DirectoryParent> _Directories = null;
    
      public string[] Files
      {
        get
        {
          UpdateFiles();
          return _Files;
        }
      }
      private string[] _Files;
    
      public string Name{get;private set;}
    
    
    
      public override string ToString()
      {
        return Name;
      }
    }
    
    
    

    #ネットワークドライブとかセキュリティで読み取り不可とかは考慮してません。

     

    2011年7月21日 12:48
  • gekka 様

    ご回答頂きましてありがとうございます。

    ソースコードをこんなに書いて頂いて大変感謝いたしております。

    すみません、1点、ソースコードの内容について質問させてください。

    DirectoryParent クラス の中で

    GetDirectories = new Func<System.IO.DirectoryInfo[]>(() = >; や、

    GetFiles = new Func<System.IO.FileInfo[]>(() = >; の最後のところでエラーとなり

    修正方法がどうしてもわからなかったので教えていただけますでしょうか。

    エラー内容は、

    1.')' は 無効です。

    2.'>' は無効です。

    3.) が必要です。

    4.')' は無効です。

    7.型または名前空間名Funcが見つかりませんでした。usingディレクティブまたはアセンブリ参照が不足しています。

    となり、Func< System ・・・ (() = >; あたりでエラーとなっているのですが、どのように修正したらよいのでしょうか。

    ご教授よろしくお願い致します。

     

     

     

     

    2011年7月22日 2:19
  • "() => " の構文は C# 3.0 (Visual C# 2008) で導入された「ラムダ式」というもので、残念ながら unlonghorn さんの環境では使えません。
    「匿名メソッド」を使えば同じことができるはずですので、そのように書き換えるのがよろしいかと思います。

    匿名メソッド (C#)
    http://msdn.microsoft.com/ja-jp/library/0yw3tz5k(VS.80).aspx

    匿名メソッドをラムダ式に書き換える記事は結構見かけますので、その逆をすればよろしいかと。 
    "() =>" とある部分を "delegate()" に置き換えるのかな。未検証ですが。
    2011年7月22日 2:53
  • ごめんなさい、VS2005なのを見落としてました。
    暗黙の型(var),ラムダ式,自動実装プロパティ,Func<T>は2008からしか使えませんでした。

     

    public partial class Form1 : Form
    {
     public Form1()
     {
      InitializeComponent();
    
      DirTreeViewNode.Set(this.treeView1);
     }
    }
    
    /// <summary>TreeViewにディレクトリ構造を表示させるクラス</summary>
    public class DirTreeViewNode : TreeNode
    {
     /// <summary>表示させたいTreeViewをSetする</summary>
     /// <param name="tv">表示させたいTreeView</param>
     public static void Set(TreeView tv)
     {
      tv.Nodes.Clear();
      foreach (System.IO.DriveInfo drv in System.IO.DriveInfo.GetDrives())
      {
       DirTreeViewNode node = new DirTreeViewNode(new DirectoryParent(drv));
       node.LoadDir();
       tv.Nodes.Add(node);
      }
    
      //ツリービューのノードを開くときに下位ディレクトリ構造を読み込ませる
      tv.AfterExpand += DirTreeViewNode.TreeeViewAfterExpand;
      tv.CheckBoxes = true;
     }
    
     static void tv_BeforeExpand(object sender, TreeViewCancelEventArgs e)
     {
      throw new Exception("The method or operation is not implemented.");
     }
    
     protected DirTreeViewNode(DirectoryParent directoryparent)
     {
      this.directory = directoryparent;
      this.Text = directoryparent.Name;
     }
    
     public DirectoryParent Directory
     {
      get { return directory; }
     }
     private DirectoryParent directory;
    
     public override string ToString()
     {
      return directory.ToString();
     }
    
     protected void LoadDir()
     {
      LoadDir(this);
     }
    
     private static void LoadDir(DirTreeViewNode node)
     {
      foreach (DirectoryParent child in node.Directory.ChildDirectories)
      {
       DirTreeViewNode childNode;
       childNode = new DirTreeViewNode(child);
    
       node.Nodes.Add(childNode);
    
      }
     }
    
     /// <summary>ツリービューのノードを開くときに下位ディレクトリ構造を読みとるクラス</summary>
     private static void TreeeViewAfterExpand(object sender, TreeViewEventArgs e)
     {
      if (e.Action == TreeViewAction.Expand)
      {
       foreach (TreeNode childNode in e.Node.Nodes)
       {
        if (childNode.Nodes.Count == 0)
        {//下位ノードがまだディレクトリを読み取ってなかったら読み取る
         DirTreeViewNode node = childNode as DirTreeViewNode;
         if (node != null)
         {
          node.LoadDir();
         }
        }
       }
      }
     }
    }
    
    /// <summary>ディレクトリを読み取り用</summary>
    public class DirectoryParent
    {
     public DirectoryParent(System.IO.DirectoryInfo dir)
     {
      this.dir = dir;
      this.Name = dir.Name;
     }
     public DirectoryParent(System.IO.DriveInfo drv)
      : this(drv.RootDirectory)
     {
      this.Name = drv.Name;
     }
    
     System.IO.DirectoryInfo dir;
    
     private System.IO.DirectoryInfo[] GetDirectories()
     {
      try
      {
       return dir.GetDirectories();
      }
      catch (Exception)
      {
       return new System.IO.DirectoryInfo[] { };
      }
     }
     private System.IO.FileInfo[] GetFiles()
     {
      try
      {
       return dir.GetFiles();
      }
      catch (Exception)
      {
       return new System.IO.FileInfo[] { };
      }
     }
    
     private void UpdateDir()
     {
      if (_Directories == null)
      {
       _Directories = new List<DirectoryParent>();
    
       foreach (System.IO.DirectoryInfo di in GetDirectories())
       {
        _Directories.Add(new DirectoryParent(di));
       }
      }
     }
    
     private void UpdateFiles()
     {
      if (_Directories == null)
      {
       List<string> files = new List<string>();
    
       foreach (System.IO.FileInfo fi in GetFiles())
       {
        files.Add(fi.Name);
       }
       _Files = files.ToArray();
      }
     }
    
     public IList<DirectoryParent> ChildDirectories
     {
      get
      {
       UpdateDir();
       return _Directories;
      }
     }
     List<DirectoryParent> _Directories = null;
    
     public string[] Files
     {
      get
      {
       UpdateFiles();
       return _Files;
      }
     }
     private string[] _Files;
    
     public string Name
     {
      get { return _Name; }
      private set { _Name = value; }
     }
     private string _Name;
    
     public override string ToString()
     {
      return Name;
     }
    }
    
    
    今度はVS2005でコンパイルできることを確認したので大丈夫のはず。

    • 回答としてマーク unlonghorn 2011年7月25日 4:55
    2011年7月22日 3:32
  • totojo 様

    ご回答ありがとうございます。

    C#2005では、C#3.0で導入された =>(ラムダ式)は使えないので、匿名メソッド(delegate)に書き換える必要があるという事ですね。

    勉強不足で、ラムダ式という手法そのものを知りませんでした。

    大変参考になりました。

    有難うございました。

     

     

    2011年7月22日 5:33
  • gekka 様

    本当にありがとうございます。

    で、で、できました!! フォルダにチェックできるウィンドウが。。

    しかも、子ノードの表示もとても速いです。

    あとは、選択したフォルダの情報を取得(フルアドレス)できるように作るだけです。

    最初の投稿からものの数時間でご返答頂き、さらに求めていた作りと同じものをサンプルとして教えて頂きまして

    大変感謝致しております。

    C#2008から使えるラムダ式についても勉強になりました。

    ありがとうございました。

     

     

     

    2011年7月22日 5:51
  • 最初の投稿からものの数時間でご返答頂き、さらに求めていた作りと同じものをサンプルとして教えて頂きまして
    大変感謝致しております。  

    今回に限らず、サンプルコードを使って何らかのソフトウェアを開発し、それを頒布しようと考えるのであれば、著作権についても気にしてくださいね。

    # 手元で実験するだけなら大きな問題にはならないと思いますが、念のため。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年7月22日 14:27
    モデレータ
  • 今回に限らず、サンプルコードを使って何らかのソフトウェアを開発し、それを頒布しようと考えるのであれば、著作権についても気にしてくださいね。
    注意の書き込みありがとうございます。
    私はコード付の投稿をよくしますので、今後は都度お手間をかけていただくことが無いように署名に注意書きを入れることにしました。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
    2011年7月23日 14:37