DOBON.NET DOBON.NETプログラミング掲示板過去ログ

treeviewでルートフォルダを表示させるには?

環境/言語:[WindowsXP/VB.NET2002]
分類:[.NET]

こんばんわ。
VB初心者です。
.NET2002でフォルダエクスプローラを作成しているのですが、
ノードを展開するまではできたのですが、treeviewの中にルートフォルダを表示することができません。下のコードを変えたらいいんだとおもうんですけど、うまくできません…
コメントアウトをしているほうを実行すると、ノードが二重になってでてきます。
質問の内容が漠然として申し訳ないのですが、どうか教えていただけないでしょうか?

AddHandler treeFolders.AfterSelect, AddressOf treeFolders_AfterSelect
'Dim a As TreeNode
'a = treeFolders.Nodes.Add(SFRootPath)

For countSubFolders = 0 To subFolders.Length - 1
treeFolders.Nodes.Add(subFolders(countSubFolders).Replace(SFRootPath, ""))

'a.Nodes.Add(subFolders(countSubFolders).Replace(SFRootPath, ""))
Next

Call dispMenbersFiles()
■No6571に返信(はなさんの記事)
> コメントアウトをしているほうを実行すると、ノードが二重になってでてきます。

はじめまして。
6、8行目はAddメソッドの引数が同じことから、どちらかを使うのだと仮定すると、
2、3行目と8行目のコメントをはずして、
6行目をコメントにすれば、うまく表示されると思うのですが。
6、8行目はノードを追加している『位置(親ノード)』が違うだけで、
同じことをしていますから、二重に表示されるのでは?

また、
>treeviewの中にルートフォルダを表示することができません。
とありますが、
3行目と6行目はともに『ルートノード』を追加しています。
ルートノードは必ずしも一つとは限りません。
TreeView1.Nodes.Add("ルート")
とするのがルートノードの追加方法、
TreeView1.Nodes(0).Nodes.Add("子ノード")
とするのが子ノードの追加方法。

コードを見れば
3、6行目がルートノードを追加していて、
8行目はこの『TreeView1.Nodes(0)』が『a』になってるだけ、
つまり3行目で追加したルートノードに子ノードを追加していると判断できます。
rakiさん、はじめまして!
教えていただいてありがとうございます。

> 6、8行目はAddメソッドの引数が同じことから、どちらかを使うのだと仮定すると、
> 2、3行目と8行目のコメントをはずして、
> 6行目をコメントにすれば、うまく表示されると思うのですが。
> 6、8行目はノードを追加している『位置(親ノード)』が違うだけで、
> 同じことをしていますから、二重に表示されるのでは?
何回もコメントをはずしたり、コメントにしたりして繰り返しているのですが、ツリーで選択した所までのフルパス取得の時にエラーがでてきます。
フルパスの取得では、こういうコードを使っています。
If treeFolders.SelectedNode Is Nothing Then
Path = SFRootPath
Else
Path = SFRootPath & treeFolders.SelectedNode.FullPath
End If


fName = Dir(Path)←いつもここでエラーになります…


ノードの追加方法も教えていただいてありがとうございました!!
それと、rakiさんのHPからサンプルを拝見させていただきました。論理ディレクトリからのツリーフォルダでしたね。私はルートパスを指定してツリーフォルダを作成しています。参考書などに載っているサンプルも見るのですが、やはり論理ディレクトリから取得しているものです。指定する場合と論理ディレクトリから取得する場合ではどこが違っているのでしょうか、、、どこを変えたらいいかも分からない状態です。。。
■No6608に返信(はなさんの記事)
> 何回もコメントをはずしたり、コメントにしたりして繰り返しているのですが、ツリーで選択した所までのフルパス取得の時にエラーがでてきます。
> フルパスの取得では、こういうコードを使っています。
> If treeFolders.SelectedNode Is Nothing Then
> Path = SFRootPath
> Else
> Path = SFRootPath & treeFolders.SelectedNode.FullPath
> End If
>
>
> fName = Dir(Path)←いつもここでエラーになります…

Dir関数に渡しているPathの中身は正常なパス文字列ですか?
また、エラーになるとのことですが、エラーの内容はなんでしょうか?


> それと、rakiさんのHPからサンプルを拝見させていただきました。
>論理ディレクトリからのツリーフォルダでしたね。
>私はルートパスを指定してツリーフォルダを作成しています。
>参考書などに載っているサンプルも見るのですが、
>やはり論理ディレクトリから取得しているものです。
>指定する場合と論理ディレクトリから取得する場合ではどこが違っているのでしょうか、、、
>どこを変えたらいいかも分からない状態です。。。

