プロが教えるわが家の防犯対策術!

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

私が開発しているソフトウェアは、データ構造として
ツリー構造を使っています。このツリー構造のデータ
を表示するためにツリーコントロールを使って
いますが、データとツリーコントロールのリンクする
方法として良い方法を探しています。

ここでいう「リンク」とは、例えば
ツリー構造のデータにデータの追加や削除が
おこなわれた場合、該当するツリーコントロールの
データも追加と削除をおこなう。ことです。

現時点では、ツリー構造のデータにデータの追加や
削除がおこなわれたら、ツリーコントロールに
SendMessageを送ってツリーコントロール側の
データの追加や削除をおこなっています。この場合、
ツリー構造のデータのクラスに、GUIのクラスの
ポインタを保持しています(ツリーコントロール
へのSendMessageのためにCWnd*を保持している)。

データにGUIに関わるコードが存在するので、
GUIに依存しない方法に変えたいのですが、みなさんは
このような場合はどうしていますか?
ちなみに現時点では、Design PatternのObserver
Patternを採用してみようと思っています。
他に良い方法があれば教えてください。
よろしくお願いします。

開発環境:VC++6.0, MFC

A 回答 (5件)

私が話したいのは以下のようなことです。


(×) 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を直接扱うクラスのように見えます。このブラ
ックボックス化こそが、オブジェクト指向で言うところの本
質的な隠蔽というものです。
以上、簡単ですが理解していただけたら幸いです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

> InsertItem()の際に、追加したデータ自身を
> SetItemData()しておくのがミソです。

これはわかります。私もSetItemData、GetItemDataを
利用しています。しかし、CAdressViewを使った例が
わかりません。具体的には、CAdressView::AddAdress
をコールするのはわかりますが、このクラスの
オブジェクトをどこに保持して、どのようにして
オブジェクト(のポインタ)を取得するかがわかりません。
その辺りはどうお考えですか?

お礼日時:2005/09/24 02:05

MFCのドキュメント&ビューだったら、


CAdressViewは実際のビュー(CView派生クラス)かな。
CAdressViewのメンバ変数としてCAdressTreeCtrlやCAdressListCtrlがいる感じです。
フレームの中にビューがあって、さらにビューの中にツリーとリストがあると思えばわかりやすいでしょう。
また、ドキュメント&ビューであれば、やはり
CList<CAddressData, CAddressData&> m_AddressBook;
はドキュメントクラスのメンバでしょう。

そんな感じですが、イメージ沸きますでしょうか?
わかりにくいようなら、簡単なサンプルでも作成します。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
返信が遅くなりすみません。

なんとなくですがわかりました。
自分で簡単なプログラムを作って試してみます。

お礼日時:2005/09/28 10:08

ANo.1


> CTreeCtrlの派生クラスを作って、ツリーデータを直接追加できるようにするのが、一般的なオブジェクト指向の考え方です。

違うんじゃない? データーとその表現が一体化していてはツブシが効かない。
    • good
    • 0
この回答へのお礼

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

お礼日時:2005/09/21 09:06

今までそのケースは経験ありませんが、


多分GUIを含まないツリー構造のデータのクラスを作り、
それを継承してGUI依存部を実装すると思います。

こうすればGUI不要なら元のクラスを使えばいいし、
別のGUI環境下ならGUI依存部のみ書き換えればいいんじゃないかと。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

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

ぜひ参考にさせていただきます。

お礼日時:2005/09/21 09:00

この場合、考え方が全く逆でしょう。


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

ご回答ありがとうございます。

> この場合、考え方が全く逆でしょう。

すみません。よくわかりませんので
詳しく教えていただけないでしょうか?

よくわからない点は、
ユーザがツリーコントロール(CTreeCtrl)の
データを選択して、データの追加や削除の
コマンドを実行した場合はご回答いただいた
方法で良いと思いますが、ソフトウェア的に
ツリーのデータを追加と削除をおこなう場合は
どうでしょうか?
ソフトウェア的にとは、ツリー構造のデータに
対して、データの追加と削除をおこなった場合です。
この場合はツリーコントロールの更新をおこなう
必要があるとおもいますが、それをどうやって
実現するのでしょうか?

簡単な方法として、ツリーコントロールのデータを
全て削除して(CTreeCtrl::DeleteAllItems)から
再度全データを追加する(CTreeCtrl::InsertItem)
方法がありますが、これではデータ量が多い場合は
ツリーコントロールの更新に時間がかかって
しまいます。それに、ユーザによって展開されていた
ノードが全て閉じた状態になってしまいます。

ご回答いただいた方法に対して私の理解が間違って
いましたらすみません。

お礼日時:2005/09/20 08:54

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!