Information being uploaded to database even when conditions are false - delphi

I have a form which gets data from the user via edit boxes and combo boxes, this information then has to be uploaded into a database table so I have to do validation. I did just that and it keeps on saying ''is not a valid integer' yet it is not even supposed to upload anything to the database table as all conditions where not met because of the null check that I did. Did I do my validation wrong?
unit Unit6;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, ExtCtrls,unit7,unit5,unit4, DB, ADODB, Grids, DBGrids,
pngimage;
type
Tfrmupload = class(TForm)
Panel1: TPanel;
edtname: TEdit;
edtsurn: TEdit;
edtidn: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label5: TLabel;
Label6: TLabel;
Panel2: TPanel;
btnupload: TButton;
edtmail: TEdit;
cbprov: TComboBox;
Image1: TImage;
cbdiv: TComboBox;
Label4: TLabel;
procedure btnuploadClick(Sender: TObject);
procedure Label7Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmupload: Tfrmupload;
implementation
{$R *.dfm}
procedure Tfrmupload.btnuploadClick(Sender: TObject);
var
vname,vsurname,today,c,vidn,vprov,vmail,vdiv:string;
vage,year,i,age:integer;
bnotnull,bIDL,bmail,bage,byoung,valid,idrepeated:boolean;
dt:tdatetime;
begin
vname:=edtname.Text;
vsurname:=edtsurn.Text;
vidn:=edtidn.Text;
vdiv:=cbdiv.Text;
vprov:=cbprov.Text;
vmail:=edtmail.Text;
//booleans
bnotnull:=false;
bIdL:=false;
bmail:=false;
byoung:=false;
idrepeated:=false;
valid:=false;
//check if id entered is not in table already
if unit5.frmadmin.ADODetails.Locate('ID number',vidn,[]) then
begin
idrepeated:=true;
showmessage('ID Number already exists');
end;
//null check
if (vname<>'')and(vsurname<>'')and(vidn<>'')and(vprov<>'')and(vmail<>'')and(vdiv<>'') then
begin
bnotnull:=true;
end
else
begin
Showmessage('Complete All Fields!');
end;
//get current date
dt:=now;
year:=strtoint(formatdatetime('yyyy',dt));
c:=copy(vidn,1,2);
//calculate age from id number
if (strtoint(c)>=0) and (strtoint(c)<=22) then
begin
age:=year-(2000+strtoint(c));
end
else
begin
age:=year-(1900+strtoint(c));
end;
//ID length validation
if (length(vidn)=13) then
begin
bidl:=true;
end;
//check if contestant is not too young
if age<6 then
begin
showmessage('Contestant too young, cannot compete');
byoung:=true;
end;
//check if email is correct format
for i := 1 to length(vmail)do
begin
if vmail[i]='#' then
begin
bmail:=true;
end;
end;
//error message for email check
if bmail=false then
begin
showmessage('Incorrect Email Format');
end;
if bidl=false then
begin
showmessage('ID Number must be 13 characters');
end;
//checks if all the conditions are met and if so we can then upload to database
if (bnotnull=true) and (bidl=true) and (byoung=false)and (bmail=true)and (idrepeated=false) then
begin
valid:=true;
end;
if valid=true then
begin
unit5.frmadmin.ADODetails.Append;
unit5.frmadmin.ADODetails['Name(s)']:=vname;
unit5.frmadmin.ADODetails['Surname']:=vsurname;
unit5.frmadmin.ADODetails['ID Number']:=vidn;
unit5.frmadmin.ADODetails['Age']:=age;
unit5.frmadmin.ADODetails['Province']:=vprov;
unit5.frmadmin.ADODetails['Email']:=vmail;
unit5.frmadmin.ADODetails.Post;
unit4.frmcontest.ADOLead.Insert;
unit4.frmcontest.ADOLead['ID Number']:=unit4.frmcontest.DBComboID.Text;;
unit4.frmcontest.ADOLead['Name(s)']:=unit4.frmcontest.DBCombonme.Text;
unit4.frmcontest.ADOLead['Division']:=vdiv;
unit4.frmcontest.ADOLead.Post;
showmessage('Details Uploaded');
frmupload.Hide;
end;
//clears all inputs
edtname.Clear;
edtsurn.Clear;
edtidn.Clear;
cbdiv.Text:='';
cbprov.Text:='';
edtmail.Clear;
end;
procedure Tfrmupload.Label7Click(Sender: TObject);
begin
frmupload.Hide;
end;
end.

