ツリーコントロールで指定した場所にアイテムを追加する方法

[戻る]
こう 2006/07/13(木) 15:11:06
環境
OS:WIN2000
VC++.NET

度々お世話になります。

エクスプローラの画面上でアイテムを選択しなくても、
引数を与える事により、
引数で指定の場所にアイテムを追加する方法を考えています。

選択した後ならGetSelectedItemを使えますが、
今回は選択せずに引数を用いて追加します。

どう考えればいいのかが思いつかないので、
どなたかご教授お願いします。

επιστημη 2006/07/13(木) 15:41:12
なにが問題なのかわかりません。

「階層リストをすべて展開する方法」
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200607/06070034.txt

で、GetRootItem,GetChildItem,GetNextItem によって
すべてのitemを辿ることができたじゃないですか。

「どう考えればいいのかが思いつかない」とは如何なる意味ですか?

こう 2006/07/13(木) 17:37:34
>επιστημηさん
返信ありがとうございます。

私が今やろうとしているのはCSVファイルからデータを読み込み、
それを元にツリーを作ろうとしています。
今の疑問は、どんな引数でもらえばいいかです。

何もない状態からツリーを作成していくとき、
その引数を基に作成していきます。
例を以下に示します。

例:
以下のデータを基にツリーを作成する。
CSVテーブル内容
・(アイテム文字列A,作成場所1)
・(アイテム文字列B,作成場所2)
・(アイテム文字列C,作成場所3)
・(アイテム文字列D,作成場所4)
・(アイテム文字列E,作成場所5)

ツリー
□A、1
□B、2
 □C、3
 | □E,5
 □D、4

この様にファイルを基にツリーを作成するにあたり、
どうやって作成場所を指定すればいいのかがわからないのです。
もしこれを直書きで作成すると以下のようにするしかありません。

HTREEITEM htree;
HTREEITEM htree1;
HTREEITEM htree2;
HTREEITEM htree3;
htree = m_CtrlTree.InsertItem( csData, TVI_ROOT, TVI_LAST);

// 一階層したのアイテムの追加
htree1 = m_CtrlTree.InsertItem( "subroot", htree, TVI_LAST);
htree2 = m_CtrlTree.InsertItem( "subroot", htree1, TVI_LAST);
htree3 = m_CtrlTree.InsertItem( "subroot", htree2, TVI_LAST);

// 一階層したのアイテムの追加
htree1_1 = m_CtrlTree.InsertItem( "subroot", htree1, TVI_LAST);

このように先頭を基準に作成していくことはできます。
しかしファイルを読み込む際、
CSVのファイルをどういう引数にするべきなのかがわかりません。


私が何がわからないかわかっていただけたでしょうか。

Blue 2006/07/13(木) 17:43:14
単に親を指定するだけじゃダメでしょうか?

CSVテーブル内容
・(1,アイテム文字列A,0)
・(2,アイテム文字列B,1)
・(3,アイテム文字列C,2)
・(4,アイテム文字列D,2)
・(5,アイテム文字列E,3)

みたいな。
※(ID,文字列,親のID)

こう 2006/07/13(木) 17:49:42
>Blueさん
返信ありがとうございます。

>※(ID,文字列,親のID)
とありますが、一番左のIDはこのハンドルのことでしょうか。
仮にだとすればどうやってファイルに個々ハンドルが決定できるのでしょうか。

こう 2006/07/13(木) 17:50:35
>とありますが、一番左のIDはこのハンドルのことでしょうか。
「このハンドル」ではなく、「子のハンドル」です。

Blue 2006/07/13(木) 18:10:53
> 一番左のIDはこのハンドルのことでしょうか。
違いますよ。単にこのアイテムはどの親の子か示すための一意な番号です。
一意なら何でもよいのですが、わかりやすく1から昇順にしただけです。

