Create an on click event handler for an array of Radiobuttons - delphi

I have created an array of radiobuttons, which will be created in an event. I want to create an event, saying to make the radiobutton invisible when it is clicked and the show a message. But it has to happen on click. Can you help me?
This is how I created my radiobuttons
for k := 1 to 20 do
begin
rd[k] := TRadioButton.Create(Self);
rd[k].Parent := pgcVerkiesing;
rd[k].Caption := 'rs'+IntToStr(k);
rd[k].Left := 16;
if k = 1 then
rd[k].Top := 26
else
rd[k].Top := (k*24) ;
rd[k].OnClick := OnClick;
end;
Now I want to do something like this : rs1.clicked //procedure
rs1.disabled := true;
richedit1.lines.add := 'Name';
showmessage(names);
What to do?

If I understood correctly, you want to disable the clicked radiobutton.
Define an event for your radiobuttons:
procedure TForm1.OnRadioButtonClick(Sender : TObject);
When creating your radiobuttons, tie this event handler to the radiobuttons.
rd[k].OnClick := OnRadioButtonClick;
procedure TForm1.OnRadioButtonClick(Sender : TObject);
begin
TRadioButton(Sender).Enabled := false;
RichEdit1.Lines.Add( 'Name');
ShowMessage( names); // names not defined ??
end;

Related

How to make the same button run different code everytime it is clicked?

I am currently doing a school project, I am making a Credit Card machine. I need the 'Enter Button' to
run different code when it is clicked. The first click must get the card number from an edit ps... (I clear the edit once the card number has been retrieved), and the second click must get the pin from the same edit.
How would I do this?
procedure TfrmMainMenu.btbtnEnterClick(Sender: TObject);
var
sCvv,sPin:string;
begin
iCount2:=0;
sCardNumber:=lbledtCardInfo.Text;
if (Length(sCardNumber)<>16) AND (iCount2=0) then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
end
else
begin
Inc(iCount2);
lbledtCardInfo.clear;
lbledtCardInfo.EditLabel.Caption:='Enter Pin' ;
btbtnEnter.Enabled:=false;
end; //if
if iCount2=2 then
begin
btbtnEnter.Enabled:=true;
sPin:=lbledtCardInfo.Text;
ShowMessage(sPin);//returns a blank
end;
You could try to do everything in a single event handler. There are several different ways to handle that. However, a different solution would be to use separate event handlers for each task, and then each task can assign a new handler for the next click to perform, eg:
procedure TfrmMainMenu.FormCreate(Sender: TObject);
begin
// you can set this at design-time if desired...
btbtnEnter.OnClick := GetCCNumber;
end;
procedure TfrmMainMenu.GetCCNumber(Sender: TObject);
begin
sCardNumber := lbledtCardInfo.Text;
if Length(sCardNumber) <> 16 then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
Exit;
end;
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Pin' ;
btbtnEnter.OnClick := GetCCPin;
end;
procedure TfrmMainMenu.GetCCPin(Sender: TObject);
var
sPin: string;
begin
sPin := lbledtCardInfo.Text;
if Length(sPin) <> 4 then
begin
ShowMessage('Card Pin has to 4 digits,please try again!!');
Exit;
end;
ShowMessage(sPin);
...
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Number' ;
btbtnEnter.OnClick := GetCCNumber;
end;
A variation of this would be to create multiple buttons that overlap each other in the UI, and then you can toggle their Visible property back and forth as needed, eg:
procedure TfrmMainMenu.FormCreate(Sender: TObject);
begin
// you can set this at design-time if desired...
btbtnCCPinEnter.Visible := False;
btbtnCCNumEnter.Visible := True;
end;
procedure TfrmMainMenu.btbtnCCNumEnterClick(Sender: TObject);
begin
sCardNumber := lbledtCardInfo.Text;
if Length(sCardNumber) <> 16 then
begin
ShowMessage('Card number has to 16 digits,please try again!!');
Exit;
end;
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Pin' ;
btbtnCCNumEnter.Visible := False;
btbtnCCPinEnter.Visible := True;
end;
procedure TfrmMainMenu.btbtnCCPinEnterClick(Sender: TObject);
var
sPin: string;
begin
sPin := lbledtCardInfo.Text;
if Length(sPin) <> 4 then
begin
ShowMessage('Card Pin has to 4 digits,please try again!!');
Exit;
end;
ShowMessage(sPin);
...
lbledtCardInfo.Clear;
lbledtCardInfo.EditLabel.Caption := 'Enter Number' ;
btbtnCCPinEnter.Visible := False;
btbtnCCNumEnter.Visible := True;
end;
Notice that you test iCount2 = 0 immediately after setting iCount2 := 0. Thus, that test will always be True. Furthermore, the later test iCount2 = 2 will always be False because the value starts at 0 and you only have one Inc in between.
Instead try the following.
Add two string fields FCardNumber and FPin to your form class:
private
FCardNumber: string;
FPin: string;
Also create an enumerated type TEntryStage = (esCardNumber, esPin) and add a field of this type. This will make your code look like this:
private
type
TEntryStage = (esCardNumber, esPin);
var
FCardNumber: string;
FPin: string;
FEntryStage: TEntryStage;
In Delphi, class fields (class member variables) are always initialized, so FEntryStage will be esCardNumber (=TEntryStage(0)) when the form is newly created.
Add a TLabeledEdit (I see you use those) and a TButton; name them eInput and btnNext, respectively. Let the labeled edit's caption be Card number: and the caption of the button be Next.
Now add the following OnClick handler to the button:
procedure TForm1.btnNextClick(Sender: TObject);
begin
case FEntryStage of
esCardNumber:
begin
// Save card number
FCardNumber := eInput.Text;
// Prepare for the next stage
eInput.Clear;
eInput.EditLabel.Caption := 'Pin:';
FEntryStage := esPin;
end;
esPin:
begin
// Save pin
FPin := eInput.Text;
// Just do something with the data
ShowMessageFmt('Card number: %s'#13#10'Pin: %s', [FCardNumber, FPin]);
end;
end;
end;
You might notice that you cannot trigger the Next button using Enter, which is very annoying. To fix this, do
procedure TForm1.eInputEnter(Sender: TObject);
begin
btnNext.Default := True;
end;
procedure TForm1.eInputExit(Sender: TObject);
begin
btnNext.Default := False;
end;
Much better!

Delphi: Adding Combobox Dropdown to TADVStringGrid

I have a form that has a TADVStringGrid. I am trying to add a combobox to some rows in a specific column (2); however, I can't get the data to show on the dropdown. I added the HasComboBox event, I set the DirectComboDrop, and it still did not show any data in the dropdown. They were just empty. I check the object that I am adding to the dropdown, and they had data. What am I missing here?
procedure UserForm.DisplayGrid(Sender: TObject);
var
J : Integer;
begin
... additional logic
...
if OutputList.Count > 2 then
begin
with UserGrid.Combobox do
begin
for J := 0 to OutputList.Count - 1 do
BEGIN
if not(OutputList[J] = '') then
begin
dValue := DropDownValue.Create;
dValue.ID := J + 1;
dvalue.Name := OutputList[J];
dvalue.TestValue := OutputList[J] + 'testvalue'; // where value will be a list to choose from
ListOfTest.Add(dValue); // this is a glabal variable where I for later reference
ItemIndex := dValue.ID;
end;
END;
end;
end;
//event
procedure UserForm.UserGridHasComboBox(Sender: TObject; ACol, ARow: Integer;
var HasComboBox: Boolean);
begin
HasComboBox := True;
end;
There is an event handle called EditorProp that needed to be added. Data that need to be added for a specific column have to be added when the EditorProp event is called. The piece of code below was moved into the editorprop event, and it's working fine since.
for J := 0 to OutputList.Count - 1 do
BEGIN
if not(OutputList[J] = '') then
begin
dValue := DropDownValue.Create;
dValue.ID := J + 1;
dvalue.Name := OutputList[J];
dvalue.TestValue := OutputList[J] + 'testvalue'; // where value will be a list to choose from
ListOfTest.Add(dValue); // this is a glabal variable where I for later reference
ItemIndex := dValue.ID;
end;

How Add pickup list to DBGrid at run time?

i have a DBGrid and it is linked to client dataset when i assign a SQLQuery at run time
the DBGrid automatically assigns no of column. What i need is when DBGrid automatically assign columns i need to set one of those columns to assign a picklist.
can anyone help me?
the following procedure calls in the forms on show event. the form contains DataSource, ClientDataSet, SQLViewQuery (TSQLQuery), DatasetProvider and DBGridDetails (TDBGrid).
procedure TViewDetailsForm.ViewPendingAndReturnCheques;
var I : Integer;
slPickList:TStringList;
begin
slPickList := TStringList.Create;
slPickList.Add('Pending');
slPickList.Add('Returned');
slPickList.Add('Passed');
SQL := 'SELECT a.CHEQUE_NO, a.BANK, a.CHEQUE_DATE, a.AMOUNT,a.STATUS FROM CHEQUES a';
//refreshisng the DBGrid
SQLViewQuery.SQL.Clear;
SQLViewQuery.SQL.Add(SQL);
ClientDataSet.Active := false;
ClientDataSet.Active := true;
DBGridDetails.Columns[0].Width := _Block;
DBGridDetails.Columns[1].Width := _Block;
DBGridDetails.Columns[2].Width := _Block;
DBGridDetails.Columns[3].Width := _Block;
DBGridDetails.Columns[4].Width := _Block;
for I := 0 to DBGridDetails.Columns.Count - 1 do
begin
if DBGridDetails.Columns[I].FieldName = 'STATUS' then
begin
DBGridDetails.Columns[i].ButtonStyle := cbsAuto;
DBGridDetails.Columns[I].PickList := slPickList;
end;
end;
Show;
end;
Here's a sample app I just created in Delphi 2007 that demonstrates how to accomplish this. Here's all I did to set it up:
Click File->New-VCL Forms Application from the IDE's main menu.
Drop a TClientDataSet, a TDataSource, and a TDBGrid on the form.
Click on the form, and then use the Object Inspector to create a new OnCreate event handler. Add the following code:
procedure TForm1.FormCreate(Sender: TObject);
var
SL: TStringList;
begin
with ClientDataSet1 do
begin
FieldDefs.Clear;
FieldDefs.Add('OrderNo', ftInteger);
FieldDefs.Add('Status', ftString, 10);
CreateDataSet;
end;
ClientDataSet1.Active := True;
// Connect a datasource to the CDS
DataSource1.DataSet := ClientDataSet1;
// Connect the grid to that datasource to create the columns.
DBGrid1.DataSource := DataSource1;
// Create the picklist for the second column (Status)
SL := TStringList.Create;
try
SL.Add('Pending');
SL.Add('Returned');
SL.Add('Passed');
DBGrid1.Columns[1].ButtonStyle := cbsAuto;
DBGrid1.Columns[1].PickList := SL;
finally
SL.Free;
end;
end;
Run the application, click in the Status column in the grid, and you'll see the three choices added to the PickList above.
You can assign values to the dbgrid column picklist during the run time.
Below is the code:
procedure Tfrm1.FormShow(Sender: TObject);
var
slPickList:TStringList;
I: Integer;
begin
slPickList := TStringList.Create;
slPickList.Add('Pending');
slPickList.Add('Returned');
slPickList.Add('Passed');
for I := 0 to 2 do
begin
dbgViewAxiomClaims.Columns1.PickList.add(slPickList[i]);//assigning
end;
end;
Below is the result:

Access violation and Abstract error

I have a TButton event handler which either throws an Access Violation or Abstract Error when the method has completed execution. Delphi then highlights end. in my project source file. The method runs successfully, deleting the correct row from the database.
I have components which are created at runtime. Each row contains a label, an 'update' button and a 'delete' button, which have the database row number they are associated with in their names. The event handler throwing the errors is for delete.
procedure TFormInventoryMngmnt.ProductDeleteClick(Sender: TObject);
var
button : TButton;
row : integer;
confirm : integer;
begin
// Assuming it is button..how else can this be called?
button := Sender as TButton;
// Get row
row := StrToInt(StringReplace(button.Name, 'Delete', '', [rfReplaceAll, rfIgnoreCase]));
// Confirm
confirm := MessageDlg('Are you sure?', mtInformation, [mbYes, mbNo], 0);
if confirm = mrYes then
begin
// Delete row
UnitSession.Query.SQL.Clear;
UnitSession.Query.SQL.Add(Format(' DELETE FROM Products WHERE Product_ID = %s ', [IntToStr(row)]));
UnitSession.Query.ExecSQL;
buildManagementSection;
end;
// FIXME: why is this throwing access / abstract violation
end;
If you need more code or explanations just leave a comment and I'll get back to you.
Thanks!
Updated with code for buildManagementSection
I commented out the call to buildManagementSection from the listener and it did not throw any exceptions. This error could be caused from the method below, but I've never had a problem with it until now.
procedure TFormInventoryMngmnt.buildManagementSection;
var
index : integer;
runningHeight : integer;
productName : TLabel;
productUpdate : TButton;
productDelete : TButton;
const
MARGIN_TOP = 35;
// Left value of labels
LABEL_LEFT = 0;
// Left value of update button
UPDATE_LEFT = 235;
// Left value of delete button
DELETE_LEFT = 315;
begin
// Run sql query
UnitSession.Query.SQL.Clear;
UnitSession.Query.SQL.Add('SELECT Product, Product_ID FROM Products ORDER BY Product_ID');
UnitSession.Query.Active := true;
// Remove all components in the manage section
for index := (ScrollBoxManage.ComponentCount - 1) downto 0 do
begin
ScrollBoxManage.Components[index].Free;
end;
// No items
if UnitSession.Query.RecordCount = 0 then
begin
productName := TLabel.Create(ScrollBoxManage);
productName.Parent := ScrollBoxManage;
productName.Caption := 'No items!';
productName.Font.Color := clRed;
productName.Visible := true;
exit;
end;
// Build form
UnitSession.Query.First;
runningHeight := 0;
for index := 0 to (UnitSession.Query.RecordCount - 1) do
begin
// Create components
productName := TLabel.Create(ScrollBoxManage);
productUpdate := TButton.Create(ScrollBoxManage);
productDelete := TButton.Create(ScrollBoxManage);
// Set parents
productName.Parent := ScrollBoxManage;
productUpdate.Parent := ScrollBoxManage;
productDelete.Parent := ScrollBoxManage;
// Set values
productName.Caption := UnitSession.Query.Fields[0].AsString;
productUpdate.Caption := 'Update';
productDelete.Caption := 'Delete';
// Set event handlers
productUpdate.OnClick := FormInventoryMngmnt.ProductUpdateClick;
productDelete.OnClick := FormInventoryMngmnt.ProductDeleteClick;
// Set top position
productName.Top := runningHeight + 3;
productUpdate.Top := runningHeight;
productDelete.Top := runningHeight;
// Set button association
productName.Name := 'Label' + UnitSession.Query.Fields[1].AsString;
productUpdate.Name := 'Update' + UnitSession.Query.Fields[1].AsString;
productDelete.Name := 'Delete' + UnitSession.Query.Fields[1].AsString;
// Set left position
productName.Left := LABEL_LEFT;
productUpdate.Left := UPDATE_LEFT;
productDelete.Left := DELETE_LEFT;
// Set as visible
productName.Visible := true;
productUpdate.Visible := true;
productDelete.Visible := true;
runningHeight := runningHeight + MARGIN_TOP;
UnitSession.Query.Next;
end;
end;
The button instance hasn't been created!
There is no need to say
button := Sender as Tbutton
just reference the original button in the code since it is in scope on the form
row := StrToInt(StringReplace(ProductDelete.Name, 'Delete', '', [rfReplaceAll, rfIgnoreCase]));
or do this
with TButton(sender) do
row := StrToInt(StringReplace(Name, 'Delete', '', [rfReplaceAll, rfIgnoreCase]));
Or if you really want to do it this way, with a variable holding the button instance, then
you must create it first
begin
button := TButton.Create(Self);
try
button := TButton(Sender)
// your code
finally
button.Free
end
end

Forms creation and destroying in OnMouseEnter ; OnMouseLeave events in Delphi

Sorry if there is already made such question earlier, but I have no time at the moment to dig in stackoverflow db ...
So, I have this code:
procedure TForm1.GraphPrevBtnMouseEnter(Sender: TObject);
var frm_PrevBtn : TForm;
begin
GraphPrevBtn.Width := 75;
if z = 0 then begin
frm_PrevBtn := TForm.Create(nil);
with frm_PrevBtn do begin
Name := 'frm_PrevBtn';
BorderStyle := bsNone;
Position := poDesigned;
Top := Form1.Top + GraphprevBtn.Top + (form1.Height - Form1.ClientHeight) - 3;
Left := Form1.Left + GraphprevBtn.Left + 3;
Width := GraphprevBtn.Width; Height := GraphprevBtn.Height; transparentColor := True; TransparentColorValue := clbtnFace;
Show;
end;
GraphPrevBtn.Parent := frm_PrevBtn;
if GetLastError = 0 then z := frm_prevBtn.GetHashCode;
end;
end;
procedure TForm1.GraphPrevBtnMouseLeave(Sender: TObject);
var frm_PrevBtn_H : THandle;
begin
// if form is created then- if mouse is under button then- if z = formshashcode ( form is on creatin stage )
if not (FindVCLWindow(Mouse.CursorPos) = GraphPrevBtn) and ((FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm).Visible = True) and (GraphPrevBtn.Parent = FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm) then begin // if mouse is not under graphprevBtn
ShowMessage(FindVCLWindow(Mouse.CursorPos).Name); //
if z = 112 // then if form is created
then begin
GraphPrevBtn.Parent := Form1;
GraphPrevBtn.bringtoFront;
GraphPrevBtn.Top := 29; GraphPrevBtn.Left := 226;
(FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm).Free;
if GetLastError = 0 then z := 0;
end;
end;
end;
So, my wish is the following:
When I enter this GraphPrevBtn with mouse, form is created. As for is created, the focus goes from Control to new form. As focus is to new form, the OnMouseLeave event is fired. As event is fired, it should destroy the form, BUT ONLY IF user ( NOT active control / focus ) actually leaves control by mouse.
What happens now is that either new forms is not destroyed at all or both events goes infinite loop ( *frm_PrevBtn* is created and destroyed again and again and again...).
What would be best solution?
My idea is to get new forms rect and check whenever mouse is inside this rect. If it is, then perform allow OnMouseLeave event, otherwise deattach it ... would it work?
As much I tried with these samples:
http://delphi.about.com/od/windowsshellapi/a/get-active-ctrl.htm
http://delphi.about.com/od/delphitips2010/qt/is-some-delphi-tcontrol-under-the-mouse.htm
No luck. Where is the problem ... ?
Remarks: global var z : byte;
P.S. Thanks for negative votes ... great motivation to use this site in future ...
Mouse enters on 'GraphPrevBtn', you create a form over the button. As soon as this form becomes visible, since mouse is not anymore over 'GraphPrevBtn', 'OnMouseLeave' is fired. You destroy the new form and now mouse is again on the button so 'OnMouseEnter' is fired, hence the infinite loop.
As a solution, you can move the form disposing code to 'OnMouseEnter' of Form1:
procedure TForm1.FormMouseEnter(Sender: TObject);
begin
if z = 112
then begin
GraphPrevBtn.Parent := Form1;
[...]
.. and what's with the 'GetLastError', it seems fully irrelevant. If you're going to use it, at least set last error to '0' by calling GetLastError or SetLastErrorbefore beginning your operation.
Maybe something more like this will help you:
var
frm_PrevBtn : TForm = nil;
procedure TForm1.GraphPrevBtnMouseEnter(Sender: TObject);
var
P: TPoint;
begin
GraphPrevBtn.Width := 75;
if frm_PrevBtn = nil then begin
P := GraphPrevBtn.ClientOrigin;
frm_PrevBtn := TForm.Create(nil);
with frm_PrevBtn do begin
BorderStyle := bsNone;
Position := poDesigned;
SetBounds(P.X, P.Y, GraphPrevBtn.Width, GraphPrevBtn.Height);
TransparentColor := True;
TransparentColorValue := clBtnFace;
GraphPrevBtn.Parent := frm_PrevBtn;
GraphPrevBtn.Top := 0;
GraphPrevBtn.Left := 0;
Show;
end;
end;
end;
procedure TForm1.GraphPrevBtnMouseLeave(Sender: TObject);
begin
if (FindVCLWindow(Mouse.CursorPos) <> GraphPrevBtn) and (frm_PrevBtn <> nil) then begin
GraphPrevBtn.Parent := Self;
GraphPrevBtn.BringToFront;
GraphPrevBtn.Top := 29;
GraphPrevBtn.Left := 226;
FreeAndNil(frm_PrevBtn);
end;
end;
Why don't you do it like this:
MainForm.OnMouseOver: Create a secondary form.
SecondaryForm.OnMouseOver: Set FLAG_ON_SECONDARY.
SecondaryForm.OnMouseLeave: Clear FLAG_ON_SECONDARY.
MainForm.OnMouseLeave: if not FLAG_ON_SECONDARY then destroy the secondary form.
This might not work in case SecondaryForm.OnMouseOver fires after MainForm.OnMouseLeave. Well, think of something similar. Another solution is to start a timer which destroys SecondaryForm and disables itself if mouse is neither on Main nor on SecondaryForm.

Resources