Числовой TEdit с использованием WinApi.

Цифровой TEditНеобходимо, чтобы в TEdit пользователь мог ввести только цифры? Я часто встречаю различные решения данной задачи, которые в основном сводятся к обработке события OnKeyPress. Сегодня рассмотрим кардинально другой подход — изменение стиля TEdit с помощью WinApi.

Итак, как же чаще всего фильтруют? Этот способ я даже встречал в каких-то официальных исходниках-примерах от borland.
Суть метода состоит в том, чтобы обрабатывать событие OnKeyPress, проверять параметр Key, передаваемый обработчику. Это char, да. И если этот char — не разрешенный символ для ввода и не #8 (backspace, про него не стоит забывать), то установить тот же Key в #0. Таким образом, некавайные символы не получится ввести.
Вот из из множества возможных вариантов реализации:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if not(Key in ['0'..'9', #8]) then Key:=#0;
end;

Здесь нам помогает оператор in, который проверяет наличие символа в указанном нами массиве (от нуля до девятки и backspace). С другим набором символов это, может быть, и наиболее правильный способ, но фильтрация ввода чисел в самом TEdit уже предусмотрена, нам надо только изменить его стиль, о чём далее.

Константы, начинающиеся на WS_ (от window style) являют собой константы стилей для объектов. Обязательно указываются при создании окон через различные winapi-функции. Наш TEdit WS_CHILD и WS_VISIBLE — дочерний и видимый. Также есть специальные константы только для Edit’ов, они начинаются на ES_. ES_NUMBER — нужный нам стиль. Подробнее про стили напишу когда-нибудь на блоге :)
Для изменения стилей в 32-хбитных windows была функция SetWindowLong, для совместимости с 64-хбитными была введена api функция SetWindowLongPtr.

  SetWindowLongPtr(Edit1.Handle, GWL_STYLE, WS_CHILD or WS_VISIBLE or ES_NUMBER);

Если приведённый ниже код написать в FormCreate, то пользователь ничего не сможет ввести, кроме цифр, что и требовалось.
Да, функция SetWindowLongPtr, в отличие от SetWindowLong, отсутствует в delphi седьмой версии и ниже, если используешь, то надо импортировать из user32.dll.

Постовой:
dmitko.ru: Блог о python, django, javascript и многом другом.

(c) Всегда ваш crystalbit

21 ответ к «Числовой TEdit с использованием WinApi.»

  1. Пришлось подобным заниматься, когда писал программку для заполнения персональных данных. Чего только не пытались ввести! Сначала данные хранились в простом xml, так отдельные извращенцы лазали туда и правили руками то, что не получилось из программы — пришлось шифровать.
    Что касается поля только с цифрами, то, если я не ошибаюсь, нужно еще сделать обработчик вставки в поле чтобы через правый клик мыши — вставить не обошли защиту

    1. Хм, думал что если установить стиль через WinApi, то вставка будет фильтроваться. Сейчас проверил, действительно, можно обойти.
      Спасибо за дополнение к посту :)

  2. Установив стиль окна ES_NUMBER мы запрещаем вводить нецифровые символы с клавиатуры, однако их можно будет вставить из буфера обмена. Чтобы запретить и это нужно переопределить процедуру окна на свою:

    SetWindowLong(Edit1.Handle, GWL_USERDATA, SetWindowLong(Edit1.Handle,
      GWL_WNDPROC, LPARAM(@MyWinProc)));
    
    function MyWinProc(wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
    begin
      if (uMsg = WM_PASTE) and Clipboard.HasFormat(CF_TEXT) then begin
        if (not CheckForNum(Clipboard.AsText)) then uMsg := 0;
      end;
    
      Result := CallWindowProc(Pointer(GetWindowLong(wnd, GWL_USERDATA)),
        wnd, uMsg, wParam, lParam);
    end;
      1. Когда несколько вариантов, есть возможность поспорить :)
        В таком случае всё равно вставку из буфера фильтровать нужно.
        По мне так проще один раз поменять стиль Edit’а, чем фильтровать каждый символ через обработчик события.

  3. А всегда пользовался только: onkeypress. :) И ничего — работает! Хотя как альтернатива, что бы запутать программиста — можно попробовать :)

    1. Все варианты, которые раскрыты там, или слишком мудрёные (на onChange имхо не совсем адекватно — надо парсить всю строку), или раскрыты в моём посте :) Спасибо за ссылку

  4. Upd: В моем примере выше у функции MyWinProc нужно обязательно указать директиву stdcall иначе может быть AccessViolation

  5. спасибо, кокраз в вузе стали требовать что бы писали проги вообще без возможности вылета ошибки, а так можно исключить основную часть приводящую к из возникновению.

  6. Да, способ полезный, однако пригодится он, разве что, в достаточно крупных проектах, коими будут пользоваться множество пользователей, которые далеко не всегда адекватны в том, что вводят ;)
    А для простеньких программулек «OnKeyPress» более чем подойдёт.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *