詳細
Q1 : CFormView となるダイアログにリストボックスを付け、子ウィンドウ表示時に
それに初期値を設定しようと思い、CView::OnCreate() をオーバーライドしてその中
で AddString() したのですが、表示されません。何がいけないのでしょう?
A1 : CFormView では OnCreate() のタイミングの時には、まだダイアログのパーツ
部分は生成されていません。ですから AddString() は無効になります。丁度良いタイ
ミングとなる関数は CView::OnInitialUpdate() です。この中で AddString すれば
表示されます。
Q2 : ボタンコントロールを持つ CFormView の派生クラスを作成し、そのボタンが
押されたらその子ウィンドウを閉じたいのです。そこでボタンのメッセージ処理関数
を CFormView の派生クラスに設定し、その中で DestroyWindow() を呼び出したの
ですが、ウィンドウは消えず、その後ボタンが動作しなくなってしまいます。どう
したらよいのでしょう。
A2 : ボタンのメッセージ処理を View クラスに設定するのは良いのですが、そこで
DestroyWindow() を呼び出すと View のみが削除されてしまい、表示だけは残った状
態になっているのでボタンが利かなくなっています。正しくは View の親であるフレー
ムウィンドウ(一般的には CMDIChildWnd )に WM_CLOSE を送ってやることで消去でき
ます。
具体的にはボタンが押された時に処理する関数に以下のコードを記述します。
GetParent() -> PostMessage( WM_CLOSE );
Q3 : CFormView で作成したダイアログとぴったりあったサイズに子ウィンドウ(フ
レームウィンドウ)を表示するにはどのようにしたら良いのでしょう?
A3 : MoveWindow() や PreCreateWindow() などで設定することもできますが、ク
ライアントのサイズと実際のウィンドウのサイズとを計算しなくてはならないので、
小々面倒です。もっと簡単に行うには CScrollView::ResizeParentToFit() を使用す
るのがよいでしょう。
手順としては、CFormView の派生クラスを作り、OnInitialUpdate() をオーバーライ
ドします。その中で最初に CFormView::OnInitialUpdate() を呼び、
CWnd::GetParentFrame() を呼んで、CFormView が貼りついているフレームウィンドウ
のハンドルを得ます。そのハンドルを利用して CFrameWnd::RecalcLayout() を呼び出
します。その後、CScrollView::ResizeParentToFit() を呼び出してサイズを合わせ
ます。
<例>
void CMyFormView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
}
メンバ関数の詳細についてはマニュアルを参照して下さい。
Q4 : CFormView 中のコントロールのフォーカスを任意の位置へ移動したいのですが、
GotoDlgItem() を記述するとエラーになってしまいます。確かに CFormView は
CScrollView の派生クラスなので CDialog のメンバ関数である GotoDlgItem は使
えないとは思います。ですが、フォームビュー表示中にタブキーを押すと、コントロ
ールのフォーカスは移動します。これはどのように処理しているのでしょうか。
A4 : CFormView は CView 系のクラスなので、CDialog クラスのメンバ関数をその
まま使用することはできません。ですが、実際には DDX も使用することができてい
ますし、ダイアログそのものも作成しています。
どうしても CDialog のメンバ関数を使用したい場合は、this ポインタを CDialog
クラスでキャストすることで可能になります。
<例>
// CFormView 中の関数内での記述
((CDialog*) this ) -> GotoNextDlg( GetDlgItem(コントロールの ID) );
Q5 : CFormView のバックグランドは SetDialogBkColor の色の設定が効かないのです
が、どのようにすればよいのでしょう。
A5 : CFormView のバックグランドの色の変更は CFormView 派生クラスでブラシのオブジ
ェクトを作り、OnCtlColor() をインプリメントして、CTLCOLOR_DLG に対してブラシの
ハンドルを返すようにします。
<例>
// Class 定義
class CXxx : public CFormView
{
public:
CBrush m_Brush;
....
};
// コンストラクタ
CFormDlg::CFormDlg()
: CFormView(CFormDlg::IDD),m_Brush(RGB(255,255,0))
{ //バックグランドを RGB(255,255,0) に設定
}
// WM_CTLCOLOR のメッセージ処理関数のインプリメント
HBRUSH CFormDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if(nCtlColor == CTLCOLOR_DLG){
return (HBRUSH)m_Brush.GetSafeHandle();
}
return CFormView::OnCtlColor(pDC, pWnd, nCtlColor);
}
Q6 : CFormView にスパイをあてても FormView から別のウィンドウにフォーカスを
移しても WM_KILLFOCUS が来ません。
A6 : CFormView のフォーカスの移動をよく調べると必ず、WM_KILLFOCUS がくるタイ
ミングがあります。実際にスパイで調べてみると、View にフォーカスが移った時に
KILLFOCUS がくるのが分かります。FormView は通常の View とは違ってダイアログマ
ネージャがイベントの管理を行いますが、これが常にフォーカスを管理しています。
通常はデフォルトフォーカスを持つボタンがフォーカスを持つので、CFormView に
フォーカスを移してもデフォルトフォーカスボタンにフォーカスが移動します。
CFormView::OnKillFocus 関数のパラメータは次にフォーカスを受け取るウィンドウ
オブジェクトですが、このオブジェクトのポインタが何になっているかを確認して
ください。FormView から別のウィンドウにフォーカスを移した場合はそこに
WM_KILLFOCUS が送られているはずです。
Q7 : スクロールバーを持つ CFromView のアプリケーションを実行し、そのスクロ
ールバーをクリックすると「 Assertion Failed VIEWSCRL.CPP Lines 528 」もしく
は 534 が発生します。特に問題のあるようなことはしていないのですが、なぜでし
ょう。
A7 : この ASSERT は CScrollView::OnHScroll(), OnVScroll() の中で以下のよう
に記述されています。
ASSERT(pScrollBar == GetScrollBarCtrl(SB_HORZ)); // may be null
これは CScrollView クラスで View に付属していないスクロールバーがメッセー
ジを発生していないかをチェックしているものです。つまり CFormView 上にスクロ
ールバーがあった場合のことが考慮されていないのです。
これを避ける手段は残念ながらありません。ユーザ自身が OnHScroll() 等をオー
バーライドするか、MFC のソースを書き直すしかありません。ただし、「無視」を選
択してもなんら問題は発生しません。