トップ回答者
C#でフォルダにチェックボックスが付いているツリービューを作成したいです

質問
-
C#2005にて、フォルダ選択ダイアログのようなウィンドウを表示させ、フォルダの横にチェックボックスがありチェックしてOKボタンを押すと、チェックしたフォルダのフルパスなどの情報を取得できるような言うなれば、”チェックボックス付エクスプローラ”のようなウィンドウを作りたいと思っております。
↓こちらのサンプルプログラムを参考に作成してみましたが、ドライブが多かったり、階層が深いとすごく時間がかかってしまいます。
http://www.apfield.jp/csgarden/treeview.html // C#の庭園(ドライブのディレクトリ情報をツリービューとして表示する)
何か良い方法をご存知方がいらっしゃいましたら、教えてください。
よろしくお願いいたします。
回答
-
ごめんなさい、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; } }
- 回答としてマーク unlonghorn 2011年7月25日 4:55
すべての返信
-
一度にディレクトリ階層深くまで調べると時間がかかるので、必要になったらディレクトリ情報を取得しに行くと割と速くなりますよ。
#バックグランドワーカーなどでスレッド別けするともっと速くできます。ようは、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; } }
#ネットワークドライブとかセキュリティで読み取り不可とかは考慮してません。 -
gekka 様
ご回答頂きましてありがとうございます。
ソースコードをこんなに書いて頂いて大変感謝いたしております。
すみません、1点、ソースコードの内容について質問させてください。
DirectoryParent クラス の中で
GetDirectories = new Func<System.IO.DirectoryInfo[]>(() = >; や、
GetFiles = new Func<System.IO.FileInfo[]>(() = >; の最後のところでエラーとなり
修正方法がどうしてもわからなかったので教えていただけますでしょうか。
エラー内容は、
1.')' は 無効です。
2.'>' は無効です。
3.) が必要です。
4.')' は無効です。
・
・
7.型または名前空間名Funcが見つかりませんでした。usingディレクティブまたはアセンブリ参照が不足しています。
となり、Func< System ・・・ (() = >; あたりでエラーとなっているのですが、どのように修正したらよいのでしょうか。
ご教授よろしくお願い致します。
-
"() => " の構文は C# 3.0 (Visual C# 2008) で導入された「ラムダ式」というもので、残念ながら unlonghorn さんの環境では使えません。
「匿名メソッド」を使えば同じことができるはずですので、そのように書き換えるのがよろしいかと思います。
匿名メソッド (C#)
http://msdn.microsoft.com/ja-jp/library/0yw3tz5k(VS.80).aspx
匿名メソッドをラムダ式に書き換える記事は結構見かけますので、その逆をすればよろしいかと。
"() =>" とある部分を "delegate()" に置き換えるのかな。未検証ですが。 -
ごめんなさい、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; } }
- 回答としてマーク unlonghorn 2011年7月25日 4:55