スクロールバーのグレー化

解決


QR  2010-03-29 10:39:54  No: 71513  IP: [192.*.*.*]

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を付ければよさそうなことが書いてあるのですが、
なにか指定方法でも間違えていますでしょうか?

編集 削除
仲澤@失業者  2010-03-29 11:09:58  No: 71514  IP: [192.*.*.*]

「1.ウインドウをサブクラス化する」と
「2.あるクラスの派生クラスを作る」とは何の関係もありません。

本当にウインドウサブクラス化する必要がありますか(質問)。
提示の内容だと、ただのCWndの派生クラスでもできそうですが。

編集 削除
QR  2010-03-29 11:36:18  No: 71515  IP: [192.*.*.*]

> 本当にウインドウサブクラス化する必要がありますか(質問)。

これはどういうことでしょうか。
CWndの派生クラスを作る際には、Create経由でもダイアログリソース経由でも
呼ばれる関数なので、PreSubclassWindowを多用しているのですが。

このへんがスクロールバーがグレー化しない原因になっているのでしょうか?

編集 削除
仲澤@失業者  2010-03-29 13:13:58  No: 71516  IP: [192.*.*.*]

PreSubclassWindow()は「1.ウインドウをサブクラス化する」ときに
意味があります。つまり明示的にSubclassWindow()したときに呼ばれます。
動的サブクラス化した場合、当該HWNDのコールバックブロシージャ自体が
差し替えられます。
一方、CWndの派生クラスではコールバックプロシージャは元のまま、
メッセージマップによりメッセージ毎の処理関数がオーバーライド
できるわけです。また、本来は処理する必要のないメッセージなどは
省かれ、軽量化されます。

さて、ウインドウが構築される際、非常に特殊なケースを除けば、
WM_CREATE(OnCreate)で初期化の全ての処理を行って大丈夫です。
仮にMFCがむりやりスクロールバーを消すようなら、WM_SIZE(OnSize)の
タイミングで再表示してやれば十分なはずです。

編集 削除
ロマ  2010-03-29 15:07:01  No: 71517  IP: [192.*.*.*]

WS_HSCROLLをセットするか、SetScrollInfoを2回呼んだら出ました。
#info.cbSizeの設定が抜けているようですが

編集 削除
QR  2010-03-29 16:25:29  No: 71518  IP: [192.*.*.*]

> WS_HSCROLLをセットするか、SetScrollInfoを2回呼んだら出ました。

WS_HSCROLLをセットしたところ、
グレー化したスクロールバーが出てくるようになりました。

WS_HSCROLLを付けなくてもスクロールバーが出てくるので、気づきませんでした。
ありがとうございます。

> #info.cbSizeの設定が抜けているようですが

これはソース貼り付けのうっかりです。
失礼しました。

なお、仲澤@失業者さんに指摘したいただいた件ですが、

> さて、ウインドウが構築される際、非常に特殊なケースを除けば、
> WM_CREATE(OnCreate)で初期化の全ての処理を行って大丈夫です。

今回のようなCWnd派生の自作コントロールは、Createで直接作る方法も、
ダイアログにカスタムコントロールとして配置する方法も、
どちらでも使えるようにするために、よくこのように作っています。

OnCreateで初期化する方法は
カスタムコントロールとして配置した際にコールされないと思うのですが
(逆にPreSubclassWindowはCreateで直接作ってもコールされる)、
このようなケースではどのようにされているのでしょうか?

編集 削除
仲澤@失業者  2010-03-29 17:02:39  No: 71519  IP: [192.*.*.*]

>OnCreateで初期化する方法は
>カスタムコントロールとして配置した際にコールされないと思うのですが
>(逆にPreSubclassWindowはCreateで直接作ってもコールされる)、
>このようなケースではどのようにされているのでしょうか?

何かの勘違いですね。
DLGの場合はWM_INITDIALOG、一般ウインドウが作成される際には必ず
WM_CREATEメッセージが送付されます。NCエリアのあるウインドウの
場合は、これに先行してWM_NCCREATEが呼ばれます。

これは、当該のウインドウがカスタムコントロールとしてDLGに
配置されている場合でも同じです。DLGの初期化関数は、リソース
に記述してある「クラス名称」を指定してCreateWindow()する
だけです。この動作はEdit等の既存クラスだろうと、カスタム
コントロールだろうと同じです。また、CreateWindow()
CreateWindowEx()以外に一般ウインドウを作成する手段は
ありません。従ってウインドウが作成されたらWM_CREATEが
来るのは自明ですね(笑)。

MFCの一般ウインドウの場合、HWNDに対するスタイルを変更したい場合
や、クラススタイルを変更したい場合はPreCreateWindow()を
オーバーライドするのが普通です。
PreSubclassWindow()を使用しなければならない場面に遭遇することは
まずありません。自分は過去に1回だけありました。

編集 削除
gak  2010-03-30 17:28:57  No: 71520  IP: [192.*.*.*]

> カスタムコントロールとして配置した際にコールされない
これについてだけど、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でもコレが当て嵌まるのかは未確認

編集 削除