I think we need to see a sample database record that was saved to database with false conditions. Beside you can take precaution by applying Trim() function to the input from edit boxes
vname:=Trim(edtname.Text);
vsurname:=Trim(edtsurn.Text);
vidn:=Trim(edtidn.Text);
vdiv:=Trim(cbdiv.Text);
vprov:=Trim(cbprov.Text);
vmail:=Trim(edtmail.Text);
You may also check the length of variable vidn
If Length(vidn) < 4 then
//or
If Length(vidn) < 2 then

The mistake is pretty obvious. You have strtoint(aka STRING to INT) but you give it a formatted datetime.
year:=strtoint(formatdatetime('yyyy',dt));
I don't understand why you take the year the way you do it when you can just give the value of 2022 to the integer, but if you insist on doing it that way, you can first format dt, then use datetimetostr and then strtoint. Another thing which will reduce the code and your work and make everything much more easier is DB-aware components. What they do is, once you connect them to the db and the table and column you're working with, they automatically make updates or inserts. All you need to do is TMSQuery.Insert and then TMSQuery.Post when adding new rows and TMSQuery.Edit and TMSQuery.Post when changing values.

Related

Delphi - How to access a form from a childwindow in a mdi-application

I have an MDI programm with a child window which contains a TMemo and a button, there is also another Form1 in the application.
Clicking the button:
Unit1.Form1.Parent:=Form1;
Unit1.Form1.Show;
On Form1 is also a button:
????.(FindComponent('dataMm') as TMemo).lines.append('hallo child');
My question is: what is the right syntax to replace the ???? to access the TMemo on the childform.
PS.: the TMainForm has the following procedure:
Procedure TMainForm.CreateMDIChild(const Name: string);
var
Child: TMDIChild;
begin
Child := TMDIChild.Create(Application);
MyChild:=Child;
end;
where MyChild is declared in the public section as
var MyChild: TForm;
Thanks for your time and attention
Regards
At the time of writing this answer, you have not yet responded to my comment where I requested some clarifications, but I believe I understand your problem.
So here is the essens of the main form:
unit UMain;
uses
..., UChild, ...
type
TMDIMainForm = class(TForm)
MainMenu1: TMainMenu;
CreateChild1: TMenuItem;
ShowForm11: TMenuItem;
...
public
MyChild: TMDIChild;
As you usually have many instances of a child form, you need to use a list or array instead of the MyChild variable, but this is what you had defined.
And the implementation of those menu items, as well as the CreateMDIChild method:
procedure TMDIMainForm.CreateChild1Click(Sender: TObject);
begin
CreateMDIChild('TheOneAndOnly');
end;
procedure TMDIMainForm.ShowForm11Click(Sender: TObject);
begin
Form1.Show;
end;
procedure TMDIMainForm.CreateMDIChild(const name: string);
//var
// Child: TMDIChild; no need for this temporary variable
begin
MyChild := TMDIChild.Create(Application);
MyChild.Parent := self;
MyChild.Visible := True;
// MyChild:=Child;
end;
The TMDIChild class is as simple as:
unit UChild;
type
TMDIChild = class(TForm)
dataMm: TMemo;
end;
as it only holds the memo (for the purpose of this discussion).
Finally the TForm1 class:
unit UForm1;
interface
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
implementation
uses UMain, UChild;
procedure TForm1.Button1Click(Sender: TObject);
begin
MDIMainForm.MyChild.dataMm.Lines.Add('hello child!')
end;
I guess we have now arrived to your actual question, how to address the memo in the MyChild form.
So, note that you need to add the uses UMain, UChild; clause to refer to those units. This uses clause must be in the implementation part. If it would be in the interfacepart, it would create a circular reference, which is forbidden.
Now you can refer to the memo in the child form which has it's reference in the main form.
Edit
After you clarified in a comment: ("Actually the Button to make visible the Form1 is on the child, and this can happen on any child. Than on Form1 a button activates : dataMm.Lines.Add('hello child!'). I need a reference or connection to this currently active child", you can do as follows:
Add to the TForm1 type, a new private field to hold the MDIChild reference, and a new method, in which you can pass the reference of the calling MDIChild, e.g.
TForm1 = class(TForm)
...
private
CurrentMDIChild: TMDIChild;
public
procedure DoActivate(Sender: TMDIChild);
The implementation of DoActivate() becomes:
procedure TForm1.DoActivate(Sender: TMDIChild);
begin
if (Sender is TMDIChild) then
begin
CurrentMDIChild := Sender;
Show;
end;
end;
The TMDIChild.Button1Click becomes e.g.
procedure TMDIChild.Button1Click(Sender: TObject);
begin
Form1.DoActivate(self);
end;

DELPHI XE3 ADO Query makes my Application freeze while it waits for data

Is there any solution so as not to get my UI frozen while it waits for data to come from the Database ??
Example:
We have an adoquery and we do
adoquery.active:=false;
adoquery.active:=true;
When the adoquery tries to get data from the db everything in the UI is frozen and if the user clicks then the whole program becomes
Not Responding!!
Is there any cure to this problem ?
You might use [eoAsyncExecute,eoAsyncFetch] in the ExecuteOptions, this will require the Dataset is using an explicit TAdoConnection.
To avoid unexpected behavior you will have to use DisableControls before opening the Dataset, and EnableControls after FetchComplete.
A direct called EnableControls within FetchComplete may cause Exceptions, so using Postmessage with an user defined Message will be necessary.
Const
// define a message for handling completed AsyncFetch after leaving FetchComplete
WM_MYConnect=WM_User + 77;
type
TMyForm = class(TForm)
MyDataSet: TADODataSet;
MyDatasource: TDataSource;
DBGrid1: TDBGrid;
Button1: TButton;
ADOConnection1: TADOConnection;
procedure Button1Click(Sender: TObject);
procedure MyDataSetFetchComplete(DataSet: TCustomADODataSet; const Error: Error; var EventStatus: TEventStatus);
private
{ Private-Deklarationen }
Procedure ConnectDatasource(var MSG:TMessage); message WM_MYConnect;
public
{ Public-Deklarationen }
end;
var
MyForm: TMyForm;
implementation
{$R *.dfm}
procedure TMyForm.Button1Click(Sender: TObject);
begin
MyDataset.Close;
// example blocking command for SQL-Server 10 seconds
MyDataset.CommandText := 'WAITFOR DELAY ''00:00:10'' Select* from aa';
Mydataset.DisableControls;
Mydataset.ExecuteOptions := [eoAsyncExecute,eoAsyncFetch];
MyDataset.Open;
end;
procedure TMyForm.ConnectDatasource(var MSG:TMessage);
begin
TAdodataset(MSG.WParam).EnableControls;
// MyDataSource.DataSet := MyDataset;
end;
procedure TMyForm.MyDataSetFetchComplete(DataSet: TCustomADODataSet; const Error: Error; var EventStatus: TEventStatus);
begin
// if we want to add a datasource we will have to ensure that it will happen after leaving FetchComplete
// so we call our procedure ConnectDatasource via PostMessage
PostMessage(Handle,WM_MYConnect,wParam(DataSet),0);
end;
end.

Writing combobox items to a file? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I've created a TComboBox that displays a drop down menu of a list of about 30 items.
When I select one item from the list in the program it is not written to my file however the other TEdit parts of the form are.
How do I go about assigning these items from the combobox to a string in order to write them to a file?
I'm clueless as to how to write the procedure which will do this as you can see it isn't specified below
unit AddStudent2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, ActnList;
CONST
Filename = 'c:\COMP4\StudentList.txt';
type
NewStudent = Record
Name : string [10];
Surname : string [10];
TutorGroup : string [10];
CareerPreference : string [10];
ID : Integer;
end;
StudentList = file of NewStudent;
{ TAddStudent }
TAddStudent = class(TForm)
AddStudentButton: TButton;
CareerList: TComboBox; //item i need help with//
Label1: TLabel;
LabelChecker: TLabel;
StudentForenameEntry: TEdit;
StudentTutorGroup: TLabel;
StudentSurnameEntry: TEdit;
StudentSurname: TLabel;
StudentName: TLabel;
StudentPreferredSubject1: TLabel;
StudentTutorGroupEntry: TEdit;
procedure ButtonAddStudentClick(Sender : TObject);
end;
{StudentForm}
var
StudentForm: TAddStudent;
StudentRec : NewStudent;
StudentFile : StudentList;
MyList : TStringList;
i : integer;
TextDrop : String;
implementation
uses
StudentAddNotifier, SubjectError;
{$R *.lfm}
Procedure TAddStudent.ButtonAddStudentClick(Sender : TObject);
begin
AssignFile(StudentFile,'c:\COMP4\v2\rsd\sa\COMP44\StudentList.txt');
StudentRec.Name:= StudentForenameEntry.Text;
StudentRec.Surname:= StudentSurnameEntry.Text;
StudentRec.TutorGroup:= StudentTutorGroupEntry.Text;
begin
begin
StudentRec.CareerPreference := CareerList.Items[CareerList.ItemIndex] ;
reset(StudentFile);
seek(StudentFile,System.FileSize(StudentFile));
write(StudentFile,StudentRec);
CloseFile(StudentFile);
reset(StudentFile);
seek(StudentFile,System.FileSize(StudentFile));
write(StudentFile,StudentRec);
CloseFile(StudentFile);
StudentForm.Close;
Form3.show;
end;
end;
Just access the TComboBox.Items by the TComboBox.ItemIndex if the ItemIndex is not equal to -1. (-1 indicates no item is selected.)
var
CareerItem: string;
begin
if Career.ItemIndex <> -1 then
begin
CareerItem := CareerList.Items[ComboBox1.ItemIndex];
// Do whatever with CareerItem
end;
end;
Asside from using the ItemIndex you can also use the Text property to get the currently selected item: StudentRec.CareerPreference := Careerlist.Text;
Note that if the user typed something in the combobox that you will get that text, so you might want to disable that by setting the combobox to read only.

How to eliminate variables "panel1, panel2, panel3 .. etc." in Delphi?

i have this type
type
TMain = class(TForm)
panel1: Tpanel;
panel2: Tpanel;
panel3: Tpanel;
panel4: Tpanel;
panel5: Tpanel;
panel6: Tpanel;
panel7: Tpanel;
panel8: Tpanel;
......
panel45: Tpanel;
label1: TLabel;
label2: TLabel;
label3: TLabel;
label4: TLabel;
label5: TLabel;
label6: TLabel;
label7: TLabel;
...........
label109: TLabel;
How can i call this components in one line... inside the Type?
Thank you ...
UpDate....
Base from the anwser i get and accepted it works great when i have all this components and make the actions like a button1.click from the main form...
But i use to make the actions from units... so
When i click a button i great a procedure DoMaths(Sender: TObject);
procedure Tform1.DoMaths(Sender: TObject);
begin
if TButton1(Sender).hint := 'Make the standard Package' then
do_Maths_standard_package;
end;
the do_Maths_standard_package is in unit ComplexMaths.
is the procedure do_Maths_standard_package form unit ComplexMaths it calls some components form Form1... like Form1.label1 etc...
So when i call the RegisterClass(TLabel) and erase the Tlabel from the type it gives an error that it cant find the Label1...
Please can someone help me so not to do the hole program from the start...
Thank you again...
You can delete the name of a TPanel or TLabel then it only exists in the Controls List not in the Type declaration of the form. You either need to leave one Label and one panel or
add:
initialization
RegisterClass(TPanel);
RegisterClass(Tlabel);
end.
at the end of the form.
This makes forms with a lot of controls much neater.
Use the TForm.Controls array:
var
i: Integer;
Pnl: TPanel;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TPanel then
begin
Pnl := TPanel(Controls[i]);
Pnl.Caption := 'This is panel ' + IntToStr(i);
end;
end;
Delphi automatically creates two lists for each TWinControl:
Controls contains a list of all TControl items the control contains.
Components is a list of all the TComponents on a control.
Note that all Controls are Components, but not all Components are Controls; that's why there are two lists. (A TDataSet, for instance, is in the Components list, but not in the Controls list; a TEdit, on the other hand, will be in both.)
You can use the same technique to iterate through the components on a panel or other container as well - TPanel has both Control and Component arrays, for instance.
If what you actually want is to reduce the number of items inside the type declaration itself, create them at runtime instead - Delphi will automatically add them to the arrays based on the Owner and Parent:
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
Panel: TPanel;
Label: TLabel;
begin
for i := 0 to 10 do
begin
Panel := TPanel.Create(Self); // Set who frees it
Panel.Parent := Self; // Set display surface
Panel.Align := alTop;
Panel.Name := Format('Panel%d', [i]); // Not necessary
Panel.Caption := Panel.Name;
// Add a label on each panel, just for fun.
Label := TLabel.Create(Panel); // Panel will free label
Label.Parent := Panel; // Label will show on panel
Label.Top := 10;
Label.Left := 10;
Label.Name := Format('Label%d', [i]);
Label.Caption := Label.Caption; // Not necessary
end;
end;
Note that creating them yourself is not an "optimization", as it just shifts the loading from the VCL doing it to you doing it yourself. It will reduce the size of the .dfm file, but won't speed up your code or loadtime any, and it means you can't visually lay out the form as well. (It's also much harder to maintain your code, because your controls have meaningless names. It's much easier to know what LastNameEdit or EditLastName is than Edit1 when you read the code 6 months from now.)
You can use arrays:
panels : array[1..45] of TPanel;
This would allow you to create an array of your controls, and access and use them by index.

Save and restore event handlers

My class contains dataset (TDataSet). Users of my class can assign event handlers for this dataset:
ds.FieldByName('ID').OnChange := #ID_OnChange;
Then I have to reopen dataset:
ds.Close;
ds.Open;
After this all event handlers are gone:
if Assigned(ds.FieldByName('ID').OnChange) //returns false
So, I need to save handlers and restore them after reopen. I use TStringList for it:
var
EventHandlers: TStringList;
...
//I do this for every event of every field
if Assigned(ds.FieldByName('ID').OnChange) then
EventHandlers.AddObject('ID', #ds.FieldByName('ID').OnChange);
The problem is how to restore handlers:
ds.FieldByName('ID').OnChange := TFieldNotifyEvent(ObjToInt(EventHandlers.Objects[0]));//Invalid typecast error
How can I assign stored address to event handler?
Thanks.
If you really want to save the events, you can use TMethod Record:
unit Unit6;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm6 = class(TForm)
btn1: TButton;
btn2: TButton;
procedure btn1Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
MTD : TMethod;
end;
var
Form6: TForm6;
implementation
{$R *.dfm}
procedure TForm6.btn1Click(Sender: TObject);
begin
ShowMessage('Hello World!');
end;
procedure TForm6.btn2Click(Sender: TObject);
begin
ShowMessage('I am copy cat!');
MTD := TMethod(btn1.OnClick);
btn2.OnClick := TNotifyEvent(MTD);
end;
end.
The First Click on Btn2 will show "I am copy cat!" and The 2nd one will show Hello World.
Edit : Improve assign event to MTD(TMethod). More simple and allow events from other objects.
I myself subclass my own dataset and has options to create all fields before opening the table and mapping the field events. In doing so, the field (and their events) will not disappear after close.
This can also be done in OnBeforeOpen Event.
If CreateFIeldBeforeOpen
If FieldDefs.Count = 0 then
FieldDefs.Update;
for I := 0 to FieldDefs.Count - 1 do
If not Assigned(FindField(FieldDefs[I].Name)) then
FieldDefs[I].CreateField(Self, nil, FieldDefs[I].Name);

Resources