вторник, 29 апреля 2014 г.

Как я обустроил TNumberBox

Я уже как-то жаловался на тяжёлую жизнь с TNumberBox. Три основные проблемы это:
1.Значение Value и OnChange живут отдельно от происходящего в поле ввода.
2.При тыкании пальцем в контрол число может внезапно поменяться.
3.При вводе цифр в нулевое поле ноль так и остаётся в начале числа.

Но что делать? Отказаться от имеющегося, написать и регистрировать свой компонент? Я настолько глуп и ленив, что мне это всё трудно, и нужно что-то совершенно простое. Например:

unit MyNumberBox;
 
interface
 
uses System.Classes, FMX.Edit;
 
type
  TNumberBox = class( FMX.Edit.TNumberBox )
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure DoChangeTracking; override;
  end;
 
implementation
uses
  System.SysUtils, System.Character;
 
{ TNumberBox }
 
procedure TNumberBox.DoChangeTracking;
var
  S : String;
  I : Integer;
  V : Single;
begin
  S := Text;
  if not S.IsEmpty then begin
    I := Low( S );
    while ( S[ I ] = '0' ) and S[ I + 1 ].IsDigit do
      Inc( I );
    if I <> Low( S ) then
      Text := S.Substring( I - Low( S ) );
  end;
  if TryTextToValue( Text, V, Value ) then
    if not SameStr( FloatToStr( V ), FloatToStr( Value ) ) then
      Value := V;
  inherited;
end;
 
procedure TNumberBox.MouseMove(Shift: TShiftState; X, Y: Single);
type
  PClass = ^TClass;
var
  ClassOld: TClass;
begin
  ClassOld := PClass(Self)^;
  PClass(Self)^ := TCustomEditBox;
  try
    MouseMove( Shift, X, Y );
  finally
    PClass(Self)^ := ClassOld;
  end;
end;
 
end.

Это такая подмена-заглушка, которая не требует никаких настроек среды разработки и никак не мешает в дизайн-тайме, а действует автоматически во время выполнения. DoChangeTracking выполняет обрезку лидирующего нуля (достаточно хорошо для меня) и проверяет изменения в Value. MouseMove перекрывает обработку скрола ( всего-то навсего можно было сохранять значение ноль в вертикальной прокрутке, но я глуп, и не знаю как это делать ) и на всякий случай (на всякий случай, да) вызывает "дедушкин" метод-обработчик. Вот и всё.

Теперь вставляем в интерфейсный uses наш перехватчик, чтобы он сидел в конце и переопределял тип TNumberBox.
unit uMain;
 
interface
 
uses
...
  FMX.ListBox, FMX.StdCtrls, FMX.Layouts, FMX.Edit, System.Actions, FMX.ActnList,
  MyNumberBox;
 
type
  TForm2 = class(TForm)
...
    ItemDenominator: TNumberBox;
...
 
Я использую XE5 и у меня всё работает замечательно. А как обстоят дела у вас?