特に違いはありません。
ルートノードにドライブを指定しているか、フォルダを指定しているかで、
追加したルートノードに子ノードを追加していく処理は同じのはずです。

私のサイトのサンプルの場合、ドライブのノードのテキストはパスではないので、
少々違ってきますが。
■No6608に返信(はなさんの記事)
>それと、rakiさんのHPからサンプルを拝見させていただきました。
>論理ディレクトリからのツリーフォルダでしたね。
>私はルートパスを指定してツリーフォルダを作成しています。
>参考書などに載っているサンプルも見るのですが、
>やはり論理ディレクトリから取得しているものです。
>指定する場合と論理ディレクトリから取得する場合ではどこが違っているのでしょうか、、、
>どこを変えたらいいかも分からない状態です。。。

私のサイトのサンプルページに『フォルダツリークラス3』という名前で
指定したパス以下をツリー表示するクラスを公開しました。
相変わらずVB.NET2003で申し訳ないのですが・・・。
参考までに。
rakiさん、お早い返信本当にどうもありがとうございます!!

>Dir関数に渡しているPathの中身は正常なパス文字列ですか?
>また、エラーになるとのことですが、エラーの内容はなんでしょうか?

pathの中身はあってると思っているのですが…
エラーの内容は

System.NotSupportedException' のハンドルされていない例外が mscorlib.dll で発生しました。

追加情報 : 指定されたパスのフォーマットはサポートされていません。

というエラーが出てきます。

>私のサイトのサンプルページに『フォルダツリークラス3』という名前で
>指定したパス以下をツリー表示するクラスを公開しました。
>相変わらずVB.NET2003で申し訳ないのですが・・・。
>参考までに。

サンプル公開していただいて、どうもありがとうございます!
サンプルを参考にしっかり考え直したいと思います。
■No6615に返信(はなさんの記事)
> エラーの内容は
>
> System.NotSupportedException' のハンドルされていない例外が mscorlib.dll で発生しました。
>
> 追加情報 : 指定されたパスのフォーマットはサポートされていません。
>
> というエラーが出てきます。

エラー情報からすると、パス文字列が不正という可能性がありますが、
パス文字列が正常、かつそのディレクトリが存在するのであれば、
Dir関数ではアクセスできないパスを指定しているか、
ネットワークパスだったり特殊フォルダだったり・・・

そもそも、
>fName = Dir(Path)
はどのような理由で使われているのでしょうか?
他の関数で代用できませんか?
こんにちは。

> エラー情報からすると、パス文字列が不正という可能性がありますが、
> パス文字列が正常、かつそのディレクトリが存在するのであれば、
> Dir関数ではアクセスできないパスを指定しているか、
> ネットワークパスだったり特殊フォルダだったり・・・
このエラーは文字列が不正だったことでした。。

> そもそも、
> >fName = Dir(Path)
> はどのような理由で使われているのでしょうか?
> 他の関数で代用できませんか?
ファイルを選択したら、ファイル名、ファイルのサイズを表示するプログラムを作成しているので、ここで文字列に変えています。

子ノードがひとつしか展開できません。子ノードを展開していくときにはrakiさんのサンプルコードのようにダミーをつくらなければならないのですか?
あと、サンプルコードの.keyとはどういう使い方ですか?2002でもできるのでしょうか?
こんにちは。

>fName = Dir(Path)
のエラー問題は解決したものと思って宜しいでしょうか?


> 子ノードがひとつしか展開できません。子ノードを展開していくときにはrakiさんのサンプルコードのようにダミーをつくらなければならないのですか?

私のサンプルでは、初回表示までの速度を上げるため、
画面に表示されるノード分しか情報を取得しておりません。
このため、サブフォルダのあるノードの場合、[+]マークを表示させるために、
ダミーノードを子ノードとして追加しております。

そして、このノードが展開されるときに初めて
サブフォルダの情報を取得し、ダミー子ノードと差し替えて追加、表示しております。
これはテクニックの一つであり、必ずこうしなければいけないわけではありません。


>子ノードがひとつしか展開できません。

展開してもダミーノードしかない、という意味であれば、
展開前に行っているダミーノードの削除とサブフォルダノードの追加が
できていないのではないでしょうか。


> あと、サンプルコードの.keyとはどういう使い方ですか?2002でもできるのでしょうか?

