【VC#】TreeNodeとFlowLayoutPanelを使用してWin7エクスプローラを再現したい
解決済
回答 2
投稿
- 評価
- クリップ 2
- VIEW 4,330
今回はMicrosoft Visual Studio Express 2013 for Windows Desktopを使用して
Windows 7 のエクスプローラーの再現をC#で試みています。
そこでTreeNodeの操作の仕方が良く分からず、早くから詰まってしまいました。
以下、再現出来なかったことをできるようにするためには、ソースのどの部分を直せばいいのでしょうか?
■今のところ再現したいこと
Windows7のフォルダツリーとアドレスバーの動作(と、選択フォルダ配下画像の表示)
■再現出来たこと
フォルダツリー表示(TreeNode)
アドレスバーの表示(FlowLayoutPanelにフォルダ名が表示されるButtonをAdd)
フォルダツリーのみでディレクトリ変更(上の階層に行く)、アドレスバー更新
画像の表示
■再現出来なかったこと(今回解決したいこと)
フォルダツリーでディレクトリ変えた後アドレスバーでディレクトリ変更(上の階層に行く)
→System.IndexOutOfRangeException発生
その前には以下のことをしています。Findしても検索結果がゼロになってしまいます。
◯押されたButtonのTextプロパティを取得
(アドレスバーにButtonを追加する時、Textプロパティにフルパスを入れておいた)
◯取得したフルパスをキーにフォルダツリーからNodeを検索
→ここで検索結果ゼロ
◯検索されたNodeをSelectedNodeに追加しようとするも、配列に何もないため例外発生
→TreeNodeのSelectedNodeプロパティにTreenodeを代入してもnullのまま
「folderTree.SelectedNode」で現在TreeNodeで選択されているNodeを取得し
上にさかのぼったりする処理をしたいのですが、
「folderTree.Nodes.Clear();」で「folderTree.SelectedNode」がnullになることに気づき、
以下処理を試してみましたが、folderTree.SelectedNodeは更新されずnullのままでした。
TreeNode tmp = folderTree.SelectedNode; //現在のSelectedNodeを一時保管
folderTree.Nodes.Clear(); //TreeNodeを初期化→folderTree.SelectedNodeがnull
updateFolderTree(currentSelectedNode); //TreeNode追加
folderTree.SelectedNode = tmp; //一時保管していたNodeを再格納
■エラーが起きる具体例
①起動時のフォルダツリーの状態
+ C:\
+ D:\
②フォルダツリーで「C:\」「ProgramFiles」と選択
③アドレスバーは「[C:\] [Program Files] [ProgramFiles配下のフォルダ]」と表示され
フォルダツリーが以下の状態になる
- C:\
+ ProgramFiles配下のフォルダ
+ C:\配下の他フォルダ
+ D:\
④アドレスバーの[C:\] [Program Files] [TortoiseSVN]のいずれかを押す
→UpdateTreeViewイベントが発生
→System.IndexOutOfRangeExceptionが発生して処理中断
■ソース
※プロパティの名前
folderTree…フォルダツリー
addressBar…アドレスバー
picturePanel…サムネイル表示画面
processBar…プロセスバー
public partial class Form1 : Form
{
private String currentDirectoryPath;
private TreeNode folderTreeNode;
public Form1()
{
InitializeComponent();
initialFolderTree();
}
#region Event
private void folderTree_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
updateFolderTree(e.Node);
}
private void folderTree_AfterSelect(object sender, TreeViewEventArgs e)
{
updateFolderTree(e.Node);
}
private void update_formSize(object sender, EventArgs e)
{
Size size = Form1.ActiveForm.Size;
split_middle.Size = new Size(size.Width, size.Height - 80);
}
private void picturePanel_enterMouse(object sender, EventArgs e)
{
picturePanel.Focus();
}
private void folderTree_enterMouse(object sender, EventArgs e)
{
folderTree.Focus();
}
/*
* ツリービューの更新(アドレスバーのButtonを押下した際に発生)
*/
private void folderTree_Update(Object sender, EventArgs e)
{
TreeNode currentSelectedNode = null;
//アドレスバーを連続で操作するとSelectedNodeがnullになりNullReferenceExceptionが発生
if (folderTree.SelectedNode != null)
{
currentSelectedNode = folderTree.SelectedNode;
}
else
{
currentSelectedNode = folderTreeNode;
}
initialFolderTree();
updateFolderTree(currentSelectedNode);
//以下処理をやってもSelectedNodeは変わらずnullになる
//TreeNode tmp = folderTree.SelectedNode; //現在のSelectedNodeを一時保管
//folderTree.Nodes.Clear(); //TreeNodeを初期化
//updateFolderTree(currentSelectedNode); //TreeNode追加
//folderTree.SelectedNode = tmp; //一時保管していたNodeを再格納
//
String key = currentDirectoryPath.ToString().Replace("\\\\", "\\");
//↓↓↓↓↓↓↓↓↓↓↓↓↓↓エラー発生場所↓↓↓↓↓↓↓↓↓↓↓↓↓↓
//TreeNode[] nextSelectedNode = folderTree.Nodes.Find(key, true); だとnameプロパティで探す→検索結果はゼロ
//※以下サイトを参考にTextプロパティで探せるようにしても検索結果はゼロ
//「Is there a method for searching for TreeNode.Text field in TreeView.Nodes collection?」
//(http://stackoverflow.com/questions/12388249/is-there-a-method-for-searching-for-treenode-text-field-in-treeview-nodes-collec)
TreeNode[] nextSelectedNode = folderTreeNode.Nodes.Cast<TreeNode>().Where(r => r.Text == key).ToArray();
folderTree.SelectedNode = nextSelectedNode[0];
//↑↑↑↑↑↑↑↑↑↑↑↑↑↑エラー発生場所↑↑↑↑↑↑↑↑↑↑↑↑↑↑
String beforePath = currentDirectoryPath.ToString().Replace("\\\\", "\\");
Button clickedButton = sender as Button;
String afterPath = clickedButton.Name.ToString().Replace("\\\\", "\\");
updateAddressBar(afterPath);
if (beforePath != null)
{
String diff = beforePath.Replace(afterPath, "");
int depth = diff.Split('\\').Count();
for (int i = 0; i < depth; i++)
{
if (folderTree.SelectedNode == null)
{
folderTree.SelectedNode = currentSelectedNode;
}
folderTree.SelectedNode = folderTree.SelectedNode.Parent;
}
folderTree.Focus();
}
}
#endregion
/*
*フォルダツリーの初期化(ドライブ一覧)
*/
private void initialFolderTree()
{
folderTree.Nodes.Clear();//SelectedNodeがクリアされる
foreach (String driveName in Environment.GetLogicalDrives())
{
folderTreeNode = new TreeNode(driveName, 0, 0);
folderTreeNode.Name = driveName;
folderTree.Nodes.Add(folderTreeNode);
folderTreeNode.Nodes.Add("...");
}
folderTree.Sorted = true; //文字列順に自動Sort
}
/*
*選択されたノードに応じてフォルダツリーを更新
*/
private void updateFolderTree(TreeNode currentNode)
{
//folderTreeNode = currentNode;
String nodePath = null;
try
{
nodePath = currentNode.FullPath;
currentDirectoryPath = nodePath;
}
catch(InvalidOperationException e)
{
nodePath = currentDirectoryPath;
}
currentNode.Nodes.Clear();
if (System.IO.Directory.Exists(nodePath))
{
DirectoryInfo directoryInfo = new DirectoryInfo(nodePath);
try
{
DirectoryInfo[] directoryList = directoryInfo.GetDirectories();
if (directoryList.Count() != 0)
{
DirectoryInfo[] directoryInfoList = directoryInfo.GetDirectories();
String root = currentDirectoryPath.ToString().Substring(0, 1);
foreach (DirectoryInfo childDirectoryInfo in directoryInfoList)
{
folderTreeNode = new TreeNode(childDirectoryInfo.Name);
folderTreeNode.Name = childDirectoryInfo.FullName;
currentNode.Nodes.Add(folderTreeNode);
folderTreeNode.Nodes.Add("...");
}
}
folderTreeNode = currentNode;
updatePicturePanel(directoryInfo);
updateAddressBar(directoryInfo.FullName);
}
catch (UnauthorizedAccessException){}
}
}
/*
* アドレスバーの更新
*/
private void updateAddressBar(String fullPath)
{
String[] arraypath = fullPath.Replace("\\\\", "\\").Split('\\');
addressBar.Controls.Clear();
currentDirectoryPath = arraypath[0] + '\\';
foreach (String path in arraypath)
{
if (currentDirectoryPath != path + '\\')
{
currentDirectoryPath += ('\\' + path);
}
Button button = new Button();
button.Text = path; //後でフルパスが取得できるようにしておく
button.Click += new EventHandler(folderTree_Update);
button.MinimumSize = new Size(10, 23);
button.MaximumSize = new Size(50, 23);
button.Name = currentDirectoryPath;
addressBar.Controls.Add(button);
}
progressText.Text = fullPath;
}
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
Keyとして使っている(String key = currentDirectoryPath.ToString().Replace("\\\\", "\\");)currentDirectoryPath にはTreeNode のFullPathを使っているにも関わらず、(updateFolderTree内)Nodeを作成するときのNameプロパティにはDirectoryInfoのFullNameを使っているため不一致になっているのでは?
そこを合わせば、Nameプロパティで検索してもFindで引っかかると思いますよ。
検証はしていませんので悪しからず。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
以下のように書きなおした所動くようになりました。
もしかしたらバグ等あるかもしれませんが
厳密にテストした場合エラーが出るかもしれませんが、
他の方の少しでも参考になればと思い、以下、フォルダツリーとアドレスバーに関係ある部分を抜き出しました。
private String currentDirectoryPath;
private TreeNode folderTreeNode;
public Form1()
{
intializeizeComponent();
intializeFolderTree();
}
private void folderTree_Update(Object sender, EventArgs e)
{
Button clickedButton = sender as Button;
TreeNode currentSelectedNode = new TreeNode(clickedButton.Name);
String beforePath = currentDirectoryPath.ToString().Replace("\\\\", "\\");
String afterPath = clickedButton.Name.ToString().Replace("\\\\", "\\");
if (afterPath.Split('\\').Count() == 2) //ルートの場合
{
while (folderTree.SelectedNode.Parent != null) {
folderTree.SelectedNode = folderTree.SelectedNode.Parent;
}
}
else if(beforePath != null && beforePath != afterPath)
{
String diff = beforePath.Replace(afterPath, "");
int depth = diff.Split('\\').Count() -1;
for (int i = 0; i < depth; i++)
{
if (folderTree.SelectedNode == null)
folderTree.SelectedNode = currentSelectedNode;
folderTree.SelectedNode = folderTree.SelectedNode.Parent;
}
}
TreeNode tmp = folderTree.SelectedNode;
updateFolderTree(currentSelectedNode);
folderTree.SelectedNode = tmp;
folderTree.Focus();
}
private void intializeFolderTree()
{
folderTree.Nodes.Clear();
foreach (String driveName in Environment.GetLogicalDrives())
{
folderTreeNode = new TreeNode(driveName, 0, 0);
folderTreeNode.Name = driveName;
folderTree.Nodes.Add(folderTreeNode);
folderTreeNode.Nodes.Add("...");
}
folderTree.Sorted = true;
}
private void updateFolderTree(TreeNode currentNode)
{
TreeNode resetTreeNode = currentNode;
resetTreeNode.Nodes.Clear();
String nodePath = null;
try
{
nodePath = currentNode.FullPath;
currentDirectoryPath = nodePath;
}
catch(InvalidOperationException e)
{
nodePath = currentDirectoryPath;
}
if (System.IO.Directory.Exists(nodePath))
{
DirectoryInfo directoryInfo = new DirectoryInfo(nodePath);
try
{
DirectoryInfo[] subDirectoryInfoList = directoryInfo.GetDirectories();
if (subDirectoryInfoList.Count() != 0)
{
foreach (DirectoryInfo subDirectoryInfo in subDirectoryInfoList)
{
folderTreeNode = new TreeNode(subDirectoryInfo.Name);
folderTreeNode.Name = subDirectoryInfo.FullName;
resetTreeNode.Nodes.Add(folderTreeNode);
folderTreeNode.Nodes.Add("...");
}
}
folderTreeNode = currentNode;
folderTree.SelectedNode = currentNode;
updatePicturePanel(directoryInfo);
updateAddressBar(directoryInfo.FullName);
}
catch (UnauthorizedAccessException){}
}
}
private void updateAddressBar(String fullPath)
{
String[] arraypath = fullPath.Replace("\\\\", "\\").Split('\\');
addressBar.Controls.Clear();
currentDirectoryPath = arraypath[0] + '\\';
foreach (String path in arraypath)
{
if (currentDirectoryPath != path + '\\')
currentDirectoryPath += ('\\' + path);
if (path == "")
continue;
Button button = new Button();
button.Text = path;
button.Click += new EventHandler(folderTree_Update);
button.MinimumSize = new Size(10, 23);
button.MaximumSize = new Size(100, 23);
button.AutoSize = true;
button.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
button.Name = currentDirectoryPath;
addressBar.Controls.Add(button);
}
progressText.Text = fullPath;
}
private void folderTree_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
updateFolderTree(e.Node);
}
private void folderTree_AfterSelect(object sender, TreeViewEventArgs e)
{
updateFolderTree(e.Node);
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
2015/01/14 01:21
ご指摘の通りFullNameにしないとフルパスが格納出来ず
検索出来ないと気づきましたので以下のとおり修正しました。
updateFolderTree(TreeNode)のforeach内
[修正前] folderTreeNode.Name = childDirectoryInfo.Name;
[修正後] folderTreeNode.Name = childDirectoryInfo.FullName;
しかし、相変わらず結果がゼロ件で同じIndexOutOfRangeExceptionになります。
デバッグで例外発生時の変数を確認してみたところ、以下のとおりになっていました。
key →"C:\\Program Files"
folderTreeNode
.Parent →Name="C:\\", Text="C:\\"
.Nodes[0] →Name="C:\\Program Files", Text="Program Files"
.Nodes[0] →Name="C:\\Program Files\\Adobe", Text="Adobe"
※Nodesは他のフォルダ情報も同様に取得出来ています。
これなら結果が返ってきてもいいと思うのですが、Findした結果はゼロです。
そもそもこういった形になっていたら取得出来ないのでしょうか?
AfterSelect(object sender, TreeViewEventArgs e)
BeforeExpand(object sender, TreeViewCancelEventArgs e)
で取得したTreeNodeの扱いがマズイのでしょうか?
お手間とらせてしまい申し訳ございませんが、
もしよろしければ、引き続きご確認いただけると助かります。
以上、よろしくお願いします。
2015/01/14 14:16
結論としてTreeNodeのNameプロパティとKeyとが一致していれば確かにFindで見つかります。
で、monchikenさんのコードをこちらで書いてみましたが、
folderTree_Updateの頭でinitialFolderTree()を呼び出してTreeを作り直していますが、
この部分をコメントアウトしてみると、とりあえず今回の箇所のIndexOutOfRangeExceptionはなくなりました。おそらく作り直すときに思い通りにTreeが作成できていないのでしょう。
ただ上記の修正だけではそのあとの動作が不安定でした。ボタンを押すと固まるなど、いろいろエラーが出ました。(こちらの移植ミスかもしれませんが…)
上の文章を読んでる限り、ご自身の中でも、「folderTreeNode」と「folderTree.Nodes」の扱いがごっちゃになっていませんか?笑
でもあとは想定通りの動きになるようにデバッグを頑張るしかありませんね。
頑張ってください。
2015/01/20 00:03
いっその事一から書きなおしてみればと思い、torakichiさんの仰るところを気をつけながら記述してみたところ、私が操作テストした限りで動くようになりました。
実はTreeNodeの扱いがよく分からずじまいなのですが、今後動かしていく中で学んでいこうと思います。