Reputation: 263
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
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.
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!)
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
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