私のサンプルの場合、ドライブの表示が『C:\』というパスではなく、
『ローカルディスク(C:)』という[表示名]になっているため、
一般的な TreeView1.SelectedNode.FullPath という方法で
フルパスを取得することができません。
(ローカルディスク(C:)\AAA\BBB\CCC となってしまう)
これを回避する為、フルパスを格納しておく.Keyプロパティを追加したのが
サンプルクラスの一番下にある『FolderTreeNode』という自作クラスです。

このクラスは.NET FrameworkのTreeNodeクラスを継承して作ったものですから、
2002でも問題なく使用できると思います。
頑張って下さい。
返信ありがとうございます。

> >fName = Dir(Path)
> のエラー問題は解決したものと思って宜しいでしょうか?

解決です。どうもありがとうございました。

> 私のサンプルでは、初回表示までの速度を上げるため、
> 画面に表示されるノード分しか情報を取得しておりません。
> このため、サブフォルダのあるノードの場合、[+]マークを表示させるために、
> ダミーノードを子ノードとして追加しております。
>
> そして、このノードが展開されるときに初めて
> サブフォルダの情報を取得し、ダミー子ノードと差し替えて追加、表示しております。
> これはテクニックの一つであり、必ずこうしなければいけないわけではありません。
>

ダミーを使わない方法もあるんですね、今ダミーをまったく使わない方法で進めて、入れ子のように子ノードをどんどん追加していきたいのですが、下のようなコードで、ひとつの階層しか奥にいけません。
新しくノードを作ってそのノードに入れ込めばいいというのは分かるのですが、
そのタイミングなど、よく分かりません。やはりダミーを作ったほうがいいのでしょうか?

Private Sub treeFolders_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles treeFolders.AfterSelect
'ツリービューでフォルダが選択される度の処理

Dim subDirs As String()
Dim countSubs As Integer

subDirs = System.IO.Directory.GetDirectories(SFRootPath)

