VC2008 XPでCWnd派生のテスト用クラスを作り、PreSubclassWindowの中で
void CTestWnd::PreSubclassWindow()
{
SCROLLINFO info;
ZeroMemory(&info, sizeof(info));
info.fMask = SIF_ALL;
info.nMin = 0;
info.nMax = 100;
info.nPage = 50;
info.nPos = 0;
SetScrollInfo(SB_HORZ, &info);
CWnd::PreSubclassWindow();
}
としてみたところ、とりあえずは問題無くスクロールバーが表示されました。
ところが、nPageを500などにして
スクロールバーが不要な状態にした場合、表示が消えてしまいます。
このときはスクロールバーをグレー化したいのですが、fMaskの部分を
info.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
としてみても、表示されないままとなっています。
上記のSIF_DISABLENOSCROLLを付ければよさそうなことが書いてあるのですが、
なにか指定方法でも間違えていますでしょうか?
「1.ウインドウをサブクラス化する」と
「2.あるクラスの派生クラスを作る」とは何の関係もありません。
本当にウインドウサブクラス化する必要がありますか(質問)。
提示の内容だと、ただのCWndの派生クラスでもできそうですが。
> 本当にウインドウサブクラス化する必要がありますか(質問)。
これはどういうことでしょうか。
CWndの派生クラスを作る際には、Create経由でもダイアログリソース経由でも
呼ばれる関数なので、PreSubclassWindowを多用しているのですが。
このへんがスクロールバーがグレー化しない原因になっているのでしょうか?
PreSubclassWindow()は「1.ウインドウをサブクラス化する」ときに
意味があります。つまり明示的にSubclassWindow()したときに呼ばれます。
動的サブクラス化した場合、当該HWNDのコールバックブロシージャ自体が
差し替えられます。
一方、CWndの派生クラスではコールバックプロシージャは元のまま、
メッセージマップによりメッセージ毎の処理関数がオーバーライド
できるわけです。また、本来は処理する必要のないメッセージなどは
省かれ、軽量化されます。
さて、ウインドウが構築される際、非常に特殊なケースを除けば、
WM_CREATE(OnCreate)で初期化の全ての処理を行って大丈夫です。
仮にMFCがむりやりスクロールバーを消すようなら、WM_SIZE(OnSize)の
タイミングで再表示してやれば十分なはずです。
WS_HSCROLLをセットするか、SetScrollInfoを2回呼んだら出ました。
#info.cbSizeの設定が抜けているようですが
> WS_HSCROLLをセットするか、SetScrollInfoを2回呼んだら出ました。
WS_HSCROLLをセットしたところ、
グレー化したスクロールバーが出てくるようになりました。
WS_HSCROLLを付けなくてもスクロールバーが出てくるので、気づきませんでした。
ありがとうございます。
> #info.cbSizeの設定が抜けているようですが
これはソース貼り付けのうっかりです。
失礼しました。
なお、仲澤@失業者さんに指摘したいただいた件ですが、
> さて、ウインドウが構築される際、非常に特殊なケースを除けば、
> WM_CREATE(OnCreate)で初期化の全ての処理を行って大丈夫です。
今回のようなCWnd派生の自作コントロールは、Createで直接作る方法も、
ダイアログにカスタムコントロールとして配置する方法も、
どちらでも使えるようにするために、よくこのように作っています。
OnCreateで初期化する方法は
カスタムコントロールとして配置した際にコールされないと思うのですが
(逆にPreSubclassWindowはCreateで直接作ってもコールされる)、
このようなケースではどのようにされているのでしょうか?
>OnCreateで初期化する方法は
>カスタムコントロールとして配置した際にコールされないと思うのですが
>(逆にPreSubclassWindowはCreateで直接作ってもコールされる)、
>このようなケースではどのようにされているのでしょうか?
何かの勘違いですね。
DLGの場合はWM_INITDIALOG、一般ウインドウが作成される際には必ず
WM_CREATEメッセージが送付されます。NCエリアのあるウインドウの
場合は、これに先行してWM_NCCREATEが呼ばれます。
これは、当該のウインドウがカスタムコントロールとしてDLGに
配置されている場合でも同じです。DLGの初期化関数は、リソース
に記述してある「クラス名称」を指定してCreateWindow()する
だけです。この動作はEdit等の既存クラスだろうと、カスタム
コントロールだろうと同じです。また、CreateWindow()
CreateWindowEx()以外に一般ウインドウを作成する手段は
ありません。従ってウインドウが作成されたらWM_CREATEが
来るのは自明ですね(笑)。
MFCの一般ウインドウの場合、HWNDに対するスタイルを変更したい場合
や、クラススタイルを変更したい場合はPreCreateWindow()を
オーバーライドするのが普通です。
PreSubclassWindow()を使用しなければならない場面に遭遇することは
まずありません。自分は過去に1回だけありました。
> カスタムコントロールとして配置した際にコールされない
これについてだけど、MFCの問題、というか仕様だと思われる。
サブクラス化って、要するに SetWindowLongPtr(HWND, GWLP_WNDPROC) して既存のウィンドウプロシージャを独
自ウィンドウプロシージャに置き換えてメッセージ処理する事。
で、SetWindowLongPtr(HWND) しようとするならば、HWND が作成済みである必要がある。
つまり、サブクラス化は作成済みWindowに対してのみ可能。故に、WM_CREATE 等のWindow作成完了前に発生する
メッセージはCWnd派生クラスでは拾えないという事になる。
けど、実際はMFCが頑張って(Window作成時のイベントをHookして WM_CREATE 等が発行される直前にサブクラス
化している)のでCWnd派生に届いている。
ただ、ダイアログの場合 CreateDialog() 等のAPIでダイアログと共に子コントロールも一気に作成してれう。
そうなると、子コントロールが作成された際の通知をHookして拾っても、この時点では誰(どのCWnd派生クラス
)とサブクラス化して良いか判らない(実際にコントロールとCWnd派生クラスが結び付けられるのは CDialog::
OnInitDialog() でDDXが実行された時)ので作成通知は無視されてしまう。
> カスタムコントロールとして配置した際にコールされない
結果、WM_CREATE 等が発行された時点ではコントロールとCWnd派生が結びついていないので↑になっちゃう。
ちなみに、CWnd派生::Create() と自分でした場合は、誰と結びつければ良いか判明しているのでWindow作成イ
ベントをHook時にサブクラス化してくれる。
昔調べた時にはこんな動作してたと思う。けど、最新のMFCでもコレが当て嵌まるのかは未確認
ツイート | ![]() |