.NET2005 VISTAです。
CLRのツリービューについてお尋ねします。
たいへん初歩的なことなのですが、ノードを描画する場合、階層について、手元の参考書では、
treeView1->Nodes->Add(_T("親"));
treeView1->Nodes[0]->Nodes->Add(_T("子"));
treeView1->Nodes[0]->Nodes[0]->Nodes->Add(_T("孫"));
というように例示しています。
しかし、これですと、ツリーの構造があらかじめ分かっていないと、描画することができません。
ノードごとのツリーの深さと構造とをあらかじめデータとして持っていて、
if (・・・) {
treeView1->Nodes[a]->Nodes->Add(_T("子"));
}
else if (・・・) {
treeView1->Nodes[b]->Nodes[c]->Nodes->Add(_T("孫"));
}
というような方法もあるかなぁとは思ったのですが、分岐の数で階層の深さの限界が決まってしまうので、なんだかピンときません。
くぐってみましたが、さがし方が悪いのか、参考になるようなコードは見つかりませんでした。
なにか方法はあるのでしょうか。
やり方次第でどうにでもなります。
たとえば
親
(降りる)
子1
(降りる)
孫
(登る)
子1
なんてなデータを用意しておき、
TreeNode^ current;
TreeNode node;
if ( 読んだデータ = (降りる) )
current = node;
else if ( 読んだデータ = (昇る) )
current = current->Parent;
else {
node = gcnew TreeNode(読んだデータ);
current->Nodes->Add(node);
}
ってやれば
親
|
+--子1
| |
| +-- 孫
|
+--子2
ってなるです。
επιστημη さん、ありがとうございます。
お話の意味は理解していると思うのですが、回帰自体がまだよく分かっていなくて、少し時間がかかりそうです。
チャレンジします。
実は。。。
お返事と違うことを書くのは、たいへん心苦しいのですが、
あちこち「くぐって」みました。
VBについて、
Dim NodeX As Node
Set NodeX = trvItem.Nodes.Add(, , "Root", "ルート")
Set NodeX = trvItem.Nodes.Add("Root", tvwChild, "Child1", "子ノード1")
Set NodeX = trvItem.Nodes.Add("Child1", tvwChild, "Child2", "子ノード2")
Set NodeX = trvItem.Nodes.Add("Child2", tvwChild, "Child3", "子ノード3")
Set NodeX = trvItem.Nodes.Add("Child3", tvwChild, "Child4", "子ノード4")
というコードを見つけました。
これを下記のようにおきかえたところ、
treeView1->BeginUpdate();
treeView1->Nodes->Add("Root", tvwChild, "Child1", "子ノード1");
treeView1->Nodes->Add("Child1", tvwChild, "Child2", "子ノード2");
treeView1->Nodes->Add("Child2", tvwChild, "Child3", "子ノード3");
treeView1->Nodes->Add("Child3", tvwChild, "Child4", "子ノード4");
treeView1->EndUpdate();
ビルドでエラーは出ないのですが、実行すると、
「'tvwChild' : 定義されていない識別子です。」
というエラーになります。
Nodes Add tvwChild で検索をかけてみたのですが、役に立つ情報は得られませんでした。
「Set NodeX = trvItem.Nodes.Add(, , "Root", "ルート")」
をはずしたのは、これがあるとビルド時にエラーになるというレベルの話です。
第一引数と、第二引数が空欄なのがいけないようですが、どうしたらいいのか分かりません。
次に、C#のコードを見つけました。
treeView1.Nodes.("花").Nodes.Add("ヒマワリ");
「花」というノードの下に、「ヒマワリ」という子ノードをぶら下げる、というコードなのですが、
treeView1->BeginUpdate();
treeView1->Nodes->Add("子ノード1");
treeView1->Nodes->Add("子ノード2");
treeView1->Nodes->Add("子ノード3");
treeView1->Nodes["子ノード3"]->Nodes->Add("子ノード4");
treeView1->EndUpdate();
としてみると、ビルドは通りますが、実行すると、
NullReferenceException はハンドルされませんでした。
オブジェクトインスタンスの作成には、newキーワードを使用します。
というダイアログボックスみたいなのが出ます。
気を悪くなさったら許して頂きたいのですが、VC++のマネージコードでも、こうした形で
ノードをつけることは、できないでしようか。。。
できます。マニュアルを読みましょう。
編集 削除細かいんですが
>お話の意味は理解していると思うのですが、回帰自体がまだよく
再帰関数のことですよね
>VC++のマネージコードでも
MSDN見た感じだとこんなかなぁ
treeView1->Nodes->Add(gcnew String(_T("子ノード1")));
System名前空間が若干気になりますが・・・普通入ってるような
> treeView1->Nodes->Add(gcnew String(_T("子ノード1")));
> System名前空間が若干気になりますが・・・普通入ってるような
問題はそこじゃない。文字列リテラルはSystem::Stringに暗黙変換されます。
検索サイトを探し回るのは構いませんが、なぜマニュアルを読まないの?
treeView1->Nodes->Add("1","親");
treeView1->Nodes["1"]->Nodes->Add("1.1","長男");
treeView1->Nodes["1"]->Nodes->Add("1.2","二男");
treeView1->Nodes["1"]->Nodes["1.1"]->Nodes->Add("1.1.1","初孫");
↑さっくりできちゃいましたょ?
> 細かいんですが
>> お話の意味は理解していると思うのですが、回帰自体がまだよく
> 再帰関数のことですよね
再帰関数になんかなってませんよ。
データ構造が再帰的なだけ。
※ 当然ですわね、TeeeNodeの子もまたTreeNodeなんだから。
επιστημηさん、そださん、ありがとうございます。
επιστημηさんがご提示のコード、試しました。
「回帰」は、許されるなら、笑って見逃してやってください。
申し訳ありません。
実は、説明が不十分だったと反省したのですが、ツリー構造の深さにかかわらずに描画できるコードを探しています。
treeView1->Nodes->Add("1","親");
treeView1->Nodes["1"]->Nodes->Add("1.1","長男");
treeView1->Nodes["1"]->Nodes->Add("1.2","二男");
treeView1->Nodes["1"]->Nodes["1.1"]->Nodes->Add("1.1.1","初孫");
ですと、やはり階層が多くなるごとに、Nodes->が増えてゆきます。
試しに、
treeView1->Nodes["1"]->Nodes["1.1"]->Nodes->Add("1.1.1","初孫");
を
treeView1->Nodes["1.1"]->Nodes->Add("1.1.1","初孫");
に置き換えてみたのですが、ビルドは通るものの、実行はできませんでした。
やはり「再帰」的にたどってゆくしかないのでしょうか。
> なぜマニュアルを読まないの?
読んではいるのですが。。。
そもそも、私には、Add が見つかりません。
改めて「treeView Nodes Add」で検索して、επιστημηさんがご提示の
コード例は見つかりましたが、そこまでです。
(正直に言うと、質問を投稿する前にもこのページには行っていましたが、コードは見過ごしていました。)
「treeView メンバ」で検索しても、「Nodes」までです。
「Add」だけで検索しても、
TreeNodeCollection..Add メソッド は見つかりますが、感動を呼ぶようなことはあまり書いてないようです。
きっとどこかに書いてあるのでしょうが。。。
> やはり「再帰」的にたどってゆくしかないのでしょうか。
再帰しないやりかたはありますが、再帰的な構造には再帰的な処理がもっとも楽で単純です。
>「treeView メンバ」で検索しても、「Nodes」までです。
あぁ、調べ方を知らないんだ。
TreeView.Nodes の型は何と書いてありましたか?
TreeViewCollection のはず。
そこでTreeViewCollection のヘルプを開けば...
> treeView1->Nodes->Add("1","親");
> treeView1->Nodes["1"]->Nodes->Add("1.1","長男");
> treeView1->Nodes["1"]->Nodes->Add("1.2","二男");
> treeView1->Nodes["1"]->Nodes["1.1"]->Nodes->Add("1.1.1","初孫");
> ですと、やはり階層が多くなるごとに、Nodes->が増えてゆきます。
ちょいちょいと書き替えてみましょ。
TreeNode^ current = nullptr;
TreeNode^ node = gcnew TreeNode("親");
treeView1->Nodes->Add(node);
current = node; // current は"親"
node = gcnew TreeNode("長男");
node->Nodes->Add(node); // "親"の下に"長男"
current = node; // current は"長男"
node = gcnew TreeNode("初孫");
current->Nodes->Add(node); // "長男"の下に"初孫"
current = current->Parent; // current は(階層を登って)"親"
node = gcnew TreeNode("次男");
node->Nodes->Add(node); // "親"の下に"次男"
ですと、階層が多くなるごとに、Nodes->が増えて"ゆきません"。
で、こいつを一般化すると、だ。
array<String^>^ data = { "ナミヘイ",
"→", "サザエ",
"→", "タラ",
"←", "カツオ", "ワカメ",
};
TreeNode^ current = nullptr;
TreeNode^ node;
for each ( String^ text in data ) {
if ( text == "→" ) {
current = node;
} else
if ( text == "←" ) {
current = current->Parent;
} else {
node = gcnew TreeNode(text);
if ( current == nullptr ) treeView1->Nodes->Add(node);
else current->Nodes->Add(node);
}
}
επιστημηさん、たいへんありがとうございました。
ご提示のコードを、実行しました。
ステップ実行して、手順を確認しました。
コードをいろいろ変化させて、応用を試してみます。
併せて、ヘルプの調べ方も、ありがとうございました。
「・・・Collection」は、なにかにつけ登場するので、気にはしていましたが、よく分っていませんでした。
うーん...
つまり最初のレスでとっくに答が出てたわけじゃん?
そのものズバリのコードが提示されるまで手も足も出ないてのは
すっごく問題ですよ。
επιστημηさん、こんにちは。
ご回答、ありがとうございました。
問題は、相当にあります。
でも、現実に、自分では、解決にたどりつけなかったかもしれません。
ノードの親子関係を、配列の中で、どのように表現したらいいのか、考えていました。
回答をご提示頂いて、「どうだ、簡単だろう」と言われれぱ、シンプルではありますが、私にはあまり簡単でもありません。
↓再帰を使わない例。これではいかがでしょ。
array<String^>^ data = {
// テキスト, 親テキスト
"ナミヘイ", nullptr,
"サザエ", "ナミヘイ",
"カツオ", "ナミヘイ",
"ワカメ", "ナミヘイ",
"タラ", "サザエ",
};
Dictionary<String^,TreeNode^> dic;
for ( int i = 0; i < 5*2; i += 2 ) {
TreeNode^ node = gcnew TreeNode(data[i]);
dic.Add(data[i],node);
String^ key = data[i+1];
if ( key == nullptr ) {
treeView1->Nodes->Add(node);
} else {
dic[key]->Nodes->Add(node);
}
}
επιστημηさん、ありがとうこざいます。
また、わからなくなりました。
最後にご提示のコードのうち、まず、Dictionaryは、VC++では用意されていないようです。
ただ、こうしたものを使わなくても、下記で、通るような気がしたのです。
array<String^>^ data = {
// テキスト, 親テキスト
"ナミヘイ", nullptr,
"サザエ", "ナミヘイ",
"カツオ", "ナミヘイ",
"ワカメ", "ナミヘイ",
"タラ", "サザエ",
};
TreeNode^ node;
TreeNode^ current = nullptr;
for ( int i = 0; i < 5*2; i += 2 ) {
node = gcnew TreeNode(data[i]);
if ( i == 0 ) {
treeView2->Nodes->Add(node);
} else {
current = gcnew TreeNode(data[i+1]);
current->Nodes->Add(node);
}
}
再帰のコードで、ステッブ実行すると、「さざえ」ノードを追加するとき、TreeNode^ current は、
gcnew TreeNode("ナミヘイ");
になっています。
それで、
current->Nodes->Add(gcnew TreeNode("サザエ"));
で、
ナミヘイの子ノードに、サザエが追加されています。
ということは、再帰を使わなくても、
current = gcnew TreeNode(data[i+1]);
//(data[i+1]は"ナミヘイ")
current->Nodes->Add(node);
//(nodeは"ナミヘイ")
で、同じように、ナミヘイの子にサザエが追加されるはずだと思いました。
でも、実際には、ナミヘイしか表示されません。
重ね重ね申し訳ありませんが、アドバイスを頂けませんでしょうか。
すみません。
node は、「サザエ」です。
タイプミスしました。
> 最後にご提示のコードのうち、まず、Dictionaryは、VC++では用意されていないようです。
名前空間 System::Collections::Generic にありますょ?
> ただ、こうしたものを使わなくても、下記で、通るような気がしたのです。
> ...
> でも、実際には、ナミヘイしか表示されません。
"通るような気がした"のが間違いだからです。
> ということは、再帰を使わなくても、
> current = gcnew TreeNode(data[i+1]);
> current->Nodes->Add(node);
> で、同じように、ナミヘイの子にサザエが追加されるはずだと思いました。
このcurrentがTreeViewにAddされていませんから当然表示されませんね。
かといってTreeViewにAddされたとしたら、ナミヘイがたくさん表示されてしまいますけど。
επιστημηさん、ありがとうこざいます。
再帰を使わない例で、System::Collections::Generic を追加することで、表示させることができました。
ヘルプで、Dictionaryの名前空間が System::Collections::Generic であることを知る手順も確かめました。
current = gcnew TreeNode(data[i+1]);
current->Nodes->Add(node);
がうまくいかない理由を含め、とても「理解した」とは言えない状態ですが、少し時間をかけて確かめてみます。
たいへんありがとうございました。