CSVテーブル内容
・(1,アイテム文字列A,0)← これは ID が 0 つまり ルート の子
・(2,アイテム文字列B,1)← これは ID が 1 つまり アイテム文字列A の子 
・(3,アイテム文字列C,2)
・(4,アイテム文字列D,2)
・(5,アイテム文字列E,3)

# CSVだからこんな構造にしないといけないわけなんだろうな。。
# もともとXMLのようなツリー構造を持っていれば簡単なんだろうか。

こう 2006/07/13(木) 18:47:46
>Blueさん
返信ありがとうございます。

>CSVテーブル内容
>・(1,アイテム文字列A,0)← これは ID が 0 つまり ルート の子
>・(2,アイテム文字列B,1)← これは ID が 1 つまり アイテム文字列A の子 
これは親と子をIDで指定するという事ですよね。
IDに対応したハンドルとかはどうやって設定するのでしょうか?

parentID、childIDの値はCSVに書く場合、固定値でどのようにべきなのでしょうか?
もしや固定値で書くべきではないのでしょうか?
ご教授お願いします。


現在CSVのデータを作成するとしたら、
子はどんな値にすればいいのでしょうか。
以下は全て直書きの例です。

void CMGLDlg::OnBnClickedBtnaddcsv()
{
// 以下は簡易テストデータ
// 本来はこのCSVテストデータが大量にある
 CString csData = "test1";                               // アイテム名(文字列)
 HTREEITEM parentID = m_CtrlTree.GetFirstVisibleItem();  // 親のハンドル
 HTREEITEM childID  = m_CtrlTree.GetChildItem(parentID); // 子のハンドル

// ツリーにアイテム追加
 Insert(  childID,  parentID,  csData );
}

Blue 2006/07/13(木) 19:37:54
ちょっと確認してもよいでしょうか?

ツリーは何もないところから作っていくんですよね?
既存のツリーにCSVのデータを追加するってことではなく。
(↑この場合うまい方法は浮かびません)

επιστημη 2006/07/13(木) 22:17:55
> 私が今やろうとしているのはCSVファイルからデータを読み込み、
> それを元にツリーを作ろうとしています。
> 今の疑問は、どんな引数でもらえばいいかです。

…タイトル下手すぎ。

ツリーコントロールとは何の関係もなく、
「木構造をCSVで表現するには?」
ってことやんか。

επιστημη 2006/07/13(木) 22:22:37
> □A、1
> □B、2
> □C、3
> | □E,5
> □D、4

CSVでなくちゃダメ?
XMLならそのものヅバリの木構造が表現できるのに。

<?xml version='1.0' ?>
<item name='A'>
  <item name='B'>
    <item name='C'>
      <item name='E' />
    <item name='D' />
  </item>
</item>

こう 2006/07/14(金) 10:13:20
>Blueさん
返信ありがとうございます。
ツリーは何もないところから作成していきます。
勘違いされるような記述をして申し訳ありません。

>επιστημηさん
返信ありがとうございます。
現状ではCSVで作成されるデータをVC側でツリーにするという仕様になっています。
そのためにCSVがどのような値を設定すればいいのかがわかりません。
それはVC側でどのような値が必要なのか、私自身が理解できてないからです。

本当に申し訳ございません。
何卒、ご教授お願いします。

Geo=TK3 2006/07/14(金) 11:05:15
横槍失礼します。

>それはVC側でどのような値が必要なのか、私自身が理解できてないからです。

要するにこうさんのやりたい事は以前提示してくれたサンプルの

>void CMGLDlg::OnBnClickedBtnaddcsv()
>{
>// 以下は簡易テストデータ
>// 本来はこのCSVテストデータが大量にある
> CString csData = "test1";                               // アイテム名(文字
列)
> HTREEITEM parentID = m_CtrlTree.GetFirstVisibleItem();  // 親のハンドル
> HTREEITEM childID  = m_CtrlTree.GetChildItem(parentID); // 子のハンドル
>
>// ツリーにアイテム追加
> Insert(  childID,  parentID,  csData );
>}

