Многие разработчики Delphi сталкиваются с проблемой, когда стандартное свойство NumbersOnly компонента TEdit не работает должным образом, особенно при использовании определенных виджетсетов, таких как Cocoa, GTK2 или GTK3. Как видно из обсуждения на форуме, пользователь atlatl столкнулся именно с этой проблемой, когда мог вводить любые символы, несмотря на установленное свойство NumbersOnly.
Решения проблемы
1. Использование обработчика OnKeyPress
Простейшее решение - создать обработчик события OnKeyPress для вашего TEdit:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: char);
begin
// Разрешаем цифры (коды 48-57), Backspace (код 8) и Delete (код 46)
if not ((Ord(Key) in [48..57]) or (Ord(Key) = 8) or (Ord(Key) = 46)) then
Key := #0;
end;
Этот код позволяет вводить только цифры и использовать клавиши Backspace и Delete. Однако у этого метода есть недостаток - он не защищает от вставки текста через буфер обмена.
2. Улучшенная версия с поддержкой отрицательных чисел и десятичных разделителей
Если вам нужно разрешить ввод отрицательных чисел и десятичных разделителей:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: char);
begin
if not (Ord(Key) in [48..57]) and // цифры
not (Ord(Key) = 8) and // Backspace
not (Ord(Key) = 46) and // Delete
not ((Ord(Key) = 45) and (TEdit(Sender).SelStart = 0)) and // минус только в начале
not ((Ord(Key) = 44) and (Pos(',', TEdit(Sender).Text) = 0)) then // запятая только одна
Key := #0;
end;
3. Проверка через таймер (универсальное решение)
Для более надежной проверки, включая защиту от вставки текста, можно использовать таймер:
function IsValidNumber(const AText: string; AllowFloat, AllowNegative: Boolean): Boolean;
var
Dummy: Double;
I: Integer;
begin
Result := TryStrToFloat(AText, Dummy);
if not Result and AllowNegative then
Result := TryStrToFloat(Copy(AText, 2, MaxInt), Dummy) and (AText[1] = '-');
if not Result and not AllowFloat then
Result := TryStrToInt(AText, I);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if not IsValidNumber(Edit1.Text, True, True) then
Edit1.Text := '';
end;
Установите интервал таймера на 100-200 мс для быстрой реакции.
4. Использование TryStrToInt вместо исключений
Как отметил пользователь wp, лучше использовать TryStrToInt вместо StrToInt с обработкой исключений:
function IsAnInteger(const AText: string): Boolean;
var
Dummy: Integer;
begin
Result := TryStrToInt(AText, Dummy);
end;
5. Альтернативные компоненты
Если вам нужно более надежное решение, рассмотрите использование специализированных компонентов:
TSpinEdit - встроенный компонент для ввода чисел
TATEdit из пакета TATSynEdit (как упомянул AlexTP) с OptInputNumberOnly
TJvValidateEdit из библиотеки JVCL
Пример полной реализации
Вот пример полной реализации компонента, наследующего от TEdit, с поддержкой только числового ввода:
type
TNumericEdit = class(TEdit)
private
FAllowFloat: Boolean;
FAllowNegative: Boolean;
procedure SetAllowFloat(const Value: Boolean);
procedure SetAllowNegative(const Value: Boolean);
procedure CheckText;
protected
procedure KeyPress(var Key: Char); override;
procedure DoEnter; override;
procedure Change; override;
public
constructor Create(AOwner: TComponent); override;
published
property AllowFloat: Boolean read FAllowFloat write SetAllowFloat default False;
property AllowNegative: Boolean read FAllowNegative write SetAllowNegative default False;
end;
constructor TNumericEdit.Create(AOwner: TComponent);
begin
inherited;
FAllowFloat := False;
FAllowNegative := False;
end;
procedure TNumericEdit.KeyPress(var Key: Char);
begin
inherited;
if not (Ord(Key) in [48..57]) and // цифры
not (Ord(Key) = 8) and // Backspace
not (Ord(Key) = 46) and // Delete
not ((Ord(Key) = 45) and FAllowNegative and (SelStart = 0)) and // минус
not ((Ord(Key) in [44, 46]) and FAllowFloat and
(Pos(FormatSettings.DecimalSeparator, Text) = 0)) then // десятичный разделитель
Key := #0;
end;
procedure TNumericEdit.CheckText;
var
D: Double;
I: Integer;
IsValid: Boolean;
begin
if Text = '' then Exit;
if FAllowFloat then
IsValid := TryStrToFloat(Text, D)
else
IsValid := TryStrToInt(Text, I);
if not IsValid and FAllowNegative then
if Text[1] = '-' then
if FAllowFloat then
IsValid := TryStrToFloat(Copy(Text, 2, MaxInt), D)
else
IsValid := TryStrToInt(Copy(Text, 2, MaxInt), I);
if not IsValid then
Text := '';
end;
procedure TNumericEdit.DoEnter;
begin
inherited;
CheckText;
end;
procedure TNumericEdit.Change;
begin
inherited;
CheckText;
end;
procedure TNumericEdit.SetAllowFloat(const Value: Boolean);
begin
FAllowFloat := Value;
CheckText;
end;
procedure TNumericEdit.SetAllowNegative(const Value: Boolean);
begin
FAllowNegative := Value;
CheckText;
end;
Заключение
Хотя свойство NumbersOnly в TEdit не всегда работает корректно на всех платформах, существует несколько способов реализовать числовой ввод в Delphi. Выбор метода зависит от ваших требований:
Для простых случаев достаточно обработчика OnKeyPress
Для более надежной проверки используйте таймер или наследуйте свой компонент
Для профессиональных решений рассмотрите специализированные компоненты
Представленные решения работают на всех виджетсетах, включая проблемные Cocoa, GTK2 и GTK3, и обеспечивают надежный ввод только числовых значений.
В контексте описывается проблема неработающего свойства NumbersOnly компонента TEdit в Delphi и предлагаются различные решения для реализации числового ввода, включая обработчики событий, таймеры, проверку текста и использование альтернативных компоненто
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.