• ベストアンサー

ツリーコントロールとツリー構造のデータとのリンク

ツリーコントロールとツリー構造のデータとのリンク 私が開発しているソフトウェアは、データ構造として ツリー構造を使っています。このツリー構造のデータ を表示するためにツリーコントロールを使って いますが、データとツリーコントロールのリンクする 方法として良い方法を探しています。 ここでいう「リンク」とは、例えば ツリー構造のデータにデータの追加や削除が おこなわれた場合、該当するツリーコントロールの データも追加と削除をおこなう。ことです。 現時点では、ツリー構造のデータにデータの追加や 削除がおこなわれたら、ツリーコントロールに SendMessageを送ってツリーコントロール側の データの追加や削除をおこなっています。この場合、 ツリー構造のデータのクラスに、GUIのクラスの ポインタを保持しています(ツリーコントロール へのSendMessageのためにCWnd*を保持している)。 データにGUIに関わるコードが存在するので、 GUIに依存しない方法に変えたいのですが、みなさんは このような場合はどうしていますか? ちなみに現時点では、Design PatternのObserver Patternを採用してみようと思っています。 他に良い方法があれば教えてください。 よろしくお願いします。 開発環境:VC++6.0, MFC

質問者が選んだベストアンサー

  • ベストアンサー
  • buuchan1
  • ベストアンサー率31% (6/19)
回答No.4

