トップ回答者
ドッキングウィンドウ上のコントロールとのやりとり

質問
-
現在MFCのSDIアプリにて、MainFrameにCDockPane派生のドッキングウィンドウを配置し、そこにスライダーコントロールを配置しています。
スライダーの操作が行われたら、ドキュメントクラス内でスライダーポジションに応じた処理を行いたいと考えています。
そこで、
(1)ドッキングウィンドウのスライダー操作のイベントをドキュメントクラスに通知するにはどのようにするのが一般的でしょうか?
コマンドメッセージをPostMessageするような形でしょうか?
(2)ドキュメントクラスからスライダーの位置を知るにはどのようにするのが一般的でしょうか?
DocumentクラスにCMainFrm.hをincludeし、CMainFrameにスライダーの値を取得するメソッドを追加するような形でしょうか?そのような実装は出来たのですが、DocumentクラスとMainFrameクラスの独立性が損なわれているような気がして、他に何か良い方法があれば教えていただけないでしょうか?
回答
-
そんな感じだと思いますが、
ドキュメントが自らを表示する機能を持つ様にコードすると、
ドキュメント自体が肥大化する傾向が強くなり、後で手に負えなくなります。
従って、以下の擬似コードのように、SDIView::OnDraw()
{
// フレームの取得と描画
int Sld_Cur_Pos = 編集制御クラス->スライダ位置の取得関数();
フレーム * Cur_frame = GetDocument()->フレームの取得関数( Sld_Cur_Pos);
Draw_One_Frame( Cur_frame); // カレントフレームの描画
}
のようなコードのようにして、ドキュメントはなるべくシンプルに
しておくほうが良いかもしれません。
1.ドキュメントは、指定された素材を提供するだけ。
2.それをどのように表現するか。がViewの仕事。
というわけですね。
すべての返信
-
「SDIドキュメント」の寿命と「対照スライダ位置値」の寿命が
一致しているものと仮定し、ドキュメントの属性と仮定します。この場合「対照スライダ位置値」はドキュメントのメンバにするのが
もっとも簡単な実装となります。
従って、1.CDocTemplate::OpenDocumentFile()の戻り値である
ドキュメントクラスのインスタンスは、
アプリケーション寿命に一致する何らかのクラスに
保持させるのがもっとも簡単。となります。
これ(ドキュメント)を保持するクラスは一般的にはアプリケーションクラスです。
本ケースの場合、そのドキュメントを取得する関数Get_SDI_Document()が
あれば十分という結論になります。つまり、void スライダの親ペイン::Onスライダの報告()
{
theApp.Get_SDI_Document()->スライダの位置値の設定関数( 報告された位置値);
}といったコードでよいでしょう。
この場合、ドキュメントは「対照スライダ位置値」がメンバなので自明です。またこの値が永続記憶として保管される場合、OpenDocumentFile()された後、
「対照スライダ位置値」を、対象スライダに再現しなければなりません。2.アプリケーションは、メインフレーム経由で、各ペイン構築後、
「対照スライダ位置値」の再現手順を実装する必要があるでしょう。
さて、この設計は、最初の仮定が前提です。従って、そうでない場合は、
当たり前ですが、そのスライダ値の意味によって、実装がまったく異なります。
その場合は、その旨ご指摘ください。 -
>動画プレーヤのようなもので、画面下側にシークバーを実装したいと考えていました。
シークバーの位置が表示中のカレントフレーム番号など、
「ドキュメント内の位置」を意味する場合は、
それは「ドキュメントの属性」ではなく「編集パラメータ」の一種として
実装すべきだと考えられます。
テキストエディタのキャレットの位置と類似していると考えられます。この場合、新たに「編集制御用クラス」を用意し、その属性として、
「ドキュメント内の位置」を実装すべだと考えられます。
「編集制御用クラス」はアプリケーションに保持され、
カレントのドキュメントと、現在の動作モードモード
たとえば「プレイ/ストップ/ポーズ」等の編集用パラメータ
とともに「ドキュメント内の位置」を保持する様に実装した方が良いと考えます。「編集制御用クラス」は制御ボタン等のコントロールと密接に関わるため、
唯一の実体をアプリケーションに保持、その唯一性を保障した上で、
メインフレームを含む他のペインはその参照(ポインタ)を保持してかまいません。
この方が、もう少しきれいな実装になると思われます。- 編集済み 仲澤@失業者 2014年5月28日 0:58
-
仲澤@失業者様
返信が遅くなり申し訳ありませんでした。
非常に参考になるご意見ありがとうございます。
またしても教えていただきたいのですが、編集制御用クラスをアプリケーションに持たせた場合、スライダーが制御されると1.スライダーを含むペインでWM_HSCROLLを補足
2.スライダーのペインが持つ編集制御クラスのポインタ経由で、編集制御クラスの位置属性を設定
3.編集制御クラスから
theApp.Get_SDI_Document()->Seek(pos);
のような流れでカレントフレームを更新、といったイメージでしょうか?
なお現在Documentクラスには、DirectShowでファイル読み込み等を行うクラスを保持させています。
-
そんな感じだと思いますが、
ドキュメントが自らを表示する機能を持つ様にコードすると、
ドキュメント自体が肥大化する傾向が強くなり、後で手に負えなくなります。
従って、以下の擬似コードのように、SDIView::OnDraw()
{
// フレームの取得と描画
int Sld_Cur_Pos = 編集制御クラス->スライダ位置の取得関数();
フレーム * Cur_frame = GetDocument()->フレームの取得関数( Sld_Cur_Pos);
Draw_One_Frame( Cur_frame); // カレントフレームの描画
}
のようなコードのようにして、ドキュメントはなるべくシンプルに
しておくほうが良いかもしれません。
1.ドキュメントは、指定された素材を提供するだけ。
2.それをどのように表現するか。がViewの仕事。
というわけですね。 -
あまり参考にはならないと思いますが、
自分が初歩的なオブジェクト指向を学んだ時代には、
とっかかりとして擬人化を勧められました。
まずアプリケーションに名前をつけて論理的上の人格とした上で、
その役割と機能を宣言します。その役割や機能を詳細化する過程での
項目や部分も全て命名した上で人格化するといった手法でした。そののちGoFを読み、デザインパターンの説明の難解さを知りました。
それらの多くは、そのままでは実務的に利用不可能なほど抽象的な難物でした。
つまり、これらを利用するためには、具体的に扱う対象の「抽象的な本質は何なのか」を
見抜く必要があったのです。で、それの練習をしました。
蛇口や扉の本質的機能は「通過させること」と「止めること」ではないかなど、
簡単で具体的な実物についてあえて考えてみるといった、脳の筋トレですね。
そうして、やっとドキュメントクラスに求められる本質的機能について
何か言えるようになるわけです。
というわけで、まだまだ先人の頭の良さにはついていけてません。