で言うなら、parentID, childID の値を“(GetFirstVisibleItem や GetChildItem とい
った関数を使わずに) 直接 CSV ファイルから読み込みたい”って事ですか?

こう 2006/07/14(金) 11:11:59
>Geo=TK3さん
返信ありがとうございます。

解釈ありががとうございます。
まさにその通りです。
それを実現させるにはどうすればいいのかがわからないのです。

Blue 2006/07/14(金) 11:25:11
一応先に示したCSV形式のサンプル
※ファイルを読み込んだりカンマで分割するとかそういうことは
 省略してあります。

Tree
□食べ物
 |
 □野菜
 │├大根
 │└人参
 □果物
  └メロン

csv
(1,食べ物,0)
(2,野菜,1)
(3,大根,2)
(4,人参,2)
(5,果物,1)
(6,メロン,5)

ソース
void CTreeTestDlg::OnButton1() 
{
    // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
    this->m_tree.DeleteAllItems();

    HTREEITEM hTreeItem;
    HTREEITEM hParent;
    HTREEITEM items[ 50 ]; // 適当。動的にしたいならばCArray、CMapとか。
    memset( items, 0, sizeof( items ) );

    items[ 0 ] = TVI_ROOT;

    // (1,食べ物,0)
    hParent = items[ 0 ];
    hTreeItem = this->m_tree.InsertItem( _T( "食べ物" ), hParent );
    items[ 1 ] = hTreeItem;

    // (2,野菜,1)
    hParent = items[ 1 ];
    hTreeItem = this->m_tree.InsertItem( _T( "野菜" ), hParent );
    items[ 2 ] = hTreeItem;

    // (3,大根,2)
    hParent = items[ 2 ];
    hTreeItem = this->m_tree.InsertItem( _T( "大根" ), hParent );
    items[ 3 ] = hTreeItem;

    // (4,人参,2)
    hParent = items[ 2 ];
    hTreeItem = this->m_tree.InsertItem( _T( "人参" ), hParent );
    items[ 4 ] = hTreeItem;

    // (5,果物,1)
    hParent = items[ 1 ];
    hTreeItem = this->m_tree.InsertItem( _T( "果物" ), hParent );
    items[ 5 ] = hTreeItem;

    // (6,メロン,5)
    hParent = items[ 5 ];
    hTreeItem = this->m_tree.InsertItem( _T( "メロン" ), hParent );
    items[ 6 ] = hTreeItem;
}

PATIO 2006/07/14(金) 11:35:51
CSVファイルの中に直接HTREEITEMと入れとくなんて事は無理でしょう。
そもそもHTREEITEMはツリーコントロールにアイテムを登録しないと
返ってこない値です。CSVの中だけで完結させようと言う考えは止めた方がいいでしょう。
というか、不可能ですし。
Blueさんが書かれているように内部的にツリーの関係がわかるような記述方法を
しておいて、それを元に解釈しながらツリー構造を構築するしかありません。
なので考え方を変えましょう。

PATIO 2006/07/14(金) 11:35:58
CSVファイルの中に直接HTREEITEMを入れとくなんて事は無理でしょう。
そもそもHTREEITEMはツリーコントロールにアイテムを登録しないと
返ってこない値です。CSVの中だけで完結させようと言う考えは止めた方がいいでしょう。
というか、不可能ですし。
Blueさんが書かれているように内部的にツリーの関係がわかるような記述方法を
しておいて、それを元に解釈しながらツリー構造を構築するしかありません。
なので考え方を変えましょう。

επιστημη 2006/07/14(金) 11:36:29
/*
 * なんかできたー。
 */

