3

私の現在のプロジェクトでは、ここ数日、編集ボックスのサブクラス化に苦労しています。ここまでで、編集ボックスのサブクラス化に成功し、数字、コンマ、マイナス記号、およびキーボード コマンドのみを受け入れるように入力を検証しました。

しかし、かなり長い間、入力検証の改良に行き詰まっています。編集ボックスを次のように動作させたい:

  • 最初の位置でのみマイナス記号を受け入れる
  • 先行ゼロを 1 つだけ受け入れる
  • コンマを 1 つだけ受け入れます
  • 先行ゼロの後にコンマを強制する
  • 「戻る」、「削除」、すべて選択を介して単一の文字またはテキストの選択を削除し、その上に何かを貼り付ける場合のこれらのケースを管理します

現在の形式での私のコードは次のようになり、上記で指定した高度な検証要件はほとんど提供されません。

inline LRESULT CALLBACK decEditBoxProc(HWND hWnd,
                                       UINT msg, 
                                       WPARAM wParam, 
                                       LPARAM lParam,
                                       UINT_PTR uIdSubclass,
                                       DWORD_PTR dwRefData)
{
    if(msg == WM_CHAR)
    {
        decEditBoxData* data = reinterpret_cast<decEditBoxData*>(ULongToPtr(dwRefData));

        bool isDigit          = (wParam >= '0' && wParam <= '9');
        bool isZero           = ((wParam == '0') && !data->blockZero);
        bool isSign           = (wParam == '-');
        bool isComma          = ((wParam == '.' || wParam == ',') && !data->blockComma);
        bool isValidCommand   = (wParam == VK_RETURN  
                                || wParam == VK_DELETE 
                                || wParam == VK_BACK);


        // Restrict comma to one.
        if(isComma && data->nCommas > 0)
            return FALSE;
        else if(isComma && data->nCommas == 0)
            data->nCommas++;

        // Restrict trailing zeroes to one.
        if(isZero && data->nTrailingZeroes > 0)
            return FALSE;
        else if(isZero && data->nTrailingZeroes == 0)
            data->nTrailingZeroes++;

        // Filter everything but digits, commas and valid commands.
        if(!isDigit && !isValidCommand && !isComma)
            return FALSE;
    }
    return DefSubclassProc(hWnd, msg, wParam, lParam);
}

この問題をアルゴリズム的に解決する方法についてのアイデアは非常に高く評価されています。

アップデート

David Heffernan と IInspectable の提案のおかげで、編集コントロールをサブクラス化せずに (ほぼ) 問題を解決することができました。

ダイアログ プロシージャ (編集コントロールを含む) で:

switch(msg)
{
case WM_COMMAND:
   switch(LOWORD(wParam))
   {
      case IDC_IN_REAL:
         if(HIWORD(wParam)==EN_CHANGE) onEditChange(hDlg, IDC_IN_REAL);
         break;

      case IDC_IN_IMAG:
         if(HIWORD(wParam)==EN_CHANGE) onEditChange(hDlg, IDC_IN_IMAG);
         break;
    }
    break;
}

onEditChange の場合:

void onEditChange(HWND hDlg, int ctrlID)
{
    HWND hEdit    = GetDlgItem(hDlg, ctrlID);
    size_t len    = GetWindowTextLength(hEdit)+1;
    wchar_t* cstr = new wchar_t[len];
    GetWindowText(hEdit, cstr, len);

    std::wstring wstr(cstr);

    if(!(tools::isFloat(wstr)))
    {
        EDITBALLOONTIP bln;
        bln.cbStruct = sizeof(EDITBALLOONTIP);
        bln.pszTitle = L"Error";
        bln.pszText  = L"Not a valid floating point character.\nUse '.' instead of ','";
        bln.ttiIcon  = TTI_ERROR;
        Edit_ShowBalloonTip(hEdit, &bln);
    }
    delete [] cstr;
}

そして isFloat():

bool tools::isFloat(std::wstring str)
{
    std::wistringstream iss(str);
    float f;
    wchar_t wc;
    if(!(iss >> f) || iss.get(wc))
        return false;
    return true;
}

ユーザーに視覚的なフィードバックを追加する予定ですが、現時点では重要ではありません。

しかし、質問はまだ答えられていません。私の意図は、可能な小数点として「、」を許可することでした。

4

1 に答える 1

-1

ステートマシンが必要です。最初に州を宣言します。

enum InputState
{
  NoCharacters
  MinusSign
  LeadingZero
  PreDecimalPoint
  PostDecimalPoint
}
InputState mState = NoCharacters

ユーザーが文字を入力するたびに、スイッチ ブロックを使用して mState の値に応じて異なる検証関数を呼び出します。

bool ValidCharacter(char input)
{
  switch (mState)
  {
        case NoCharacters:
          return NoCharacters(input);
        case MinusSign:
          return MinusSign(input);
        /// etc
  }
}

たとえば、mState == NoCharacters のときに呼び出す関数は、任意の数字、小数点、またはマイナス記号を受け入れます。次に、文字がマイナス記号の場合は mState を MinusSign に変更し、ゼロの場合は LeadingZero に変更します。

于 2014-06-20T20:35:56.897 に答える