階層リストをすべて展開する方法

[戻る]
こう 2006/07/11(火) 17:40:16
環境
OS:WIN2000
VC++.NET

ツリーコントロールでダイアログ起動時にツリーが出力されます。
この起動時にディレクトリなどの項目の階層リストをすべて展開する方法を教えてください。

またどうやって検索をすればヘルプがあるかわかりません。
よろしくお願いします。

三草 2006/07/11(火) 20:24:29
こういったことでしょうか?
http://www.athomejp.com/goldfish/mfc/treeview/itemexpand.asp

ヘルプが何をさしているのか分かりませんが、
参考書の巻末にはたいていIndexがあるでしょうし、
MSDNライブラリをインストールしていれば、それで調べることも出来ますし、
オンラインMSDNならGoogleやYahoo!で MSDN と検索すればおそらく一番上に出てくるで
しょう。
直接Googleで検索しても、知りたい情報が出てくるかと。
また、VC++ラウンジの ・ラウンジの全トピックから単語[    ]を[検索] でも、
ラウンジ内の情報を検索できます。

開発者にとって、検索するスキルは重要なスキルの一つです。
#だと私は思ってます。
なので、上記の方法だけではなく、思いつく限りの検索をしてみて、
こうさん自身の検索方法を身につけてみてはいかがでしょうか?

こう 2006/07/11(火) 20:43:04
>三草さん
このページは私も参照しました。
この場合だと、カーソルがあたったところだけが展開されます。

起動した後に展開したいのではなく、
起動と同時に全体を展開したいのです。

いくつものツリー構造になっていると、
カーソルがあたっただけという条件では展開できません。

以上です。よろしくお願いします。

επιστημη 2006/07/11(火) 20:58:36
"ノードを展開する"を再帰的に適用するだけじゃないんですか?

展開する(ノード node) {
  if ( node は子ノードを持つ ) {
    for ( 各子ノード child に対し ) {
      展開する(child);
    }
  } else {
    nodeをexpandする。
  }
}

三草 2006/07/11(火) 21:08:08
参照サイト:GoldFishのサンプルより
>     // 選択されているItemを取得
>    HTREEITEM item =
>            m_TreeCtrl.GetSelectedItem();    ←←←
>     // 展開する
>    m_TreeCtrl.Expand( item, TVE_EXPAND);
なので、選択されているところ( = カーソルが当たったところ )なのでしょう。

先ほどご紹介したMSDNで Expandメソッド は調べてみましたか?
第一引数に指定する HTREEITEM で指定されたツリーアイテムを、
第二引数で指定された動作をさせます。

では、ツリーアイテムのハンドル(HTREEITEM)は、どうやって得るのでしょうか?
同じくCTreeCtrlのメンバの一覧を眺めていると、

GetFirstVisibleItem
 指定されたビュー アイテムの最初に表示されるアイテムを取得します。 

という項目が見当たります。
説明を見てみると、

HTREEITEM GetFirstVisibleItem( ) const;
 ツリー ビュー コントロールの最初に表示されるアイテムを取得します。

とあります、どうやらHTREEITEMが得られるようです。
GetFirstVisibleItem()とのことなので、きっとGetNextVisibleItemとかもあるのではな
いか?
と、一例ですが調べる方法です。

他にも、GetChildItem() や GetItem()でも取得できるようですね。

以上のことを、επιστημηさんのご指摘とあわせて、
考えてみてはいかがでしょうか。

PATIO 2006/07/12(水) 13:19:59
最近はクラスにものすごく便利なメンバー関数が実装されていたりして
呼び出せば、そこまでやってくれるのかと言うような場合もありますけれど、
基本的にクラスが提供しているメンバー関数と言うのは単機能で
いくつかの機能を組み合わせて目的を達成するのが普通です。
なので、目的の処理を行うにはどういう機能と組み合わせれば、
うまくいくのかと言う発想が必要です。

MSDNで自分が使用しているクラスのメンバー関数を一通り見てみて
目的ずばりの関数が無いならある関数の組み合わせで何とかできないか
考えて見ましょう。
通常は、よっぽど特殊な事をしようとしている場合でもない限り、
用意してある関数の組み合わせで何とかなるものです。

こう 2006/07/12(水) 15:43:35
>επιστημη 
サンプルありがとうございます。
これに伴い、途中までは作成できました。

>三草
詳しい解説ありがとうございます。非常に参考になりました。
現在試行錯誤しながら作成中です。

>PATIO
ご教授ありがとうございます。まったくその通りだと私も思います。
しかしながら、どうしても作成できません。
下記のようにツリーの中に子が複数出てきた場合に、
どのように処理をかけばいいかで悩んでます。

------------------------------------------
・現在のコーディング
この再帰的に展開する私が考えた結果、
図のようには展開できるようになりました。

 □
 -□
   -□
     -□
     | -□
     +■
      □

