11

浮動小数点数のみを受け入れるようにエディット コントロールをサブクラス化しました。ユーザーが無効な入力をしたときにツールチップをポップしたいと思います。私がターゲットとする動作は、次の 1 つの編集コントロールのようなものですES_NUMBER

ここに画像の説明を入力

これまでのところ、追跡ツールチップを実装し、ユーザーが無効な入力を行ったときに表示することができました。

ただし、ツールチップの場所が間違っています。これを使用ScreenToClientClientToScreenて修正しようとしましたが、失敗しました。

SCCEを作成する手順は次のとおりです。

1) Visual Studio でデフォルトの Win32 プロジェクトを作成します。

stdafx.h2) 次のインクルードを のすぐ下に追加します#include <windows.h>

#include <windowsx.h>
#include <commctrl.h>

#pragma comment( lib, "comctl32.lib")

#pragma comment(linker, \
    "\"/manifestdependency:type='Win32' "\
    "name='Microsoft.Windows.Common-Controls' "\
    "version='6.0.0.0' "\
    "processorArchitecture='*' "\
    "publicKeyToken='6595b64144ccf1df' "\
    "language='*'\"")

3) 次のグローバル変数を追加します。

HWND g_hwndTT;
TOOLINFO g_ti;

4) エディット コントロールの簡単なサブクラス プロシージャを次に示します (テスト目的のみ)。

LRESULT CALLBACK EditSubProc ( HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam, 
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
    switch (message)
    {
    case WM_CHAR:
        {
            POINT pt;
            if( ! isdigit( wParam ) )  // if not a number pop a tooltip!
            {
                if (GetCaretPos(&pt))  // here comes the problem
                {
                    // coordinates are not good, so tooltip is misplaced
                    ClientToScreen( hwnd, &pt );


                    /************************** EDIT #1 ****************************/
                    /******* If I delete this line x-coordinate is OK *************/
                    /*** y-coordinate should be little lower, but it is still OK **/
                    /**************************************************************/

                    ScreenToClient( GetParent(hwnd), &pt );

                    /************************* Edit #2 ****************************/

                    // this adjusts the y-coordinate, see the second edit
                    RECT rcClientRect;
                    Edit_GetRect( hwnd, &rcClientRect );
                    pt.y = rcClientRect.bottom;

                    /**************************************************************/

                    SendMessage(g_hwndTT, TTM_TRACKACTIVATE, 
                        TRUE, (LPARAM)&g_ti);
                    SendMessage(g_hwndTT, TTM_TRACKPOSITION, 
                        0, MAKELPARAM(pt.x, pt.y));
                }
                return FALSE;
            }
            else
            {
                SendMessage(g_hwndTT, TTM_TRACKACTIVATE, 
                    FALSE, (LPARAM)&g_ti);
                return ::DefSubclassProc( hwnd, message, wParam, lParam );
            }
        }
        break;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass( hwnd, EditSubProc, 0 );
        return DefSubclassProc( hwnd, message, wParam, lParam);
        break;
    }
    return DefSubclassProc( hwnd, message, wParam, lParam);
} 

WM_CREATE5) 次のハンドラーを追加します。

case WM_CREATE:
    {
        HWND hEdit = CreateWindowEx( 0, L"EDIT", L"edit", WS_CHILD | WS_VISIBLE |
            WS_BORDER | ES_CENTER, 150, 150, 100, 30, hWnd, (HMENU)1000, hInst, 0 );

        // try with tooltip
        g_hwndTT = CreateWindow(TOOLTIPS_CLASS, NULL,
            WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
            0, 0, 0, 0, hWnd, NULL, hInst, NULL);

        if( !g_hwndTT )
            MessageBeep(0);  // just to signal error somehow

        g_ti.cbSize = sizeof(TOOLINFO);
        g_ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
        g_ti.hwnd = hWnd;
        g_ti.hinst = hInst;
        g_ti.lpszText = TEXT("Hi there");

        if( ! SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&g_ti) )
            MessageBeep(0);  // just to have some error signal

        // subclass edit control
        SetWindowSubclass( hEdit, EditSubProc, 0, 0 );
    }
    return 0L;  

6) (ステートメントのMyRegisterClass前に)で共通コントロールを初期化します。return

// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES | 
    ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_STANDARD_CLASSES ;

if( !InitCommonControlsEx(&iccex) ) 
    MessageBeep(0);   // signal error 

SSCCEについては以上です。

