How do I read and change the value of a TEdit control? - delphi

I have a Form TForm1 having 5 TEdit and 2 TBitBtn.
I also need the Program so that after inputting Numeric Data in Edit1 and Edit2 on BitBtn1Click, Edit1 and Edit2 value will summed and will be displayed in Edit3.

You want to do something like this:
var
val1, val2, sum: Integer;
...
val1 := StrToInt(Edit1.Text);
val2 := StrToInt(Edit2.Text);
sum := val1 + val2;
Edit3.Text := IntToStr(sum);
If you want floating point arithmetic do it like this
var
val1, val2, sum: Double;
...
val1 := StrToFloat(Edit1.Text);
val2 := StrToFloat(Edit2.Text);
sum := val1 + val2;
Edit3.Text := FloatToStr(sum);

To read and set the value of a TEdit control, you simply reference the Text property of the control. The Text property is of type String.
Since Text is a String property, you can treat it in your code as a String variable. You can pass it into a function that expects a String constant:
// Edit1 is the name of the TEdit control
// Display the value in the edit control to the user
ShowMessage(Edit1.Text);
You can assign it to a String variable with a simple assignment:
var
// My string variable
myString: String;
begin
// Edit1 is the Name of the control
myString := Edit1.Text;
end;
To set the value of a TEdit control, you simply assign a string to the Text property. This could be a String constant:
Edit1.Text := 'hello';
Or it could be from a String variable:
Edit1.Text := myString;
Math is done on numeric types, so for arithmetic, you'll need to use a function to convert the string values into numbers.
For Integer arithmetic, you can use StrToInt() or StrToIntDef():
var
myInteger: Integer;
begin
// Convert Edit1.Text string to a number and assign to numeric type for math
// If the value in Edit1.Text cannot be converted, an exception will be raised
myInteger := StrToInt(Edit1.Text);
end;
Using StrToIntDef():
var
myInteger: Integer;
begin
// If Edit1.Text cannot be converted, the default value of 0 will be used
myInteger := StrToIntDef(Edit1.Text, 0);
end;
For floating point arithmetic, use StrToFloat() or StrToFloatDef() instead.
To assign an Integer back to the Text property, you'll need to convert the Integer to a String before you assign it:
var
myInteger: Integer;
begin
myInteger := 12;
Edit1.Text := IntToStr(myInteger);
end;
For floating points, use FloatToStr().
Finally, to put everything together, to get the numeric values of two edit boxes and display the sum in a third edit box, simply do this:
var
// Floating point variables
value1: Real;
value2: Real;
sum: Real;
begin
// Get the values from the edit boxes, converting them to floating point types
value1 := StrToFloat(Edit1.Text);
value2 := StrToFloat(Edit2.Text);
// Sum them
sum := value1 + value2;
// Convert the sum to string and assign back to edit box
Edit3.Text := FloatToStr(sum);
end;
Or in one step:
Edit3.Text := FloatToStr(StrToFloat(Edit1.Text) + StrToFloat(Edit2.Text));

