タイトル通りに WS_THICKFRAME スタイルなしで作成されたウインドウの
右下隅のみでサイズを変更したいです。作成するウインドウのスタイルは
WS_POPUPWINDOW、WS_EX_LAYERED、WS_EX_TOPMOST です。
http://wisdom.sakura.ne.jp/system/winapi/win32/win43.html
http://wisdom.sakura.ne.jp/system/winapi/win32/win42.html
http://katsura-kotonoha.sakura.ne.jp/prog/win/tip00025.shtml
上記のリンク先より WM_NCHITTEST メッセージを処理すれば
サイズ変更可能と思いましたが上手くいきませんでした。
上手くいかないとは右下隅に出るカーソルもでず、サイズ変更も出来ない。
前に付箋ソフトを作ったときにダイアログでほぼ同じ処理を行った経験があり
ダイアログの場合は
case WM_NCHITTEST:
if ( 右下隅の判定 ){
SetWindowLong( hDlg, DWL_MSGRESULT, HTBOTTOMRIGHT );
return TRUE;
}
としたら出来ました。
今回はダイアログではなくてトップレベルのウインドウです。
ウインドウなので直接 HTBOTTOMRIGHT を返せば良いと思ったのですができない。
なぜですか?
ちなみに WS_THICKFRAME スタイルを付ければ出来ます。
でもウインドウの枠が太くなってしまいます。
以下にテスト・プログラムのプロシージャを載せます。
LRESULT CALLBACK mainWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
lParam )
{
switch ( uMsg ){
case WM_CREATE:
SetLayeredWindowAttributes( hWnd, 0, 128, LWA_ALPHA );
return 0;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
case WM_NCHITTEST:
{
// スクリーン座標
POINT po;
po.x = LOWORD(lParam);
po.y = HIWORD(lParam);
// ウインドウのサイズ
RECT rc;
GetWindowRect( hWnd, &rc );
// 右下隅の判定
rc.left = (rc.right - 10);
rc.top = (rc.bottom - 10);
return PtInRect(&rc,po) ? HTBOTTOMRIGHT : HTCAPTION;
}
default:return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
}
Windows XP Home SP3
VC2003(SDK)
よろしくお願いします。
将来OSのバージョン変わるとNGかもしれん。
一度サイズ変更可能でウィンドウ作ってないと効かないって話を見かけたことあるぞ。
もう解決していると思いますが、念のため・・・。
もし、縁なしの透明なウインドウを作成しているのなら、
透明なウインドウへのマウスメッセージは、その下の
ウインドウに透過するので、透明ウインドウがマウス
メッセージを受け取ることはありません。
また、縁なしウインドウはいくつかのWM_NC・・系メッセージ
を受け取りません。NonClient自体が無いのだから当然ですね。
wclrp ( 'o')さんへ
>一度サイズ変更可能でウィンドウ作ってないと効かないって話を見かけたことあるぞ。
変更可能ウインドウで作成して動的に変更してもサイズ変更できませんでした。
どうやら WS_THICKFRAME 属性が付いていないとサイズ変更できないようです。
>将来OSのバージョン変わるとNGかもしれん。
ダイアログ・ウインドウは問題がないようです。
仲澤@失業者へ
>もう解決していると思いますが、念のため・・・。
まだです。
>もし、縁なしの透明なウインドウを作成しているのなら、
>透明なウインドウへのマウスメッセージは、その下の
>ウインドウに透過するので、透明ウインドウがマウス
>メッセージを受け取ることはありません。
透明ではなくて不透明ウインドウです。
それでも最初はこれが原因かと思って普通のウインドウで試しました。
その結果、普通のウインドウでもサイズ変更できなかったので関係ないと思って質問。
>また、縁なしウインドウはいくつかのWM_NC・・系メッセージ
>を受け取りません。NonClient自体が無いのだから当然ですね。
WM_NCHITTEST は飛んできます。
何か良いアイディアありませんか?
>透明ではなくて不透明ウインドウです。
もう一度、create等の関数を調べてみたらどうでしょうか?
> 何か良いアイディアありませんか?
ユーザーメッセージもだめですか?
必要なメッセージのうち、受け取れるメッセージと受け取れないメッセージを
区分けしてみたらいいのではないですか?
受け取れるのメッセージのうち、
マウスのメッセージ等のウインドウ共通のメッセージは
別のウインドウで受け取って問題になっているウインドウに
ユーザーメッセージを送ればいいのでは?
WN_のメッセージを受け取らない問題を解決するのも大切ですが、だめなら
どうするかも考えるのも大切だと思います。
>もう一度、create等の関数を調べてみたらどうでしょうか?
全く分からずにいます。
ソースを載せておきます。
#define WINDOW_EXSTYLE (WS_EX_LAYERED | WS_EX_TOPMOST)
#define WINDOW_STYLE (WS_POPUPWINDOW | WS_THICKFRAME)
// CreateWindowEx部分
hWnd = CreateWindowEx(
WINDOW_EXSTYLE, // 拡張ウインドウスタイル
lpClassName, // 登録されているクラス名
lpTitleName, // ウインドウのタイトル名
WINDOW_STYLE, // ウインドウのスタイル種
CW_USEDEFAULT, // ウインドウの初期X座標
CW_USEDEFAULT, // ウインドウの初期Y座標
WINDOW_WIDTH, // ウインドウの初期の横幅
WINDOW_HEIGHT, // ウインドウの初期の高さ
NULL, // 親ウインドウのハンドル
NULL, // メニューのハンドル
hInstance, // インスタンスのハンドル
NULL ); // ウインドウ作成のパラメータ
// RegisterClassEx部分
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcex.lpfnWndProc = mainWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = myLoadImage( NULL, IDI_APPLICATION, ICON );
wcex.hIconSm = myLoadImage( NULL, IDI_APPLICATION, ICON );
wcex.hCursor = myLoadImage( NULL, IDC_ARROW, CURSOR );
wcex.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = lpClassName;
RegisterClassEx( &wcex );
>ユーザーメッセージもだめですか?
これは WM_APP のことですか?
>WN_のメッセージを受け取らない問題を解決するのも大切ですが、だめなら
>どうするかも考えるのも大切だと思います。
最終的に自分でサイズ変更の部分を作成すれば可能だとは思っています。
つまり
1...WM_LBUTTONDOWNでドラッグ開始
2...WM_MOUSEMOVEでサイズ変更
3...WM_LBUTTONUPでドラッグ完了
このほかに WM_ACTIVATE でマウスキャプチャをキャンセルするなど。
この方法ならできると思っています。
まだ試していませんけどね。
今は WS_THICKFRAME スタイルをつけたウインドウを作成して
WM_NCHITTEST、WM_NCPAINT、WM_ERASEBKGND を処理して自分で細枠を描いています。
この方法でサイズ変更は出来ていますが…。
こんな解決方法で良いのでしょうかね。
(最終的に動けばいいんですけど)
WS_EX_LAYEREDを選択しているということは、「レイヤード ウィンドウ」
にしたいということですね。
いろいろな設定が必要になるみたいです。
僕もここまではやったことないです。
参考にどうぞ
http://www.microsoft.com/japan/msdn/windows/windows2000/layerwin.aspx
ダイアログの場合は出来たとのことですが、当方の結果はカーソルが変化するだけでし
た。
WS_POPUPWINDOWでもシステムメニューからSC_SIZEを削除すればカーソルは変化しまし
た。
という訳で、カーソル作成時はシステムメニューを見ているようです。
本格的対策は、
(キーボード用に)SC_SIZEは残し、WM_INITMENUPOPUPでMF_ENABLEDにする
WM_NCHITTEST,WM_SETCURSORを処理
WM_NCLBUTTONDOWNでWM_SYSCOMMAND(SC_SIZE + 方向)を呼ぶ
簡易策、BOTTOMRIGHTだけで良ければ、SBS_SIZEBOXを貼り付けWM_SIZEで右下に移動する
いろいろ実験してみました。その中でわかった事は
1. WS_EX_LAYEREDスタイルのウインドウはCreateWindowEx()
だけでは表示されず、SetLayeredWindowAttributes()を
コールしたときに初めて表示される。
従って、このスタイルのウインドウをCreateWindowEx()だけで
表示できていると思うのなら、それはペテンにかかっている。
2. WS_EX_LAYEREDスタイルのウインドウの場合SetLayeredWindowAttributes()
で、LWA_COLORKEYを指定した場合はクライアント領域内では
WM_NCHITTESTはそもそも来ない。しかし、LWA_ALPHAを指定した場合なら来る。
この場合で、WS_THICKFRAMEスタイルがあれば、HTBOTTOMRIGHT等を戻すと
ウインドウサイズを変更できる。このスタイルがない場合はHTBOTTOMRIGHT
等を戻どしても何も起こらない。
つまり、透明ウインドウのクライアントにはWM_NCHITTESTは来ない。
これは、当初から自分が主張していた通りの結果である。
3. 当たり前だがWM_NCHITTESTのlparamはGET_X_LPARAM()マクロで取得しなければ
ならない。この座標はスクリーン座標であるため、クライアント座標に
変換してからでないと正しく評価できない。
自分の結論
WS_EX_LAYEREDである場合、WS_THICKFRAMEであり、LWA_ALPHAでないと
WM_NCHITTESTで、HTBOTTOMRIGHT等を戻しても効果が無い。
WS_EX_LAYEREDで無い場合WS_THICKFRAMEであることだけが、WM_NCHITTESTで、
HTBOTTOMRIGHT等を戻した効果が出る条件である。
というこで、ぴょぴょさんの言っていることと、微妙に異なる結論でした。
条件は VS2008 Std Ver9.0.30729.1SP on XP(SP3)です。
ITOさん。
参考URLありがとうございます。
でも既に読んだページでした。
ロマさん。
>簡易策、BOTTOMRIGHTだけで良ければ、SBS_SIZEBOXを貼り付けWM_SIZEで右下に移動す
る
これはスクロールバーですか?
一通り解決していますが、この方法の詳しい実装方法を教えてほしいです。
もし、読んでくれたらお願いします。
仲澤@失業者さんへ。
こちらもいろいろと実験しました。
>1. WS_EX_LAYEREDスタイルのウインドウはCreateWindowEx()
同じ結果。
でもペテンにはかかってはいませんけど。
WM_CREATE で SetLayeredWindowAttributes() を呼んでいます。
>2. WS_EX_LAYEREDスタイルのウインドウの場合SetLayeredWindowAttributes()
こちらも同じ。
LWA_COLORKEY だとマウス・イベントが取得できませんでした。
今回は LWA_ALPHA で α値を 255 で利用するので WM_NCHITTEST が届きます。
>3. 当たり前だがWM_NCHITTESTのlparamはGET_X_LPARAM()マクロで取得しなければ
この辺はちゃんと正しく変換して確認は取っています。
確認方法は WM_NCHITTEST 内で GetWindow() を利用してマウス座標や右下隅の
判定矩形を Rectangle() で描画してマウスがこの上に来たら 1 となるように
クライアント領域に描いて確認しました。
>自分の結論
同じ結果です。
僕の結論は WS_THICKFRAME スタイルがないと WM_NCHITTEST で BOTTOMRIGHT だけ
返してもサイズ変更されない。です。よって自分でマウス・キャプチャを使って
サイズ変更する処理を実装しました。
これで解決とさせてもらいます。