このとき「□」は展開したもの、「■」は展開してないものです。
右の子には展開できますが、4回目の展開の時のように子が複数いた場合の展開方法がわかりま
せん。
試行錯誤してみたのですがどうしてもわかりません。

クラスの中にある関数に目を通し考えてみましたが、
私のレベルでは作れませんでした。

大変申し訳ありませんが、
ご教授お願いします。

επιστημη 2006/07/12(水) 16:22:41
> 下記のようにツリーの中に子が複数出てきた場合に、
> どのように処理をかけばいいかで悩んでます。

…どんなコード書いたんでしょ。
きちんと再帰させていれば、すべての子に対して展開するはず。
僕が示したコード、ほとんどそのままだと思うのですが。

こう 2006/07/12(水) 19:19:16
ただいま作成中で途中ですが、以下に現在のソースを表示します。


HTREEITEM rootFirstItem = m_Tree.GetFirstVisibleItem();
m_Tree.Expand( rootFirstItem, TVE_EXPAND);

if(rootFirstItem != NULL)
{
    HTREEITEM hSELECT = m_Tree.GetChildItem(rootFirstItem);    // 
hItem で指定されたアイテムの子であるツリー ビュー アイテムを取得します。


//        HTREEITEM hParent = m_Tree.GetNextItem( hSELECT, 
TVGN_PARENT );    // 親
    HTREEITEM hCHILD  = m_Tree.GetNextItem( hSELECT, TVGN_CHILD );    // 子
    HTREEITEM hNEXT   = m_Tree.GetNextItem( hSELECT, TVGN_NEXT );    // 弟
    
    
    if( hNEXT == NULL && hCHILD == NULL)
    {

    }
    else    // 弟と子のどちから一方でも存在している!
    {
        for(;;)
        {
            if( hNEXT == NULL && hCHILD == NULL)
            {
                break;
            }
            m_Tree.Expand( hSELECT, TVE_EXPAND);
//                HTREEITEM hNEXT = m_Tree.GetNextItem( 
hSELECT, TVGN_NEXT );    // 弟
            
            if( hNEXT == NULL )
            {
                m_Tree.Expand( hCHILD, TVE_EXPAND );    
                hCHILD = m_Tree.GetNextItem( hCHILD, 
TVGN_CHILD );    // 子
                if( hCHILD != NULL )
                {
                    for(;;)
                    {
                        m_Tree.Expand( hCHILD, 
TVE_EXPAND );
                        hCHILD = 
m_Tree.GetChildItem( hCHILD );
                        if(hNEXT == NULL)
                        {
                            break;
                        }
                    }
                }
            }
            else // ( hNEXT != NULL )
            {
                m_Tree.Expand( hNEXT, TVE_EXPAND );    
                HTREEITEM hCHILD = m_Tree.GetNextItem( 
hNEXT, TVGN_CHILD );    // 子
                if( hCHILD != NULL )
                {
                    m_Tree.Expand( hCHILD, 
TVE_EXPAND );    
                    hCHILD = m_Tree.GetChildItem( 
hCHILD );
                }
            }
        }
    }

}

επιστημη 2006/07/12(水) 19:47:48
えーと、これのどこが再帰になってんでしょか?
再帰とは"自分自身を呼び出す"ことなんですけど。

三草 2006/07/13(木) 09:49:55
ご参考までにεπιστημηさんのサンプルをお借りし、説明(?)すると


//ダイアログ初期化処理
BOOL CTreeTest::OnInitDialog()
{
  ・・・
  //ツリーにアイテムを追加する処理

  ・・・
  ノード node = 最初のノード;
  展開する( node );
  ・・・
}

//ツリーを展開させる処理
void CTreeTest::展開する(ノード node) {
  ・・・
  if ( node は子ノードを持つ ) {
    for ( 各子ノード child に対し ) {
      展開する(child);        // ←←← ここで自分自身を呼び出しています
    }
  } else {
    nodeをexpandする。
  }
  ・・・
}