I noticed the following snippet:
so that after inputting Numeric Data in Edit1 and Edit2
If you want to only allow Numeric data, it's a good idea to disallow non numeric data in the edit boxes.
Here's how to do that.
const
TabKey = #9;
Backspace = #8;
Enter = #13;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (Key in ['0'..'9','-',TabKey,Enter,Backspace]) then Key:= #0; //integers
//realnumbers: if not (Key in ['0'..'9','-','e','E','.',TabKey,Enter,Backspace]) then Key:= #0;
end;
If you only have integer data, this will do, if you have scientific numbers, you need to do some testing for the letter e and the decimal point as well to allow for irrational numbers.
Regardless of what you do it's a good idea to check to see the input is a valid number and let the user know.
procedure TForm1.Edit1Change(Sender: TObject);
var
MyEdit: TEdit;
OtherEdit: TEdit;
TryNumber: double;
OtherNumber: double;
Success: boolean;
begin
Success:= true;
if (Sender is TEdit) then begin
MyEdit:= TEdit(Sender);
try
if MyEdit.Text = '' then TryNumber:= 0
else TryNumber:= StrToFloat(MyEdit.Text);
MyEdit.Color:= clWindow; //all is OK make edit standard white.
MyEdit.Hint:= '';
except
MyEdit.Color:= clRed; //Let the user know the output will not compute.
MyEdit.Hint:= MyEdit.Text + ' is not a valid number ';
Success:= false;
end;
end;
if (MyEdit = Edit1) then OtherEdit:= Edit2
else OtherEdit:= Edit1;
try
if OtherText.Text = '' then OtherNumber:= 0
else OtherNumber:= StrToFloat(OtherEdit.Text);
except
Success:= false;
end;
if Success then Edit3.Text:= FloatToStr(TryNumber + OtherNumber);
end;
Note that you can attach this Event to both Edit1 and Edit2, so you do not have to write the code twice. (But I'm sure you already knew that).
(Both edit's share the same event).
Important things to remember
Always use try..except to catch errors so that your program does not break with an error, see: http://www.delphibasics.co.uk/Article.asp?Name=Exceptions
If you have an editbox that only allows numeric data, consider using a maskedit which only allows valid chars, or code your own filter (if it's trivial to do so).
Try and use a single routine for multiple controls, so you don't end up with multiple very similar routines that all do almost the same thing. That way if you change something, you'll only have to change it in one place and it will work in all the controls that make use of that routine.

Related

TCheckListBox strange behaviour, not showing first char

So I have a CheckListBox with 6 Items :
Items.Strings = (
'Banana'
'Apple'
'Pomelo'
'Orange'
'Peach'
'BlueBarry')
If I want to show them then into a ShowMessage dialog, the message printed is.
'anana','pple','omelo','range','each','lueBarry'.
The procedure I use is this.
procedure TForm1.Button1Click(Sender: TObject);
var I : Integer;
begin
for I := 0 to CheckListBox1.Items.Count - 1 do
ShowMessage(CheckListBox1.Items.ValueFromIndex[I]);
end;
Why can't I get the first char from my Item?
Try to insert items in the right way like this
procedure TForm1.Button1Click(Sender: TObject);
begin
CheckListBox1.Items.Add('Banana');
CheckListBox1.Items.Add('Apple');
CheckListBox1.Items.Add('Pomelo');
CheckListBox1.Items.Add('Orange');
CheckListBox1.Items.Add('Peach');
CheckListBox1.Items.Add('BlueBarry');
end;
the result will be:
then...
procedure TForm1.Button2Click(Sender: TObject);
var I : Integer;
begin
for I := 0 to CheckListBox1.Items.Count - 1 do
ShowMessage(CheckListBox1.Items[I]);
end;
You cannot use ValueFromIndex for your porpouse.
TStrings.ValueFromIndex
Return the value part of a string based on it's index.
Declaration
public property TStrings.ValueFromIndex : string
read GetValueFromIndex
write SetValueFromIndex;
Description
ValueFromIndex returns the value part of a string based on the string index. The value part are all characters in the string after the NameValueSeparator character, or all characters if the NameValueSeparator character is not present.
TStrings.NameValueSeparator
Value of the character used to separate name,value pairs
Declaration
public property TStrings.NameValueSeparator : Char
read FNameValueSeparator
write SetNameValueSeparator;
Description
NameValueSeparator is the character used to separate name,value pair. By default, this is the equal sign (=), resulting in Name=Value pairs.
It can be set to a colon for Name : Value pairs.
thake a look at the vcl source :O
function TStrings.GetValueFromIndex(Index: Integer): string;
begin
if Index >= 0 then
Result := Copy(Get(Index), Length(Names[Index]) + 2, MaxInt) else
Result := '';
end;

How can I loop through a delimited string and assign the contents of the string to local delphi variables?

I have written a Delphi function that loads data from a .dat file into a string list. It then decodes the string list and assigns to a string variable. The contents of the string use the '#' symbol as a separator.
How can I then take the contents of this string and then assign its contents to local variables?
// Function loads data from a dat file and assigns to a String List.
function TfrmMain.LoadFromFile;
var
index, Count : integer;
profileFile, DecodedString : string;
begin
// Open a file and assign to a local variable.
OpenDialog1.Execute;
profileFile := OpenDialog1.FileName;
if profileFile = '' then
exit;
profileList := TStringList.Create;
profileList.LoadFromFile(profileFile);
for index := 0 to profileList.Count - 1 do
begin
Line := '';
Line := profileList[Index];
end;
end;
After its been decoded the var "Line" contains something that looks like this:
example:
Line '23#80#10#2#1#...255#'.
Not all of the values between the separators are the same length and the value of "Line" will vary each time the function LoadFromFile is called (e.g. sometimes a value may have only one number the next two or three etc so I cannot rely on the Copy function for strings or arrays).
I'm trying to figure out a way of looping through the contents of "Line", assigning it to a local variable called "buffer" and then if it encounters a '#' it then assigns the value of buffer to a local variable, re-initialises buffer to ''; and then moves onto the next value in "Line" repeating the process for the next parameter ignoring the '#' each time.
I think I have been scratching around with this problem for too long now and I cannot seem to make any progress and need a break from it. If anyone would care to have a look, I would welcome any suggestions on how this might be achieved.
Many Thanks
KD
You need a second TStringList:
lineLst := TStringList.Create;
try
lineLst.Delimiter := '#';
lineLst.DelimitedText := Line;
...
finally
lineLst.Free;
end;
Depending on your Delphi version you can set lineLst.StrictDelimiter := true in case the line contains spaces.
You can do something like this:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, StrUtils;
var
S : string;
D : string;
begin
S := '23#80#10#2#1#...255#';
for D in SplitString(S,'#') do //SplitString is in the StrUtils unit
writeln(D);
readln;
end.
You did not tag your Delphi version, so i don't know if it applies or not.
That IS version-specific. Please do!
In order of my personal preference:
1: Download Jedi CodeLib - http://jcl.sf.net. Then use TJclStringList. It has very nice split method. After that you would only have to iterate through.
function Split(const AText, ASeparator: string; AClearBeforeAdd: Boolean = True): IJclStringList;
uses JclStringLists;
...
var s: string; js: IJclStringList.
begin
...
js := TJclStringList.Create().Split(input, '#', True);
for s in js do begin
.....
end;
...
end;
2: Delphi now has somewhat less featured StringSplit routine. http://docwiki.embarcadero.com/Libraries/en/System.StrUtils.SplitString
It has a misfeature that array of string type may be not assignment-compatible to itself. Hello, 1949 Pascal rules...
uses StrUtils;
...
var s: string;
a_s: TStringDynArray;
(* aka array-of-string aka TArray<string>. But you have to remember this term exactly*)
begin
...
a_s := SplitString(input, '#');
for s in a_s do begin
.....
end;
...
end;
3: Use TStringList. The main problem with it is that it was designed that spaces or new lines are built-in separators. In newer Delphi that can be suppressed. Overall the code should be tailored to your exact Delphi version. You can easily Google for something like "Using TStringlist for splitting string" and get a load of examples (like #Uwe's one).
But you may forget to suppress here or there. And you may be on old Delphi,, where that can not be done. And you may mis-apply example for different Delphi version. And... it is just boring :-) Though you can make your own function to generate such pre-tuned stringlists for you and carefully check Delphi version in it :-) But then You would have to carefully free that object after use.
I use a function I've written called Fetch. I think I stole the idea from the Indy library some time ago:
function Fetch(var VString: string; ASeperator: string = ','): string;
var LPos: integer;
begin
LPos := AnsiPos(ASeperator, VString);
if LPos > 0 then
begin
result := Trim(Copy(VString, 1, LPos - 1));
VString := Copy(VString, LPos + 1, MAXINT);
end
else
begin
result := VString;
VString := '';
end;
end;
Then I'd call it like this:
var
value: string;
line: string;
profileFile: string;
profileList: TStringList;
index: integer;
begin
if OpenDialog1.Execute then
begin
profileFile := OpenDialog1.FileName;
if (profileFile = '') or not FileExists(profileFile) then
exit;
profileList := TStringList.Create;
try
profileList.LoadFromFile(profileFile);
for index := 0 to profileList.Count - 1 do
begin
line := profileList[index];
Fetch(line, ''''); //discard "Line '"
value := Fetch(line, '#')
while (value <> '') and (value[1] <> '''') do //bail when we get to the quote at the end
begin
ProcessTheNumber(value); //do whatever you need to do with the number
value := Fetch(line, '#');
end;
end;
finally
profileList.Free;
end;
end;
end;
Note: this was typed into the browser, so I haven't checked it works.

How to convert an integer value to boolean in delphi

I have a database field value, which is an integer like 0 and 1. Is it possible to convert
this integer values to Boolean while loading the data in to a DB Grid. I'm expecting without condition checking, like direct typecasting.
Thanks
There is no way to convert Integer to Boolean.
you can implement a function like this
function IntToBool(const AnInt: Integer): Boolean;
begin
if AnInt = 0 then Result := False
else Result := True;
end;
I guess that you want to show a database field in DBGrid as a CheckBox. If so, read article by Zarko Gajic. It is about Boolean fields, but you can easily modify the code for your needs.
The most simple solution to your problem would probably be to use a boolean calcfield.
If you need to edit it from the DBGrid, it gets a little bit more tricky (But still possible).
If you want to show the words "True" and "False" in DBGrid, you should use OnGetText event of Field like this :
procedure TMyForm.MyDataSetFieldGetText(Sender: TField;
var Text: string; DisplayText: Boolean);
begin
case Sender.AsInteger of
0 : Text := 'False';
1 : Text := 'True';
else
Text := '-';
end;
end;
try this sample function here:
function IntToBooleanStr(AInteger: Integer): string;
begin
case AInteger of
0:begin
Result := 'False';
end;
1:begin
Result := 'True';
end
else
Result := 'False';
end;
end;
that's all and you can use it inside the combobox onChange Event for FILTERING Some Logical Data that has the boolean values inside.
like here:
procedure TFrm_Books.ComBox_AvailableFilterChange(Sender: TObject);
begin
Table_Book.Filtered := False;
Table_Book.FilterOptions := [foCaseInsensitive];
Table_Book.Filter := '';
Table_Book.Filter := 'Available = ' + IntToBooleanStr(ComBox_AvailableFilter.ItemIndex);
Table_Book.Filtered := True;
end;
and here is the DFM Code for this combobox:
object ComBox_AvailableFilter: TComboBox
Left = 336
Top = 120
Width = 193
Height = 21
ItemHeight = 13
Items.Strings = (
'Not Available'
'Available')
TabOrder = 0
end
i hope this function resolve your question Above.

How to wash/validate a string to assign it to a componentname?

I have a submenu that list departments. Behind this each department have an action who's name is assigned 'actPlan' + department.name.
Now I realize this was a bad idea because the name can contain any strange character in the world but the action.name cannot contain international characters. Obviously Delphi IDE itself call some method to validate if a string is a valid componentname. Anyone know more about this ?
I have also an idea to use
Action.name := 'actPlan' + department.departmentID;
instead. The advantage is that departmentID is a known format, 'xxxxx-x' (where x is 1-9), so I have only to replace '-' with for example underscore. The problem here is that those old actionnames are already persisted in a personal textfile. It will be exceptions if I suddenly change from using departments name to the ID.
I could of course eat the exception first time and then call a method that search replace that textfile with the right data and reload it.
So basically I search the most elegant and futureproof method to solve this :)
I use D2007.
Component names are validated using the IsValidIdent function from SysUtils, which simply checks whether the first character is alphabetic or an underscore and whether all subsequent characters are alphanumeric or an underscore.
To create a string that fits those rules, simply remove any characters that don't qualify, and then add a qualifying character if the result starts with a number.
That transformation might yield the same result for similar names. If that's not something you want, then you can add something unique to the end of the string, such as a checksum computed from the input string, or your department ID.
function MakeValidIdent(const s: string): string;
var
len: Integer;
x: Integer;
c: Char;
begin
SetLength(Result, Length(s));
x := 0;
for c in s do
if c in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then begin
Inc(x);
Result[x] := c;
end;
SetLength(Result, x);
if x = 0 then
Result := '_'
else if Result[1] in ['0'..'9'] then
Result := '_' + Result;
// Optional uniqueness protection follows. Choose one.
Result := Result + IntToStr(Checksum(s));
Result := Result + GetDepartment(s).ID;
end;
In Delphi 2009 and later, replace the second two in operators with calls to the CharInSet function. (Unicode characters don't work well with Delphi sets.) In Delphi 8 and earlier, change the first in operator to a classic for loop and index into s.
I have written a routine
// See SysUtils.IsValidIdent:
function MakeValidIdent(const AText: string): string;
const
Alpha = ['A'..'Z', 'a'..'z', '_'];
AlphaNumeric = Alpha + ['0'..'9'];
function IsValidChar(AIndex: Integer; AChar: Char): Boolean;
begin
if AIndex = 1 then
Result := AChar in Alpha
else
Result := AChar in AlphaNumeric;
end;
var
i: Integer;
begin
Result := AText;
for i := 1 to Length(Result) do
if not IsValidChar(i, Result[i]) then
Result[i] := '_';
end;
which makes Pascal identifiers from strings.
You might also want to copy FindUniqueName from Classes.pas and apply that to the result from MakeValidIdent.
Here is my routine:
function MakeValidIdent(const s: string): string;
begin
Result := 'clm'; //Prefix
for var c in s do
if CharInSet(c, ['A'..'Z', 'a'..'z', '0'..'9', '_']) then
Result := Result + c;
end;

String representation of the content type of a Variant?

First, apologies for my English, I hope it makes sense what I`ve written here. Now to my problem.
How can I get the string representation of the content type of a Variant using TypInfo.GetEnumName(). I have tried the following, without luck, I get a numeric representation.
myString := GetEnumName( TypeInfo(TVarType), TVarData(myVar).VType );
Thank you.
Just use the build-in Delphi function for getting the string representation of a Variant type.
var
MyVariantType: string;
MyVariant: Variant;
begin
MyVariant := 'Hello World';
MyVariantType := VarTypeAsText(VarType(MyVariant));
ShowMessage(MyVariantType); //displays: String
MyVariant := 2;
MyVariantType := VarTypeAsText(VarType(MyVariant));
ShowMessage(MyVariantType); //displays: Byte
end;
Quoting from the Delphi 2007 help:
Use GetEnumName to convert a Delphi enumerated value into the symbolic name that represents it in code.
That means that you can't use it for that purpose, as TVarData.VType is not an enumerated value, but an integer which is set to one of the constants in System.pas that are taken from the Windows SDK wtypes.h file. Look at the source of GetEnumName(), it does immediately return a string containing the value of the integer.
Edit:
is there any other way to get the string representation of TVarData.VType
You can determine the string representation manually. First you need to be aware of that there are several bits of information encoded in that integer, so a simple case statement or array lookup will not work. The lower 12 bits are the type mask, and the upper bits encode information about whether it is a vector or array type and whether it is given by reference or not. The important parts are:
const
varTypeMask = $0FFF;
varArray = $2000;
varByRef = $4000;
So you could do something like:
function VariantTypeName(const AValue: TVarData): string;
begin
case AValue.VType and varTypeMask of
vtInteger: Result := 'integer';
// ...
end;
if AValue.VType and varArray <> 0 then
Result := 'array of ' + Result;
if AValue.VType and varByRef <> 0 then
Result := Result + ' by ref';
end;
Since it's not an enum, you'll have to do it manually. Write something like this:
function VariantTypeName(const value: TVarData): string;
begin
case value.VType of
vtInteger: result := 'integer';
//and so on
end;
Or, since the values in System.pas are listed in order, you could try declaring a const array of strings and have your VariantTypeName function return the appropriate member of the array.
Here's a thought for Delphi versions that don't support VarTypeAsText: You could define a enumerate type yourself that follows the VType values:
type
{$TYPEINFO ON}
TMyVarType = (
varEmpty = System.varEmpty,
varNull = System.varNull,
// etc...
);
(Fill the unused enum slots too - see Why do I get "type has no typeinfo" error with an enum type for the reasoning behind this).
Next, use these functions to read the Variants' type as your own enumerate type :
function MyVarType(VType: TVarType): TMyVarType; overload;
begin
Result := TMyVarType(VType);
end;
function MyVarType(V: Variant): TMyVarType; overload;
begin
Result := TMyVarType(TVarData(V).VType);
end;
And then you can convert it to a string like this :
function VarTypeToString(aValue: TMyVarType): string;
begin
Result := GetEnumName(TypeInfo(TMyVarType), Ord(aValue));
end;

Resources