void CXXXDlg::BuildTRree()
{ 
 
  std::string csv = 
    "1,アイテム文字列A,0 \n"
    "2,アイテム文字列B,1 \n"
    "3,アイテム文字列C,2 \n"
    "4,アイテム文字列D,2 \n"
    "5,アイテム文字列E,3 \n";

  std::istringstream stream(csv);
  std::string line;
  std::vector<item> table;
  item tmp;
  tmp.id = 0;
  tmp.handle = tree.GetRootItem();
  table.push_back(tmp);
  while ( std::getline(stream, line) ) {
    char* p = &line[0];
    tmp.id = std::atoi(std::strtok(&line[0],","));
    tmp.data = std::strtok(0,",");
    tmp.parent = std::atoi(std::strtok(0,", "));
    tmp.handle = (HTREEITEM)-1;
    table.push_back(tmp);
  }

  bool complete = false;
  while ( !complete ) {
    complete = true;
    for ( int i = 0; i < table.size(); ++i ) {
      if ( table[i].handle == (HTREEITEM)-1 ) {
        for ( int j = 0; j < table.size(); ++j ) {
          if ( table[j].id == table[i].parent && table[j].handle != (HTREEITEM)-
1 ) {
            complete = false;
            table[i].handle = tree.InsertItem(table[i].data.c_str(), table
[j].handle);
            break;
          } 
        }
      }
    }
  }
}

三草 2006/07/14(金) 11:37:01
> parentID、childIDの値はCSVに書く場合、固定値でどのようにべきなのでしょうか?
> もしや固定値で書くべきではないのでしょうか?
なにか規則性を持たせれば、出来るかもしれませんが、
解析して、ツリーから探して、追加するとなると、少々面倒のような気もします。
# 私なら後述の手を使います。
例えば)0-0-2     0番目(A)の下の0番目(B)の下の2番目(F)の下に追加(H)
□A、0
□B、0-0
| □D、0-0-0
| | □G,0-0-0-0
| □E、0-0-1
| □F、0-0-2
|   ■H,0-0-2-0
□C、0-1

>> CSVテーブル内容
>> ・(1,アイテム文字列A,0)← これは ID が 0 つまり ルート の子
>> ・(2,アイテム文字列B,1)← これは ID が 1 つまり アイテム文字列A の子 
> これは親と子をIDで指定するという事ですよね。
> IDに対応したハンドルとかはどうやって設定するのでしょうか?
([自ID],アイテム文字列,[親ID]) として、エラーチェック抜きにすると、
1.CSV 1レコード読み込む
  1.1.[親ID]が0であればルートの下に追加
  1.2.[親ID]が0以外であれば、結び付けられたIDの下に追加
      (ハンドルが配列か何かにあるので、追加できますよね)
2.ツリーに追加すると、そのアイテムのハンドルが得られるので、
  そのハンドルと[自ID]を、配列か何かで結び付けます。
3.CSVの終わりまで1〜2の繰り返し


> で言うなら、parentID, childID の値を“(GetFirstVisibleItem や 
> GetChildItem といった関数を使わずに) 直接 CSV ファイルから読み込みたい”
> って事ですか?
HTREEITEMの値って、常に同じ値でしょうか?
再度アプリを立ち上げたら、同じ値でしょうか?
別PCでも、同じ値を指しますか?
ということを考えると、CSVファイルからHTREEITEMを読み込むという設計は、
どうなのでしょう?

PATIO 2006/07/14(金) 11:37:23
すいません、最初に入れた分は間違って入れてしまっているので
無視して次の分を見てください。
投稿ミスをして申し訳ないです。

Blue 2006/07/14(金) 11:39:19
もう少し、気の利いたのに改良。