For countSubs = 0 To subDirs.Length - subDirs.Length - 1
e.Node.Nodes.Add(subDirs(countSubs).Replace(SFRootPath & e.Node.FullPath & "\", ""))

Next

Call dispMenbersFiles()

End Sub

> 私のサンプルの場合、ドライブの表示が『C:\』というパスではなく、
> 『ローカルディスク(C:)』という[表示名]になっているため、
> 一般的な TreeView1.SelectedNode.FullPath という方法で
> フルパスを取得することができません。
> (ローカルディスク(C:)\AAA\BBB\CCC となってしまう)
> これを回避する為、フルパスを格納しておく.Keyプロパティを追加したのが
> サンプルクラスの一番下にある『FolderTreeNode』という自作クラスです。
>
> このクラスは.NET FrameworkのTreeNodeクラスを継承して作ったものですから、
> 2002でも問題なく使用できると思います。
> 頑張って下さい。

keyについて分かりました。Fullpathを格納する場所なんですね。
私はTreeView1.SelectedNode.FullPath で取得していきたいと思います。

> ダミーを使わない方法もあるんですね、今ダミーをまったく使わない方法で進めて、
>入れ子のように子ノードをどんどん追加していきたいのですが、
>下のようなコードで、ひとつの階層しか奥にいけません。

For文のTo句の後ろがおかしいです。
>For countSubs = 0 To subDirs.Length - subDirs.Length - 1
ではなく
For countSubs = 0 To subDirs.Length - 1
では?
これではForループ内に入ってきません。


> 新しくノードを作ってそのノードに入れ込めばいいというのは分かるのですが、
> そのタイミングなど、よく分かりません。やはりダミーを作ったほうがいいのでしょうか?

タイミングに関してですが、
起動時に指定フォルダ以下の全ての情報を取得してツリーを表示し、
以降ツリーの再表示は行わない場合は、Form_Load時などに一気に
全てのディレクトリ情報の取得⇒ツリーの作成を行うのが普通。
ノード展開時にそれ以下のディレクトリ情報を取得する等、
ある程度動的にノード追加を行いたい場合は
1.ノード選択時(BeforeSelect、AfterSelectイベント)
2.ノード展開時(BeforeExpand、AfterExpandイベント)
にすぐ下のディレクトリの情報のみ取得してノード追加するのが一般的。

前者の方法の場合、表示されるときには全てのディレクトリ構成を持つ
ツリーが既に完成しているので、理論的にダミーは必要ないはずです。
こちらは表示までに時間がかかりますが、一旦表示された後は展開に
待ち時間が無いというのが利点です。

後者の場合、初回表示までは速いですが、
1や2のタイミングで情報を取得しに行くため、
そのタイミングで多少待ち時間が発生します。
また、サンプルのように2のタイミングでノード追加を行う場合、
『子ノードが無いと展開できない=イベントが発生しない』ため、
ダミーノードを使って子ノードを追加しています。
これも、表示されているノードの1階層下までの情報を取得するようにすれば、
ダミーノードを使う必要はないでしょう。
展開された場合は2階層下までを取得する、ということですね。

前者・後者、また後者でも1・2を使うかはお好み次第です。
ダミーノードも方法によっては使わないこともできますので。
こんにちは。

> For文のTo句の後ろがおかしいです。
> >For countSubs = 0 To subDirs.Length - subDirs.Length - 1
> ではなく
> For countSubs = 0 To subDirs.Length - 1
> では?
> これではForループ内に入ってきません。
前回まで、 For countSubs = 0 To subDirs.Length - 1 でしてたのですが、同じものがルートノードも表示されて二十二でてきたのです。
For countSubs = 0 To subDirs.Length - subDirs.Length - 1 
で行うと、きちんとしたものがでてきたので、今はこれでしています。
どこかで二重になってるんだと思いますが。。。とりあえず今はこれでしていました。

> タイミングに関してですが、
> ↓
> 前者・後者、また後者でも1・2を使うかはお好み次第です。
> ダミーノードも方法によっては使わないこともできますので。

丁寧に教えていただいてありがとうございます。
前者の方法で進めたいと思います。
ダミーを使わず行いたいのですが、子ノードが存在するか、否か判断するには、
rakiさんのサンプルでいうと、
If fullPath.Length = 3 Then
For Each _node As FolderTreeNode In mctlTreeView.Nodes
If _node.Key = fullPath Then
' 子ノードが存在する場合。
If _node.GetNodeCount(True) > 0 Then
' 子ノードがダミーのノードの場合
If StrComp(_node.FirstNode.Text, conDummyName, vbTextCompare) = 0 Then
' ダミーのノードがあれば一旦削除。
_node.Nodes.Clear()
' ノードを追加
Call AddDirectoryNodes(_node, _node.Key)

' 引数のパスと同じ場合は選択ノードとする
If _node.Key = Value Then
mctlTreeView.SelectedNode = _node
End If

treeNode = _node

Exit For
End If
End If
End If
Next
Else
For Each _node As FolderTreeNode In treeNode.Nodes
If _node.Key = fullPath Then
' 子ノードが存在する場合。
If _node.GetNodeCount(True) > 0 Then
' 子ノードがダミーのノードの場合
If StrComp(_node.FirstNode.Text, conDummyName, vbTextCompare) = 0 Then
' ダミーのノードがあれば一旦削除。
_node.Nodes.Clear()
' ノードを追加
Call AddDirectoryNodes(_node, _node.Key)

' 引数のパスと同じ場合は選択ノードとする
If _node.Key = Value Then
mctlTreeView.SelectedNode = _node
End If

treeNode = _node

Exit For
End If
End If
End If
Next
        End If

の部分でしょうか?
この場合、上のほうのfullPath.Length = 3はどういった処理なのですか?
こんにちは。

> 前回まで、 For countSubs = 0 To subDirs.Length - 1 でしてたのですが、
> 同じものがルートノードも表示されて二十二でてきたのです。
> For countSubs = 0 To subDirs.Length - subDirs.Length - 1 
> で行うと、きちんとしたものがでてきたので、今はこれでしています。
> どこかで二重になってるんだと思いますが。。。とりあえず今はこれでしていました。
>
> 丁寧に教えていただいてありがとうございます。
> 前者の方法で進めたいと思います。

前者の方法で行うのであれば、Form_Load終了時に
完成されたツリーが出来ているはずですので、
それ以降イベント発生時などにノードの追加は必要ないです。


> ダミーを使わず行いたいのですが、子ノードが存在するか、否か判断するには、
> rakiさんのサンプルでいうと、
> ---コード省略---
> の部分でしょうか?

正解です。
が、このコードの記述されている Path プロパティは
obj.Path = "C:\ABC\XYZ\"
などとした場合に、
"C:\ABC\XYZ\"のノードを選択状態にするということをしてるだけですので、
無くても問題ない部分ではあります。
また後述のようにバグ有りなので、無視して下さい。

まずForm_Load時に一気にツリーを完成させる部分を
作りこんでください。、
これには再帰処理が必要になると思いますが、
再帰に関しては分かりましたか?


> この場合、上のほうのfullPath.Length = 3はどういった処理なのですか?

"C:\"というパスを示すノードはルートノードのはずですので、
TreeView1.Nodeコレクション内で検索しています。
"C:\ABC\"というパスを示すノードはTreeView.Nodeコレクション内の
"C:\"を示すTreeNodeのNodeコレクション内にあるはずですので、
TreeNode.Nodeコレクション内で検索しています。
ドライブノードの場合はTreeViewクラスが対象に、
フォルダノードの場合はTreeNodeクラスが対象になりますので、
パスがドライブかどうかを判断する為に、
3文字か否か、と見ています。

が、サンプルの『フォルダツリークラス3』の場合、
ルートノードが必ずしもドライブノードとは限りませんので、
この方法ではまずいです。
実際にルートノードがフォルダの場合に、
Pathプロパティにパスを設定すると落ちることを確認しました。
修正をしますので、参考にはしないで下さい。
こんにちは。

> 前者の方法で行うのであれば、Form_Load終了時に
> 完成されたツリーが出来ているはずですので、
> それ以降イベント発生時などにノードの追加は必要ないです。

> まずForm_Load時に一気にツリーを完成させる部分を
> 作りこんでください。、
> これには再帰処理が必要になると思いますが、
> 再帰に関しては分かりましたか?
すいません。Form_Loadの時にツリーを作成する方法がよくわかりませんでした。
再帰処理とはどういうことですか?
また、後者1,2とありましたが、後者の1だけ使用するというのも可能でしょうか?
1でも、AfterSelectとBeforeSelectは、今AfterSelectのみで作っているのですが、BeforeSelectも必要でしょうか?


> "C:\"というパスを示すノードはルートノードのはずですので、
> TreeView1.Nodeコレクション内で検索しています。
> "C:\ABC\"というパスを示すノードはTreeView.Nodeコレクション内の
> "C:\"を示すTreeNodeのNodeコレクション内にあるはずですので、
> TreeNode.Nodeコレクション内で検索しています。
> ドライブノードの場合はTreeViewクラスが対象に、
> フォルダノードの場合はTreeNodeクラスが対象になりますので、
> パスがドライブかどうかを判断する為に、
> 3文字か否か、と見ています。
>
> が、サンプルの『フォルダツリークラス3』の場合、
> ルートノードが必ずしもドライブノードとは限りませんので、
> この方法ではまずいです。
> 実際にルートノードがフォルダの場合に、
> Pathプロパティにパスを設定すると落ちることを確認しました。
> 修正をしますので、参考にはしないで下さい。
>
ドライブかどうかを判断しているのですね、わかりました。ありがとうございます。
こんにちは。

> すいません。Form_Loadの時にツリーを作成する方法がよくわかりませんでした。
> 再帰処理とはどういうことですか?

プロシージャ内で、自分自身(プロシージャ)をコールすることです。
各ノードが一つの子ノードを持つ10階層ぶんのツリーを作成する
サンプルコードを載せます。フォームにツリービューを一つ貼り付けて
実行して下さい。
------------------------------------------------------------------
Private NodeCount As Integer = 0

Private Sub Form1_Load(
                ByVal sender As System.Object, 
                ByVal e As System.EventArgs) Handles MyBase.Load
    Dim rootNode As TreeNode = New TreeNode("ルートノード")

    AddNode(rootNode)

    TreeView1.Nodes.Add(rootNode)
End Sub

Private Sub AddNode(ByRef node As TreeNode)
    NodeCount += 1
    If NodeCount > 10 Then Exit Sub

    Dim childNode As TreeNode = New TreeNode("子ノード" & NodeCount.ToString)

    AddNode(childNode)

    node.Nodes.Add(childNode)
End Sub
-----------------------------------------------------------------

AddNodeプロシージャ内で再度AddNodeプロシージャをコールしているのが分かりますか?
これが再帰処理(関数)です。
ただ、再帰を行う場合、なんらかの方法で再帰を終える処理を入れておかないと、
永久ループになりますので、注意して下さい。
上記コードでは10回再帰した時点でプロシージャから抜けるようにしてあります。


> また、後者1,2とありましたが、後者の1だけ使用するというのも可能でしょうか?
> 1でも、AfterSelectとBeforeSelectは、今AfterSelectのみで作っているのですが、BeforeSelectも必要でしょうか?

もちろん可能です。
後者の1の場合、
ノードの選択⇒子ノードの作成⇒子ノードの選択⇒子ノードの子ノードの作成
という流れになるので、上記の再帰は必要ありません。
また、BeforeSelectとAfterSelectはユーザがノード選択時に
BeforeSelect⇒AfterSelectの順でイベントが発生します。
つまり、このどちらかに『子ノードの作成』処理を入れればいいので、
片方のみで結構です。お好きなほうをお使い下さい。
サンプルの『mctlTreeView_BeforeExpand』プロシージャを参考にして下さい。
ちなみにここでは、展開されようとするノードが指すフォルダに
サブフォルダがあるかを調べ、あったら子ノードとしてダミー子ノードと差し替え、展開。
なければダミー子ノードを削除して展開をキャンセル。
ということを行っています。
こんばんは。

> プロシージャ内で、自分自身(プロシージャ)をコールすることです。
> 各ノードが一つの子ノードを持つ10階層ぶんのツリーを作成する
> サンプルコードを載せます。フォームにツリービューを一つ貼り付けて
> 実行して下さい。

> AddNodeプロシージャ内で再度AddNodeプロシージャをコールしているのが分かりますか?
> これが再帰処理(関数)です。
> ただ、再帰を行う場合、なんらかの方法で再帰を終える処理を入れておかないと、
> 永久ループになりますので、注意して下さい。
> 上記コードでは10回再帰した時点でプロシージャから抜けるようにしてあります。

再帰処理についてサンプルコードを作って分かりやすく教えていただいて本当にありがとうございます。再帰処理について理解することができました。
前者の方法で進めていこうと頑張っていましたが、再表示とか分からなくなってしまって、後者の1で進めていこうと方法を変えることにしました。
せっかく丁寧に教えていただいたのにすみません。。

> もちろん可能です。
> 後者の1の場合、
> ノードの選択⇒子ノードの作成⇒子ノードの選択⇒子ノードの子ノードの作成
> という流れになるので、上記の再帰は必要ありません。
> また、BeforeSelectとAfterSelectはユーザがノード選択時に
> BeforeSelect⇒AfterSelectの順でイベントが発生します。
> つまり、このどちらかに『子ノードの作成』処理を入れればいいので、
> 片方のみで結構です。お好きなほうをお使い下さい。
> サンプルの『mctlTreeView_BeforeExpand』プロシージャを参考にして下さい。
> ちなみにここでは、展開されようとするノードが指すフォルダに
> サブフォルダがあるかを調べ、あったら子ノードとしてダミー子ノードと差し替え、展開。
> なければダミー子ノードを削除して展開をキャンセル。
> ということを行っています。
>
こちらも分かりやすく教えていただいてありがとうございます。
AfterSelectに子ノードの作成を入れることにしました。
サブフォルダがあるかどうか調べるのは
If Directory.Exists(mNode.Key) = False Then
の部分ですよね。rakiさんのサンプルはコメントもあってとても分かりやすいです。ありがとうございます。
なんとか自分が求めていたツリーを作成することが、できました!
Private Sub treeFolders_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles treeFolders.AfterSelect
'ツリービューでフォルダが選択される度の処理
Dim subDirs As String()
Dim countSubs As Integer
    'ルートパスと同じなら何もしない。
If e.Node.FullPath = SFRootPath Then Exit Sub

subDirs = System.IO.Directory.GetDirectories(e.Node.FullPath)

For countSubs = 0 To subDirs.Length - 1
e.Node.Nodes.Add(subDirs(countSubs).Replace(e.Node.FullPath & "\", ""))

Next

Call dispMenbersFiles()

End Sub
こういうコードです。
AddHandler treeFolders.AfterSelect, AddressOf treeFolders_AfterSelect

Dim a As TreeNode
a = treeFolders.Nodes.Add(SFRootPath)

For countSubFolders = 0 To subFolders.Length - 1

a.Nodes.Add(subFolders(countSubFolders).Replace(SFRootPath, ""))

Next
この段階で子ノードを既に作成していたので、二重になってしまっていました。
前までこのことに気づかず違うことに目がいってしまっていました。
ごめんなさい。
今回は
If e.Node.FullPath = SFRootPath Then Exit Sub
を追加して二重にならないようにしました。
今回rakiさんが色々丁寧に教えていただけなかったらきっとここまでできなかったとおもいます。何度もわけの分からない質問にお返事返していただいて大変お世話になりました。
rakiさんからたくさん勉強になりました。どうもありがとうございました。
これからも頑張りたいと思います。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板