user2296565
user2296565

Reputation: 263

How to make edit control not accept 0 as first digit?

Well, I have an edit that takes only numbers (no letters, no symbols, only numbers). The problem is that i don't want the user to put 0 as the first digit, for example (0239847).

I was thinking of making a variable "x" --> byte which will count the lenght of the edit and delete the first digit, if it is 0:

var l:length; number:string100;
begin
l:length(edit1.text);

Now if the first digit is 0 (0239847), then delete it, BUT if there are not other numbers (only 0), leave it as it is.

Omg, i finally found the easiest way to do this:

procedure TForm1.Edit1Change(Sender: TObject);
var digit1:string;
begin
digit1:=edit1.Text;
if (digit1='00') or (digit1='01') or (digit1='02') or (digit1='03') or (digit1='04') or
(digit1='05') or (digit1='06') or (digit1='07') or (digit1='08') or (digit1='09') then
edit1.Text:=clear;    //or edit1.text:=0;  it's the same

end;

end.

The can still copy and paste numbers, but i don't mind

It was a pretty silly question, because the answer was easy and obvious, but if you are way too tired, it is hard to find it, i am sorry for getting you busy for this, Andreas, thanks for your help and tips.

Upvotes: 3

Views: 1911

Answers (2)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 108963

It is hard to do this well. The most ambitious approach would be to do a lot of logic on key down, paste, etc., like in this answer. You'd be surprised how many cases there are to handle!

This works pretty well, I think:

type
  TEdit = class(StdCtrls.TEdit)

...

procedure TEdit.KeyPress(var Key: Char);

  function InvalidKey: boolean;
  begin
     InvalidKey :=
       (
         (Key = '0') and
           (
             ((SelStart = 0) and (Length(Text) - SelLength <> 0))
             or
             ((SelStart = 1) and (Text[1] = '0'))
           )
       )
       or
       (
         not (Key in ['0'..'9', #8, ^Z, ^X, ^C, ^V])
       )
  end;

begin
  inherited;

  if InvalidKey then
  begin
    beep;
    Key := #0;
  end else if (SelStart = 1) and (Text[1] = '0') then
  begin
    Text := Copy(Text, 2);
  end;
end;

procedure TEdit.WMPaste(var Message: TWMPaste);    
var
  ClipbrdText: string;

  function ValidText: boolean;
  var
    i: Integer;
  begin
    result := true;
    for i := 1 to Length(ClipbrdText) do
      if not (ClipbrdText[i] in ['0'..'9']) then
      begin
        result := false;
        break;
      end;
  end;

  function RemoveLeadingZeros(const S: string): string;
  var
    NumLeadingZeros: integer;
    i: Integer;
  begin
    NumLeadingZeros := 0;
    for i := 1 to Length(S) do
      if S[i] = '0' then
        inc(NumLeadingZeros)
      else
        break;
    result := Copy(S, NumLeadingZeros + 1);
  end;

begin
  if Clipboard.HasFormat(CF_TEXT) then
  begin

    ClipbrdText := Clipboard.AsText;

    if not ValidText then
    begin
      beep;
      Exit;
    end;

    if SelStart = 0 then
      ClipbrdText := RemoveLeadingZeros(ClipbrdText);

    SelText := ClipbrdText;

  end
  else
    inherited;
end;

This looks complicated, but all this logic is necessary. For instance, the code handles all these cases well:

  • You can only enter digits.

  • If SelStart = 0 and not every character is selected, you cannot enter 0, because then you would get a leading zero.

  • If SelStart = 0 and every character is selected, you can enter 0. Because you should be able to enter this valid number.

    • A special case: If there are zero characters, then of course zero characters are selected, so by simply logic you will be able to enter 0 from the state of the empty string!
  • If SelStart = 1 and the Text[1] = '0' [if SelStart = 1, then Text[1] exists, so we rely on boolean short-circuit evaluation here], then you cannot enter 0.

  • If the contents of the edit field is 0 and you add a non-zero character after the zero, then the zero is removed (and there is no interference with the caret position).

  • You can only paste digits. If SelStart = 0, leading zeros (in the clipboard data) are discarded.

In fact, to make the behaviour perfect, you need even more code. For instance, suppose that the contents of the edit field is 123000456. As it is now, you can select the first three characters (123) and press Backspace or Delete to obtain the invalid text 000456. The solution is to add extra code that removes leading zeroes resulting from either of these two operations. Update: I have been doing some thinking, and have come to realise that it is probably best not to implement this (why?).

One shortcut, however, would be to 'prettify' the contents of the edit when it isn't focused, because when it isn't, you are much less likely to shoot the user in the foot by doing something with the contents of the edit.

For instance, you can do something like

function PrettifyNumber(const S: string): string;
var
  NumLeadingZeros: integer;
  i: Integer;
begin
  NumLeadingZeros := 0;
  for i := 1 to Length(S) do
    if S[i] = '0' then
      inc(NumLeadingZeros)
    else
      break;
  result := Copy(S, NumLeadingZeros + 1);
  if length(result) = 0 then
    result := S;
end;

and then replace the contents of the edit field by its prettified version when it loses keyboard focus. Of course, you should consider adding this functionality to a derived edit class, but for testing you can simply use the OnExit event:

procedure TForm1.Edit1Exit(Sender: TObject);
begin
  Edit1.Text := PrettifyNumber(Edit1.Text);
end;

I suppose that you want this functionality in a lot of edits in a major application (or not so major). So you will in fact write your own subclassed control. Then I suppose you will also need other types of verification than simply removal of leading zeros? You thus have to integrate this code nicely with the rest of the code. If in fact you will use this edit 'subclass' everywhere in a big application, you could also consider adding a shortcut, to the edit control class, so that you can prettify/verify the text on demand, and not only on exit (which is particularly difficult if the edit control is alone on its form!)

Update

On request, this is a simple solution that works badly:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if (Key = '0') and (Edit1.SelStart = 0) then Key := #0;
end;

I leave it as an exercise to figure out the issues with this approach. If you fix the issues, one by one, as you discover them, you will end up with my long code above!

Upvotes: 10

David Mir&#243;
David Mir&#243;

Reputation: 2758

You could use TmaskEdit. This force the user to input the numbers. After add eventchange for delete first digit if 0.

Example.dfm

.
.
object MaskEdit1: TMaskEdit
  Left = 352
   Top = 8
   Width = 120
   Height = 21
   EditMask = '0999999;0;_'
   MaxLength = 7
   TabOrder = 3
   OnChange = MaskEdit1Change
end
.
.

Example.pas

.
.
Procedure TForm1.MaskEdit1Change(Sender: TObject);
Begin
  If Length(TMaskEdit(Sender).Text) > 1 Then
    TMaskEdit(Sender).Text := IntToStr(StrToInt(TMaskEdit(Sender).Text));
End;
.
.

Upvotes: 1

Related Questions