void CTreeTestDlg::OnButton1() 
{
    // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
    this->m_tree.DeleteAllItems();

    HTREEITEM hTreeItem;
    HTREEITEM hParent;
    HTREEITEM items[ 50 ];
    memset( items, 0, sizeof( items ) );

    struct CSVInfo
    {
        unsigned int ID;
        TCHAR        Name[ 50 ];
        unsigned int Parent;
    };
    
    CSVInfo info[] = {
        { 1, _T( "食べ物" ), 0 },
        { 2, _T( "野菜" )  , 1 },
        { 3, _T( "大根" )  , 2 },
        { 4, _T( "人参" )  , 2 },
        { 5, _T( "果物" )  , 1 },
        { 6, _T( "メロン" ), 5 }
    };
    CSVInfo temp;

    items[ 0 ] = TVI_ROOT;

    for ( int i = 0; i < sizeof( info ) / sizeof( info[ 0 ] ); i++ )
    {
        temp = info[ i ];
        hParent = items[ temp.Parent ];
        hTreeItem = this->m_tree.InsertItem( temp.Name, hParent );
        items[ temp.ID ] = hTreeItem;
    }
}

三草 2006/07/14(金) 11:39:50
>> parentID、childIDの値はCSVに書く場合、固定値でどのようにべきなのでしょうか?
>> もしや固定値で書くべきではないのでしょうか?
>なにか規則性を持たせれば、出来るかもしれませんが、
>解析して、ツリーから探して、追加するとなると、少々面倒のような気もします。
># 私なら後述の手を使います。
>例えば)0-0-2     0番目(A)の下の0番目(B)の下の2番目(F)の下に追加(H)
>□A、0
>□B、0-0
>| □D、0-0-0
>| | □G,0-0-0-0
>| □E、0-0-1
>| □F、0-0-2
>|   ■H,0-0-2-0
>□C、0-1
ごめんなさい、ちょっと勘違いしてました。
上記は無しで;;

こう 2006/07/14(金) 13:04:41
>Blueさん
返信ありがとうございます。
私が実現じたかったことはまさにこれです。
上記のように考えればよかったんですね。


>    CSVInfo info[] = {
>        { 1, _T( "食べ物" ), 0 },
>        { 2, _T( "野菜" )  , 1 },
>        { 3, _T( "大根" )  , 2 },
>        { 4, _T( "人参" )  , 2 },
>        { 5, _T( "果物" )  , 1 },
>        { 6, _T( "メロン" ), 5 }
>    };
CSVのデータ取得する際、このように書けることをしりませんでした。
テストをする際に、大変参考になりました。
SCVの参照にここを参考にしてました。
http://rararahp.cool.ne.jp/vc/class/csvfile.htm
本当にりがとうございます。


>PATIOさん
返信ありがとうございます。
>CSVファイルの中に直接HTREEITEMと入れとくなんて事は無理でしょう。
>そもそもHTREEITEMはツリーコントロールにアイテムを登録しないと
>返ってこない値です。CSVの中だけで完結させようと言う考えは止めた方がいいでしょう。
>というか、不可能ですし。
不可能だとは知りませんでした。
何でもどうにかすればできると思ってました。お恥ずかしい限りです。
私もBlueさんのものを参照し、理解ができました。
今後ともよろしくお願いします。

>επιστημηさん
返信ありがとうございます。
このようなソースの考え方もあるんですね。
上記のサンプルは自分がまだ使ったことのない書き方がしるされており、
解読するのに時間がかかってしまいました。
昨日同様、今後ともよろしくお願いします。
ありがとうございました。

>三草さん
返信ありがとうございます。
私もどうようの考え方をしてまし。
(x、y)を指定することにより、
なんとかアイテムの作成場所を指定できないかと。
例えば弟を指定する時はxを+1、子を指定するときはyを+1とかです。
でもアイテムが必ず重複してしまうんですよね。
それに悩んだのもあってここに書き込みしたんです。
ありがとうございました。
今後ともご教授お願いします。

こう 2006/07/14(金) 13:05:57
[[解決]]
以上で問題は解決できました。
今後ともよろしくお願いします。
皆様、本当にありがとうございました。

敬具

古いスレッドには追加発言できません。