こう 2006/07/13(木) 11:19:09
>επιστημηさん
申し訳ありません。
確かに再帰的ではありません。
自分なりの勝手な解釈のもと作成してしまいました。
お恥ずかしい限りです。
επιστημηさんに質問があります。
>展開する(ノード node) {
>  if ( node は子ノードを持つ ) {
>    for ( 各子ノード child に対し ) {
>      展開する(child);
>    }
この中でforを使う理由は何でしょうか。
どういった意図で使ってるのかがわかりませn。
お手数ですが、どう考えているのかご教授お願いします。

>三草さん
サンプルありがとうございます。
私の中で考えたソースを以下に示します。
この処理のままですと一列目の子は展開できますが、
弟は展開できない状況です。

BOOL CMGLDlg::OnInitDialog()
{
  ・
  ・
  ・
 //ツリーにアイテムを追加する処理
 HTREEITEM rootFirstItem = m_Tree.GetFirstVisibleItem();        
 HTREEITEM hSELECT = m_Tree.GetChildItem(rootFirstItem);
 if(hSELECT != NULL)
 {
  m_Tree.Expand( rootFirstItem, TVE_EXPAND);
  Open( hSELECT );     // ツリー展開関数
 }
}
void CMGLDlg::Open( HTREEITEM hSELECT )
{
 m_Tree.Expand( hSELECT, TVE_EXPAND);
 if( hSELECT != NULL )
 {
  HTREEITEM hCHILD  = m_Tree.GetNextItem( hSELECT, TVGN_CHILD ); // 子
  Open( hCHILD );
 }
 else
 {
  m_Tree.Expand( hSELECT, TVE_EXPAND);
 }
}

ただいま弟、また弟の子を表示させれる考案中です。
ご教授ありがとうございます。

Blue 2006/07/13(木) 11:25:08
> どういった意図で使ってるのかがわかりませn。

> 弟は展開できない状況です。
を実現するためです。

> m_Tree.GetNextItem( hSELECT, TVGN_CHILD ); 
では 一列目の子 しか取得できません。
すべての子を取得するためにループさせる必要があります。

ちなみに

> void CMGLDlg::Open( HTREEITEM hSELECT )
の直後の
> m_Tree.Expand( hSELECT, TVE_EXPAND);
はいらないのでは?

こう 2006/07/13(木) 11:37:30
>Blueさん
早急な回答ありがとうございます。
どうすれば弟を展開できるようなプログラムがかけるんでしょうか。
現在、GetNextSiblingItemとGetPrevVisibleItemを使おうと試みている状況です。
お手数ですがご教授よろしくお願いします。

>> void CMGLDlg::Open( HTREEITEM hSELECT )
>の直後の
>> m_Tree.Expand( hSELECT, TVE_EXPAND);
>はいらないのでは?
ソース上では問題ないとおもうのですが、
起動したときに上記の処理を書かないと、
ひとつだけしか展開されません。
回避案として現在追加記述したもんです。

以下のように表示されます。
 □
 +□

επιστημη 2006/07/13(木) 11:50:42
void CXXXDlg::expandRecursive(HTREEITEM item) {
  // item == 0 なら根ノードから始める
  if ( item == 0 ) {
    item = tree.GetRootItem();
  }
  // itemをExpandする
  tree.Expand(item,TVE_EXPAND);
  // 子ノードがあるなら、それをexpandRecursiveする
  HTREEITEM child;
  if ( (child = tree.GetChildItem(item) ) != 0 ) {
    expandRecursive(child);   
  }
 // 続く弟をexpandRecursiveする
  HTREEITEM sibling = item;
  while ( (sibling = tree.GetNextItem(sibling, TVGN_NEXT)) != 0 ) {
    expandRecursive(sibling);
  }
}

で、expandRecursive(0) すれば全部展開。

こう 2006/07/13(木) 12:03:16
>επιστημηさん
早急な返信ありがとうございます。
上記のソースをみて何をするべきだったのかが明確になりました。
誠にありがとうございます。

επιστημη 2006/07/13(木) 12:39:24
納得しないで。無駄が多いから。

// これで十分なハズ
void CXXXDlg::expandRecursive(HTREEITEM item) {
  if ( item != 0 ) tree.Expand(item,TVE_EXPAND);
  for ( item = item == 0 ? tree.GetRootItem() : tree.GetChildItem(item);
        item != 0;
        item = tree.GetNextItem(item, TVGN_NEXT) ) {
    expandRecursive(item);   
  }
}

で、expandRecursive(0) すれば全部展開。

こう 2006/07/13(木) 12:46:35
[[解決]]
>επιστημηさん
ご注意ありがとうございます。
こんなに短いソースで可能なんですね。
安直に考えすぎてました。

今回はこんなにもお世話になり、
誠にありがとうございます。
今後ともよろしくお願いします。

敬具

subaru 2006/07/13(木) 12:52:58
余談ですがツリービューコントロールには
テンキーの'*'キーで全展開する機能があるので
フォーカスを合わせてから
tree.SendMessage(WM_KEYDOWN, VK_MULTIPLY);
のようなコードだけでも全展開してくれるっぽいです。
もちろん正攻法ではないのでオススメはしませんが。

こう 2006/07/13(木) 13:04:02
>subaruさん
おっしゃるとおりデスクトップのPC当についてるテンキーならできました。
ただノートPCでやった場合はどうなのでしょうか。
現在手元にノートPCがないのでわかりませんが。

「*」に関しては今後使わせていただきます。
ありがとうございます。

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