Visual C++ 2005でCWnd派生のカスタムコントロールを作成し、
それをダイアログエディタ上で配置しています。
カスタムコントロール自体の作成や表示自体はできているのですが、
ダイアログ生成時のフォントが正しくセットされません。
通常のボタンなどは、ダイアログ自体のフォントが
そのままコントロールに反映された状態で表示されますが、
この処理はカスタムコントロールではどのように実現できるのでしょうか。
ON_MESSAGE(WM_SETFONT, OnSetFont)
ためしに上記のようなメッセージハンドラを
カスタムコントロールに用意してみましたが、
ダイアログ生成時にはコールされませんでした。
よろしくお願いいたします。
少し工夫をしてみましょう。
※(可能ならば)
当該のカスタムコントロールをCStaticから派生さた場合、
フォントはどうなりますか。
ちなみに、
ダイアログから GetFont() して、戻り値をそのままカスタムコントロールに SetFont
() では駄目でしょうか?
> 少し工夫をしてみましょう。
カスタムコントロールのPreSubclassWindow()の中で
GetParent()->GetFont()の中身を自クラスのCFont型の実体メンバに複製し、
自分自身の描画時にこのフォントを毎回SelectObject()することで、
親ダイアログと同じフォントで描画できるようになりました。
こういう方法でよいのかは不明ですが…。
> 当該のカスタムコントロールをCStaticから派生さた場合、
> フォントはどうなりますか。
CStaticを派生させた簡単なクラスを作成し、
WM_SETFONTのハンドラを作成してみましたが、やはりコールされませんでした。
しかし、テキストのフォント自体はダイアログのものと同じになっていました。
また、PreSubclassWindow()時の自分自身のGetFont()にはすでに値が入っていました
(カスタムコントロールのときはここではNULLが入っていた)。
ひょっとしたら、ダイアログ自体は子にちゃんとフォントを設定しているが、
この段階ではまだコントロールがサブクラス化されていないので、
WM_SETFONTは親クラスのほうに行っているということなのでしょうか。
そして、CStaitc自身はWM_SETFONTに対応していて、
カスタムコントロールの親クラスであるCWndは対応していないと。
> ひょっとしたら、ダイアログ自体は子にちゃんとフォントを設定しているが、
> この段階ではまだコントロールがサブクラス化されていないので、
> WM_SETFONTは親クラスのほうに行っているということなのでしょうか。
> そして、CStaitc自身はWM_SETFONTに対応していて、
> カスタムコントロールの親クラスであるCWndは対応していないと。
ウィンドウクラスのサブクラス化とMFC(C++)のサブクラスを混同している
ように思われますが、考え方はおおむねそのとおりです。
PreSubclassWindow()で親ウィンドウのフォントを取得すれば良いと思います。
以下のコードを参考にしてください。
class CCustomCtrl : public CWnd
{
private:
HFONT m_hfont;
};
CCustomCtrl::CCustomCtrl()
{
m_hfont = NULL;
}
void CCustomCtrl::PreSubclassWindow()
{
// この時点ではまだサブクラス化されてないから
// SetFont(GetParent()->GetFont()); ではダメ
m_hfont = (HFONT)(GetParent()->GetFont()->GetSafeHandle());
}
LRESULT CCustomCtrl::OnSetFont(WPARAM wParam, LPARAM lParam)
{
m_hfont = (HFONT)wParam;
if (LOWORD(lParam))
Invalidate();
return 0L;
}
LRESULT CCustomCtrl::OnGetFont(WPARAM wParam, LPARAM lParam)
{
return (LRESULT)m_hfont;
}
void CCustomCtrl::OnPaint()
{
CPaintDC dc(this);
CFont* oldfont = dc.SelectObject(GetFont());
// ..文字列の描画処理..
dc.SelectObject(oldfont);
}
> PreSubclassWindow()で親ウィンドウのフォントを取得すれば良いと思います。
> 以下のコードを参考にしてください。
現在自分が試行錯誤に修正していたコードとほとんど同じです。
すごく安心しました。
カスタムコントロールがサブクラス化される前にSetFont()をコールすると、
CWndのメッセージハンドラにWM_SETFONTが渡ってしまうのですね。
そしてそれは、ダイアログが最初に子ウィンドウにWM_SETFONTを送る際にも
同じことが言えるわけですね。
ありがとうございました。
> カスタムコントロールがサブクラス化される前にSetFont()をコールすると、
> CWndのメッセージハンドラにWM_SETFONTが渡ってしまうのですね。
ええぇぇと…違います。先ほど混同していると言ったのはまさにこの部分に関して
なのですが。
この時点ではまだウィンドウプロシージャはCWndクラスには関連付いていません。
この場合、カスタムコントロールのウィンドウクラスを登録したときに指定した
ウィンドウプロシージャ(MFCでは通常::DefWindowProc)が呼ばれます。
サブクラス化が行われて初めてCWndが提供するメッセージマップは有効になり、
メッセージハンドラが呼ばれるようになります。
スタティック コントロールの場合は、スタティックのウィンドウプロシージャが
WM_SETFONT/WM_GETFONTに対応しているので、サブクラス化が行われる以前でも
期待通り動作します。MFCのCStaticクラスから継承しているかどうかはこの場合
関係ありません。
> この時点ではまだウィンドウプロシージャはCWndクラスには関連付いていません。
> この場合、カスタムコントロールのウィンドウクラスを登録したときに指定した
> ウィンドウプロシージャ(MFCでは通常::DefWindowProc)が呼ばれます。
そうでした。自分でネット上のサンプルを元に
wc.lpfnWndProc = ::DefWindowProc;
などと書いていました。
ダイアログ生成時にダイアログから送られてくるWM_SETFONTは
この::DefWindowProcで無視されてしまうということですね。
そして、ボタンやスタティックコントロールなどでは
ここが各々のウィンドウプロシージャで、
この中で初めからWM_SETFONTに対応しているので、
ダイアログ生成時のWM_SETFONTにも連動できるということですね。
また勘違いしていたら指摘していただけると嬉しいです。
詳しい解説ありがとうございます。