c++ - 十进制数字的区域设置感知编辑控件子类化(格式 [sign] [xxx...] [decimal separator] [yy...] )

标签 c++ winapi locale subclassing editcontrol

简介及相关信息:

我有一个编辑控件,它应该只接受有符号 十进制数——类似于-123.456。此外,它应该能够识别区域设置,因为每个国家/地区的小数点分隔符都不相同 - 在美国使用点,而在欧洲则使用逗号等。

我为解决这个问题所做的努力:

到目前为止,我已经使用子类化 来实现它。这是我实现子类化的逻辑,通过伪代码表达:

if ( ( character is not a [ digit,separator, or CTRL/Shift... ] OR
     ( char is separator and we already have one ) )
{
    discard the character;
}

首先,我创建了一个辅助函数来确定 char 数组是否已经有一个小数点分隔符,如下所示:

bool HasDecimalSeparator( wchar_t *test )
{
    // get the decimal separator
    wchar_t szBuffer[5];

    GetLocaleInfo ( LOCALE_USER_DEFAULT, 
                    LOCALE_SDECIMAL, 
                    szBuffer, 
                    sizeof(szBuffer) / sizeof(szBuffer[0] ) );

    bool p = false; // text already has decimal separator?
    size_t i = 0;   // needed for while loop-iterator

    // go through entire array and calculate the value of the p

    while( !( p = ( test[i] == szBuffer[0] ) ) && ( i++ < wcslen(test) ) );

    return p;
}

这是子类ing 过程-我没有考虑减号:

LRESULT CALLBACK Decimalni( HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam, 
    UINT_PTR uIdSubclass, 
    DWORD_PTR dwRefData )
{
    switch (message)
    {
    case WM_CHAR:
        {
            // get decimal separator
            wchar_t szBuffer[5];

            GetLocaleInfo ( LOCALE_USER_DEFAULT, 
                LOCALE_SDECIMAL, 
                szBuffer, 
                sizeof(szBuffer) / sizeof(szBuffer[0] ) );

                wchar_t t[50];  // here we store edit control's current text
                memset( &t, L'\0', sizeof(t) );

                // get edit control's current text
                GetWindowText( hwnd, t, 50 );

                // if ( ( is Not a ( digit,separator, or CTRL/Shift... )
                // || ( char is separator and we already have one ) )
                // discard the character

                if( ( !( isdigit(wParam) || ( wParam == szBuffer[0] ) ) 
                    && ( wParam >= L' ' ) )     // digit/separator/... ?
                    || ( HasDecimalSeparator(t)        // has separator?    
                    && ( wParam == szBuffer[0] ) ) )
                {
                    return 0;
                }
            }
            break;
    }
    return DefSubclassProc( hwnd, message, wParam, lParam);
}

一个重要说明:由于 this question 的答案,我能够在我的应用程序中加载当前用户区域设置。

问题:

是否有更好的方法来实现只接受带符号的十进制数字并且能够识别区域设置的编辑控件?

如果 subclassing 是唯一的方法,我的代码是否可以进一步改进/优化?

感谢您的宝贵时间和帮助。

最好的问候。

附录:

为了进一步帮助您,这里有一个小型演示应用程序,它创建了一个编辑控件,并将其子类设置为仅接受十进制数字 -同样,我还没有实现该部分减号:

#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <locale.h>

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

const wchar_t g_szClassName[] = L"myWindowClass";

bool HasDecimalSeparator( wchar_t *test )
{
    // get the decimal separator
    wchar_t szBuffer[5];

    GetLocaleInfo ( LOCALE_USER_DEFAULT, 
                    LOCALE_SDECIMAL, 
                    szBuffer, 
                    sizeof(szBuffer) / sizeof(szBuffer[0] ) );

    bool p = false; // text already has decimal separator?
    size_t i = 0;   // needed for while loop-iterator

    // go through entire array and calculate the value of the p

    while( !( p = ( test[i] == szBuffer[0] ) ) && ( i++ < wcslen(test) ) );

    return p;
}

LRESULT CALLBACK Decimalni( HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam, 
    UINT_PTR uIdSubclass, 
    DWORD_PTR dwRefData )
{
    switch (message)
    {
    case WM_CHAR:
        {
            // get decimal separator
            wchar_t szBuffer[5];

            GetLocaleInfo ( LOCALE_USER_DEFAULT, 
                LOCALE_SDECIMAL, 
                szBuffer, 
                sizeof(szBuffer) / sizeof(szBuffer[0] ) );

                wchar_t t[50];  // here we store edit control's current text
                memset( &t, L'\0', sizeof(t) );

                // get edit control's current text
                GetWindowText( hwnd, t, 50 );

                // if ( ( is Not a ( digit,separator, or CTRL/Shift... )
                // || ( char is separator and we already have one ) )
                // discard the character

                if( ( !( isdigit(wParam) || ( wParam == szBuffer[0] ) ) 
                    && ( wParam >= L' ' ) )     // digit/separator/... ?
                    || ( HasDecimalSeparator(t)        // has separator?    
                    && ( wParam == szBuffer[0] ) ) )
                {
                    return 0;
                }
            }
            break;
    }
    return DefSubclassProc( hwnd, message, wParam, lParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_CREATE:
        {
            /************* load current locale settings *************/

            // max. len: language, country, code page

            wchar_t lpszLocale[64+64+16+3] = L""; 
            wchar_t lpszVal[128];

            LCID nLCID = ::GetUserDefaultLCID(); // current LCID for user
            if ( ::GetLocaleInfo( nLCID, LOCALE_SENGLANGUAGE, lpszVal, 128 ) )
            {
                wcscat_s( lpszLocale, 147, lpszVal ); // language
                if ( ::GetLocaleInfo( nLCID, LOCALE_SENGCOUNTRY, lpszVal, 128 ) )
                {
                    wcscat_s( lpszLocale, 147, L"_" ); // append country/region
                    wcscat_s( lpszLocale, 147, lpszVal );

                    if ( ::GetLocaleInfo( nLCID, 
                        LOCALE_IDEFAULTANSICODEPAGE, lpszVal, 128 ) )
                    { 
                        // missing code page or page number 0 is no error 
                        // (e.g. with Unicode)

                        int nCPNum = _wtoi(lpszVal);
                        if (nCPNum >= 10)
                        {
                            wcscat_s( lpszLocale, 147, L"." ); // append code page
                            wcscat_s( lpszLocale, 147, lpszVal );
                        }
                    }
                }
            }
            // set locale and LCID
            _wsetlocale( LC_ALL, lpszLocale );
            ::SetThreadLocale(nLCID);

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

            HWND hEdit1;

            hEdit1 = CreateWindowEx(0, L"EDIT", L"", 
                WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_AUTOHSCROLL, 
                50, 100, 100, 20, 
                hwnd, (HMENU)8001, GetModuleHandle(NULL), NULL);

            SetWindowSubclass( hEdit1, Decimalni, 0, 0);

        }
        break;

    case WM_SETTINGCHANGE:
        if( !wParam && !wcscmp( (wchar_t*)lParam, L"intl" ) )
        {
            // max. len: language, country, code page
            wchar_t lpszLocale[64+64+16+3] = L""; 
            wchar_t lpszVal[128];

            LCID nLCID = ::GetUserDefaultLCID(); // current LCID for user
            if ( ::GetLocaleInfo( nLCID, LOCALE_SENGLANGUAGE, lpszVal, 128 ) )
            {
                wcscat_s( lpszLocale, 147, lpszVal ); // language
                if ( ::GetLocaleInfo( nLCID, LOCALE_SENGCOUNTRY, lpszVal, 128 ) )
                {
                    wcscat_s( lpszLocale, 147, L"_" ); // append country/region
                    wcscat_s( lpszLocale, 147, lpszVal );
                    if ( ::GetLocaleInfo( nLCID, 
                        LOCALE_IDEFAULTANSICODEPAGE, lpszVal, 128 ) )
                    { 
                        // missing code page or page number 0 is no error
                        // (e.g. with Unicode)
                        int nCPNum = _wtoi(lpszVal);
                        if (nCPNum >= 10)
                        {
                             wcscat_s( lpszLocale, 147, L"." ); // append code page
                             wcscat_s( lpszLocale, 147, lpszVal );
                        }
                    }
                 }
             }
             // set locale and LCID
             _wsetlocale( LC_ALL, lpszLocale );
             ::SetThreadLocale(nLCID);

             return 0L;
         }
         else
             break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    hwnd = CreateWindowEx(
        0,
        g_szClassName,
        L"theForger's Tutorial Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, L"Window Creation Failed!", L"Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

最佳答案

考虑特定于语言环境的设置

您当然可以自己做任何事情,但是您可以选择使用 VarI4FromStr或类似的 API 为您做脏事。你输入字符串,你得到 LONG 。语言环境感知。

“应该只接受”

您没有指定控件应如何准确执行此操作。如果输入字符串无效怎么办?控件应该仍在接受它,因为例如,字符串尚未生效并且用户仍在键入。如果您正在验证外部处理程序中的输入,例如当按下 OK 按钮时,那么您甚至不需要子类化。如果您想在每次更改时检查输入,则不需要子类化,因为您在父级上有 EN_CHANGE 通知。不过,您可能出于其他原因想要子类化。

接受任何输入然后在文本更改或输入验证时以某种方式指示有效性(例如如果无效则用红色下划线)是用户友好的。

关于c++ - 十进制数字的区域设置感知编辑控件子类化(格式 [sign] [xxx...] [decimal separator] [yy...] ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21811481/

相关文章:

c++ - 提供可变参数模板 : candidate expects 1 argument, 0(推导错误)

c++ - 找不到libstdc++-6.dll

c++ win32防止上下文菜单关闭

delphi - 通过 Delphi 中的 Windows API 使用 COM 端口号获取设备名称

sorting - unix sort -n -t","给出意外结果

es_US 的 Android NumberFormat.getCurrencyInstance 返回不正确的值

c++ - 匿名命名空间中的私有(private)静态成员函数还是自由函数?

c++ - 检查线程是否完成的正确方法?

java - Swing 中的 NL(荷兰语)语言环境似乎不起作用

c++ - 来自子控件的键盘消息