ページ 11

[C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 16:36
by オカピーα
いつもお世話になっております、毎度C#で申し訳ありません。
今回、ツリービューを使った設定画面を作ろうとしています。
構想としては、左ペインにツリービュー、右ペインには選択ノードの詳細画面を表示させようと思います。
そのためには、ツリービューのノードと項目を保存するクラスを紐付けなければならないのですが
一体どうやったらいいのかまったく分かりません。
以前リストボックスで作ったときは、リストボックスのインデックス番号があるので簡単でしたが
ツリービューにはインデックスという概念はないようです。階層構造なので。

どうしたらいいのでしょう?解決策を教えてください。

言語:C#
開発環境: Windows XP , Visual Studio 2008

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 16:57
by オカピーα
イメージで言うと、こういうことがやりたいです

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 17:25
by YuO
WinFormsであれば,個人的にはTreeNodeを継承したクラスを作り,そこに右側表示用のコントロールを紐付けられるようにしておきます。
そして,選択されたタイミングで右側に表示されるコントロールをTreeNodeから取得して切り替えます。

まぁ,WPFでDataTemplate使う,でやっちゃえそうな気もしますが。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 17:45
by オカピーα
YuO さんが書きました:WinFormsであれば,個人的にはTreeNodeを継承したクラスを作り,そこに右側表示用のコントロールを紐付けられるようにしておきます。
そして,選択されたタイミングで右側に表示されるコントロールをTreeNodeから取得して切り替えます。

まぁ,WPFでDataTemplate使う,でやっちゃえそうな気もしますが。

おぉ、なるほど!出来そうな気がしてきました!
WPFでの開発予定はありません、とりあえずWinFormでがんばっています。
ところで、「紐付ける」というのは、TreeNodeを継承したクラス内にメンバとして保存用クラスを保持する、ということで
いいのでしょうか?それとも、もっと高度な技があるのでしょうか?

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 18:00
by YuO
まず,右側に表示する内容はすべてUserControlにします。
次に,TreeNodeを継承したクラスは,UserControlを型とするpublicプロパティを用意しておきます。
setに関してはコンストラクタ引数にして,プロパティはgetのみとしてもよいでしょう。

で,TreeにTreeNodeを継承したクラスのインスタンスを追加する時 (property-set) or 作成する時 (constructor) に,
上記で作成したプロパティに表示するためのUserControlを設定します。
TreeNodeはViewの物なので,UserControlを直接持っても問題ないと思います。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月27日(月) 20:00
by オカピーα
YuO さんが書きました:まず,右側に表示する内容はすべてUserControlにします。
次に,TreeNodeを継承したクラスは,UserControlを型とするpublicプロパティを用意しておきます。
setに関してはコンストラクタ引数にして,プロパティはgetのみとしてもよいでしょう。

で,TreeにTreeNodeを継承したクラスのインスタンスを追加する時 (property-set) or 作成する時 (constructor) に,
上記で作成したプロパティに表示するためのUserControlを設定します。
TreeNodeはViewの物なので,UserControlを直接持っても問題ないと思います。
なんとなくイメージは出来るのですが、コード化できません・・・質問させてください。
「上記で作成したプロパティに表示するためのUserControlを設定します」が分かりません。
すいませんできない子で・・・ 教えてください。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月28日(火) 12:15
by YuO
ListBoxでも同じだと思うんですけどね……。
単純に,View部品に関係するView部品を持たせてしまうだけですから。
AとBの間に関連がある,というのであれば,それをオブジェクトの中に関連として保持します。
外側に関連を保持するのは,その関連が分散してしまう可能性があります。

ListBoxであってもインデックスを使う,というのは上記に反するので悪手で,
ListBoxに「表示文字列」と「表示用ユーザーコントロール」を取得するためのプロパティを持つクラスのインスタンスを追加しておき,
表示はDisplayMemberで制御,コントロールの変化はSelectedItem経由で行う,というのが正攻法です。

これと同じく,TreeViewでもTreeNodeを継承したクラスには「表示用ユーザーコントロール」を取得するためのプロパティを用意します。
TreeNodeが選択されたら,そのノードから「表示用ユーザーコントロール」を取得し,それを表示します。
ここで,「表示用ユーザーコントロール」をどうやって設定するか,というのにコンストラクタを使うかプロパティへの設定で行うかは,要件次第となります。
オフトピック
2006年頃(=VS2005が出た頃)に,プラグインシステムでの設定用に考えた物が土台です。
このため,このような仕組みになっているところはあります。

WPFのようにModelの型から表示するユーザーコントロールを決定する方法もありますが,WinFormsでは
  • TreeView / ListViewはデータバインディングに対応していない (選択しているView部品にバインドしているModelが取得できない)
  • Modelの型に合わせてコントロールを切り替えるシステムが組み込まれていない
という理由から不向きだと思います。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月28日(火) 14:12
by オカピーα
YuO さんが書きました:ListBoxでも同じだと思うんですけどね……。
単純に,View部品に関係するView部品を持たせてしまうだけですから。
AとBの間に関連がある,というのであれば,それをオブジェクトの中に関連として保持します。
外側に関連を保持するのは,その関連が分散してしまう可能性があります。

ListBoxであってもインデックスを使う,というのは上記に反するので悪手で,
ListBoxに「表示文字列」と「表示用ユーザーコントロール」を取得するためのプロパティを持つクラスのインスタンスを追加しておき,
表示はDisplayMemberで制御,コントロールの変化はSelectedItem経由で行う,というのが正攻法です。

これと同じく,TreeViewでもTreeNodeを継承したクラスには「表示用ユーザーコントロール」を取得するためのプロパティを用意します。
TreeNodeが選択されたら,そのノードから「表示用ユーザーコントロール」を取得し,それを表示します。
ここで,「表示用ユーザーコントロール」をどうやって設定するか,というのにコンストラクタを使うかプロパティへの設定で行うかは,要件次第となります。
オフトピック
2006年頃(=VS2005が出た頃)に,プラグインシステムでの設定用に考えた物が土台です。
このため,このような仕組みになっているところはあります。

WPFのようにModelの型から表示するユーザーコントロールを決定する方法もありますが,WinFormsでは
  • TreeView / ListViewはデータバインディングに対応していない (選択しているView部品にバインドしているModelが取得できない)
  • Modelの型に合わせてコントロールを切り替えるシステムが組み込まれていない
という理由から不向きだと思います。


ありがとうございます!本当に参考になります!
いただいたコードを元に、自分なりに作ってみました!(例外処理とかは究極に手を抜いています)


ただ、ひとつ問題が・・・
このプロジェクトでは、入力された情報を、別途ファイルに保存し、次回起動時に復元しなければならないのですが・・・
ツリービューと情報クラスとの紐をどうやって保存するか見当もつきません・・・
どうしたらいいのでしょうか、引き続きお願いできましたら幸いです。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月28日(火) 16:37
by YuO
オカピーα さんが書きました:このプロジェクトでは、入力された情報を、別途ファイルに保存し、次回起動時に復元しなければならないのですが・・・
ツリービューと情報クラスとの紐をどうやって保存するか見当もつきません・・・
どうしたらいいのでしょうか、引き続きお願いできましたら幸いです。
ツリービューと情報クラスは紐付く必要はないでしょう。
Form自体がModelへの参照は保持していると思うので,Model自身かその一部をユーザーコントロールに渡します。
このあたりは,TreeNodeへの拡張と同じ方法です。
ただし,UserControlをデザイナで使うためにはデフォルトコンストラクタが必要なので,

コード:

private Models.Model _model;
public UserControl1 () // デザイナで必要
{
    InitializeComponents();
    _model = null; // とりあえず,モデルをnullにしておく
}

public UserControl1 (Models.Model model) : this() // デフォルトコンストラクタを必ず呼び出す
{
    _model = model; // 渡されたモデルを保持
}

protected override void OnLoad (EventArgs e)
{
    base.OnLoad(e);
    if (_model != null) modelBindingSource.DataSource = _model; // データバインドする
}
のような書き方になります。

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月28日(火) 17:22
by オカピーα
YuO さんが書きました:
オカピーα さんが書きました:このプロジェクトでは、入力された情報を、別途ファイルに保存し、次回起動時に復元しなければならないのですが・・・
ツリービューと情報クラスとの紐をどうやって保存するか見当もつきません・・・
どうしたらいいのでしょうか、引き続きお願いできましたら幸いです。
ツリービューと情報クラスは紐付く必要はないでしょう。
Form自体がModelへの参照は保持していると思うので,Model自身かその一部をユーザーコントロールに渡します。
このあたりは,TreeNodeへの拡張と同じ方法です。
ただし,UserControlをデザイナで使うためにはデフォルトコンストラクタが必要なので,

コード:

private Models.Model _model;
public UserControl1 () // デザイナで必要
{
    InitializeComponents();
    _model = null; // とりあえず,モデルをnullにしておく
}

public UserControl1 (Models.Model model) : this() // デフォルトコンストラクタを必ず呼び出す
{
    _model = model; // 渡されたモデルを保持
}

protected override void OnLoad (EventArgs e)
{
    base.OnLoad(e);
    if (_model != null) modelBindingSource.DataSource = _model; // データバインドする
}
のような書き方になります。

モモモモモモモモモデルって何でしょうか
まさかデザインパターンでしょうか?デザインパターンが出るんでしょうか?
デザインパターンがお出ましするんでしょうか!?うわぁー!世直しだぁあー!!助けてくれー!!


・・・すいません取り乱しました もうちょっと簡単な方法はないですか 泣きそうです(´;ω;`)

Re: [C#] ツリービューを使った設定画面

Posted: 2013年5月29日(水) 01:40
by YuO
モデルというのは,単純にView,見た目以外の部分を指しています。
情報クラスはモデルの一部のはずです。

結局の所,
  • フォームで必要な情報は,クラスにまとめておく
  • ユーザーコントロールで必要な情報は,クラスにまとめておく
  • フォームまたはユーザーコントロールに属するコントロール (ユーザーコントロール含む) で必要な情報は,そのフォームまたはユーザーコントロールで必要な情報が集まったクラスのプロパティとして提供する
という形のクラスが必要になります。
これらをデータバインドなり,手作業でバインドするなりして,コントロールの状態と情報を持ったクラスの状態を一致させるようにします。
この時,情報を持ったクラスにはView情報を極力含ませないようにします。
保存や復元は,この情報を持ったクラスの保存や復元であり,BinaryFormatterXmlSerializerといったSerializerを使うことで簡単に保存・復元ができます。
オフトピック
効率その他の理由で,現在ではDataContractSerializer等が推奨されますが,.NET 3.0以降なので除外。
ref) neue cc - .NET(C#)におけるシリアライザのパフォーマンス比較

あと,上記の方法で作ったクラスはView由来のクラスなので,PDS的にはP側のクラス。本来ならD側のクラスがあって,そちらをシリアライズしたいところ。

GUIアプリケーションを単純に作るだけであれば力業でもある程度できますが,大規模になればなるほど,Presentation Domain Separation (PDS)を考慮したアーキテクチャの採用が必要になります。
有名どころとしてMVCがあり,その派生として色々あるわけですが……。 あたりは,読んでおくとよいと思います。
オフトピック
こういう時の参考URLを引っ張ってくるのに,【雑談】mixC++勉強会@Tokyo • C言語交流フォーラム ~ mixC++ ~で使ったスライドの末尾の参考資料を使うのですが,
Presentation-Domain Separationでググったらなんかslideshareに上げたスライドが5番目 (かつ尾上氏の記事の下かつ尾上氏の有用なスライドの上) とかいう,非常に申し訳ない位置に来ていてびっくり。
というか,なぜ尾上氏のわんくま大阪#50のスライドが私の下にあるのか理解に苦しむ……