私の質問は次のとおりです。

  1. メイン ウィンドウにツールチップを正しく配置するにはどうすればよいですか? キャレット座標を操作するにはどうすればよいですか?

  2. ツールチップ ハンドルとツール情報構造をグローバルにしない方法はありますか?

お時間をいただきありがとうございます。

よろしくお願いします。

編集#1:

ScreenToClientサブクラス プロシージャの呼び出しを削除することで、かなりの改善を達成することができました。x座標は良好ですが、y座標はわずかに低くなる可能性があります。どうにかしてグローバル変数を削除したいのですが...

編集#2:

メッセージを使用しEM_GETRECT、y 座標を書式設定用の四角形の下部に設定することで、 y 座標を調整できました。

RECT rcClientRect;
Edit_GetRect( hwnd, &rcClientRect );
pt.y = rcClient.bottom;

これで、最終結果ははるかに良くなりました。あとはグローバル変数を削除するだけです...

編集#3:

割れちゃったみたい!解決策はメッセージにEM_SHOWBALLOONTIPありEM_HIDEBALLOONTIPます!ツールチップはキャレットの位置に配置され、バルーンの形状は写真と同じで、適切に自動終了します。そして最高のことは、グローバル変数が必要ないことです!

これが私のサブクラス プロシージャ スニペットです。

case WM_CHAR:
{
    // whatever... This condition is for testing purpose only
    if( ! IsCharAlpha( wParam ) && IsCharAlphaNumeric( wParam ) )
    {
        SendMessage(hwnd, EM_HIDEBALLOONTIP, 0, 0);
        return ::DefSubclassProc( hwnd, message, wParam, lParam );
    }
    else
    {
        EDITBALLOONTIP ebt;

        ebt.cbStruct = sizeof( EDITBALLOONTIP );
        ebt.pszText = L" Tooltip text! ";
        ebt.pszTitle = L" Tooltip title!!! ";
        ebt.ttiIcon = TTI_ERROR_LARGE;    // tooltip icon

        SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt);

        return FALSE;
    }
 }
 break;
4

3 に答える 3

7

さらにテストした後、他の人が明確に見つけられるように、これを回答とすることにしました。

解決策は、メッセージを使用EM_SHOWBALLOONTIPするEM_HIDEBALLOONTIPことです。ツールチップを作成してエディット コントロールに関連付ける必要はありません。したがって、編集コントロールをサブクラス化するだけで、すべてが機能します。

LRESULT CALLBACK EditSubProc ( HWND hwnd, UINT message, 
WPARAM wParam, LPARAM lParam, 
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
    switch (message)
    {
    case WM_CHAR:
        {
            if( ! isdigit( wParam ) )  // if not a number pop a tooltip!
            {
                EDITBALLOONTIP ebt;

                ebt.cbStruct = sizeof( EDITBALLOONTIP );
                ebt.pszText = L" Tooltip text! ";
                ebt.pszTitle = L" Tooltip title!!! ";
                ebt.ttiIcon = TTI_ERROR_LARGE;    // tooltip icon

                SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt);
                return FALSE;
            }
            else
            {
                SendMessage(hwnd, EM_HIDEBALLOONTIP, 0, 0);
                return ::DefSubclassProc( hwnd, message, wParam, lParam );
            }
        }
        break;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass( hwnd, EditSubProc, 0 );
        return DefSubclassProc( hwnd, message, wParam, lParam);
        break;
    }
    return DefSubclassProc( hwnd, message, wParam, lParam);
} 

それでおしまい!

うまくいけば、この答えも誰かを助けるでしょう!

于 2014-05-29T11:44:00.803 に答える
4

質問が回答されたことを明確にするために、コメントを回答として提供しています(以前に行うべきでした)。

TTM_TRACKPOSITION の MSDN ドキュメントには、x/y 値は「画面座標」であると記載されています。

完全にはわかりませんが、y 座標はおそらくキャレットの上部に対応します。ツールチップを編集ボックスの中央に配置する場合は、編集ボックスの高さの半分を追加できます。

グローバル変数を編集します。すべてのグローバル変数を構造体にバンドルし、構造体にメモリを割り当て、 SetWindowLongPtr API 呼び出しを使用して編集ウィンドウを使用し て構造体のポインターを渡すことができます。ウィンドウ プロシージャは、 GetWindowLongPtrGWLP_USERDATAを使用して値を取得できます。...

于 2014-05-27T16:53:28.930 に答える