私が話したいのは以下のようなことです。 (×) GUIに依存したデータクラス (○) 特定のデータを扱えるGUIクラス ここから先は、住所録を例にします。 住所録のデータを以下のように定義します。 class CAddressData {  LPCTSTR Name; // 名前  LPCTSTR Adress; // 住所  LPCTSTR Phone; // 電話番号 }; 住所録の場合、複数件同時に扱うはずですから、以下の形で 保持することにします。 CList<CAddressData, CAddressData&> m_AddressBook; これをツリー形式で表示するならば、エクスプローラのよう な形をイメージする人が多いかと思います。つまり、左側に ツリー、ツリーで選択中の情報を右側のリストに詳細表示と いうものです。 この場合、ツリーとリストは同じ住所録というデータを扱っ ていて、ただ表示の方法が違うだけだと言うことは分かりま すよね。 なので、pugooさんがやったように、[GUIに依存したデータク ラス]にしてしまうとツリーとリスト双方にSendMessageしな ければいけなくなります。 そして、GUIが変わった(コンボボックス+リストになったな ど)だけで、SendMessage先が変わります(= データクラスの実 装が変わる)。 データフォーマットが変わってないのに、データクラスの実 装が変わるのはおかしくないですか? これが、No.3の人の言う[データーとその表現が一体化してい てはツブシが効かない]です。 ところが、pugooさんのデータとツリーコントロールのリンク という考え自体方は非常に重要な考え方なのです。 発想を逆転して、[データをGUIに結びつける]のでは無く、 [GUIを特定データに特化]します。GUIの派生クラスを作って、 特定データを直接扱えるようにします。派生というのは特化と 呼ばれることもあるぐらいですから、 これはごく自然な流れです。 (例) ツリーの場合  (電話番号を[市外局番][市内局番]にツリー分けして追加する) CAdressTreeCtrl::InsertItem(CAddressData* pData) {  // ツリーアイテムを追加  HTREEITEM hSigaiItem = (pData->Phoneより市外局番の挿入位置を算出);  HTREEITEM hSinaiItem = (pData->Phoneより市内局番の挿入位置を算出);  HTREEITEM hCurItem =   CTreeCtrl::InsertItem(pData->Name, hSigaiItem, hSinaiItem);  // データを記憶  CTreeCtrl::SetItemData(hCurItem, (DWORD)pData); // これがミソ } このとき、InsertItem()の際に、追加したデータ自身を SetItemData()しておくのがミソです。このおかげで、GUIイ ベント発生時のデータ更新は、GetItemData()一発でm_AddressBook を介さずに中身のデータへ直リンクです。 質問にあったソフトウェア的にツリーのデータを追加と削除 ですが、追加を例にすると class CAdressView {  CList<CAddressData, CAddressData&> m_AddressBook; // データ  CAdressTreeCtrl m_AdressTreeCtrl; // ツリー public:  void AddAdress(CAddressData& rData)  {   POSITION Pos = m_AddressBook.AddTail(rData);   m_AdressTreeCtrl.InsertItem(&m_AddressBook.GetAt(Pos));  } } のような感じで実装を行い、CAdressView::AddAdress()をコ ールすると、データとコントロールが有機的に結びつきます。 さらに、CAdressViewの外側の人からは、CAdressViewが CAddressDataを直接扱うクラスのように見えます。このブラ ックボックス化こそが、オブジェクト指向で言うところの本 質的な隠蔽というものです。 以上、簡単ですが理解していただけたら幸いです。

pugoo
質問者

お礼

ご回答ありがとうございます。 > InsertItem()の際に、追加したデータ自身を > SetItemData()しておくのがミソです。 これはわかります。私もSetItemData、GetItemDataを 利用しています。しかし、CAdressViewを使った例が わかりません。具体的には、CAdressView::AddAdress をコールするのはわかりますが、このクラスの オブジェクトをどこに保持して、どのようにして オブジェクト(のポインタ)を取得するかがわかりません。 その辺りはどうお考えですか?

その他の回答 (4)

  • buuchan1
  • ベストアンサー率31% (6/19)
回答No.5

MFCのドキュメント&ビューだったら、 CAdressViewは実際のビュー(CView派生クラス)かな。 CAdressViewのメンバ変数としてCAdressTreeCtrlやCAdressListCtrlがいる感じです。 フレームの中にビューがあって、さらにビューの中にツリーとリストがあると思えばわかりやすいでしょう。 また、ドキュメント&ビューであれば、やはり CList<CAddressData, CAddressData&> m_AddressBook; はドキュメントクラスのメンバでしょう。 そんな感じですが、イメージ沸きますでしょうか? わかりにくいようなら、簡単なサンプルでも作成します。

pugoo
質問者

お礼

ご回答ありがとうございます。 返信が遅くなりすみません。 なんとなくですがわかりました。 自分で簡単なプログラムを作って試してみます。

回答No.3

ANo.1 > CTreeCtrlの派生クラスを作って、ツリーデータを直接追加できるようにするのが、一般的なオブジェクト指向の考え方です。 違うんじゃない? データーとその表現が一体化していてはツブシが効かない。

pugoo
質問者

お礼

buuchan1さんの考えがよくわからないので、 buuchan1さんの回答を待ちたいと思います。

  • terra5
  • ベストアンサー率34% (574/1662)
回答No.2

今までそのケースは経験ありませんが、 多分GUIを含まないツリー構造のデータのクラスを作り、 それを継承してGUI依存部を実装すると思います。 こうすればGUI不要なら元のクラスを使えばいいし、 別のGUI環境下ならGUI依存部のみ書き換えればいいんじゃないかと。

pugoo
質問者

お礼

ご回答ありがとうございます。 他の掲示板にも同じ質問を書き込んでいたのですが、 (マルチポストは嫌われるとご指摘がありましたが) 同じような意見をいただきました。 ぜひ参考にさせていただきます。

  • buuchan1
  • ベストアンサー率31% (6/19)
回答No.1

この場合、考え方が全く逆でしょう。 MFCということはCTreeCtrlを使っているかと思いますが、CTreeCtrlの派生クラスを作って、ツリーデータを直接追加できるようにするのが、一般的なオブジェクト指向の考え方です。 (例) CTreeCtrlの派生クラス、CMyTreeCtrlを作成し、ツリーデータを格納するクラスCMyDataを直接追加できるように CMyTreeCtrl::SetItemData(CMyData)関数をオーバーライドします。 このとき、CMyDataクラスのメンバ変数にツリーコントロールに表示するテキストを含めてやれば、CMyTreeCtrl::SetItemData(CMyData)関数内からCTreeCtrl::SetItemText()を呼ぶことにより、ツリーコントロールに対して、データの設定とテキストの設定を同時に行うことができます。

pugoo
質問者

お礼

ご回答ありがとうございます。 > この場合、考え方が全く逆でしょう。 すみません。よくわかりませんので 詳しく教えていただけないでしょうか? よくわからない点は、 ユーザがツリーコントロール(CTreeCtrl)の データを選択して、データの追加や削除の コマンドを実行した場合はご回答いただいた 方法で良いと思いますが、ソフトウェア的に ツリーのデータを追加と削除をおこなう場合は どうでしょうか? ソフトウェア的にとは、ツリー構造のデータに 対して、データの追加と削除をおこなった場合です。 この場合はツリーコントロールの更新をおこなう 必要があるとおもいますが、それをどうやって 実現するのでしょうか? 簡単な方法として、ツリーコントロールのデータを 全て削除して(CTreeCtrl::DeleteAllItems)から 再度全データを追加する(CTreeCtrl::InsertItem) 方法がありますが、これではデータ量が多い場合は ツリーコントロールの更新に時間がかかって しまいます。それに、ユーザによって展開されていた ノードが全て閉じた状態になってしまいます。 ご回答いただいた方法に対して私の理解が間違って いましたらすみません。

関連するQ&A