What I Want to do:
I create on the run a panel, 2 editfield, a radiobox.
When I Click on the RadioButton Edit1 I want to have the Edit1 field visible, and Edit2 filed Not visible
and reverse by the click on the RadioButton Edit2.
But it doesn't work I probably misunderstand something.
Do you have any idea?
procedure CreationPanel(L,T,H,W:integer;Nom,Titre:string
{MyPanel:TPanel});
begin
MyPanel:=TPanel.Create(Form1);
with MyPanel do
begin
Height:=H;
Left:=L;
Width:=W;
Top:=T;
BorderStyle:=bsSingle;
BevelWidth:=3;
BevelOuter:=bvRaised;
Parent:=Form1;
Visible:=True;
TabOrder:=-1;
TabStop:=False;
Tag:=100;
Name:=Nom;
Caption:=Nom;
end;
end;
procedure Creation_Edit(L,T,FontSize:integer;
NomComp,NomPanel:String;
Bool:Boolean
{MyPanel:Tpanel});
begin
MyEdit:=TEdit.Create(MyPanel);
with MyEdit do
begin
Parent:=MyPanel;
{Relation Table Dossier avec Table Bezeichnung}
Height :=21 ;
Left :=L ;
Top :=T ;
Width :=100;
Name:=NomComp;
Font.Name:='MS Sans Serif';
Font.Size:=FontSize;
Visible:=Bool;
end;
end;
procedure Creation_RadioGroup(L,T,H,W,FontSize:integer;
RGName,NomPanel:string
{MyPanelRG:TPanel});
begin
MyRadioGroup:=TRadioGroup.Create(MyPanel);
with MyRadioGroup do
begin
Parent:=MyPanel;
Height :=H ;
Left :=L ;
Top :=T ;
Width :=W;
Font.Name:='MS Sans Serif';
Name:=RGName;
Font.Size:=FontSize;
Items.Add('Edit1);
Items.Add('Edit2');
ItemIndex:=0;
OnClick:=Form1.RadioGroupClick;
end;
end;
procedure TForm1.RadioGroupClick(Sender: TObject);
var
E1,E2:TEdit;
begin {1}
E1:=TEdit(FindComponent('Edit1'));
E2:=TEdit(FindComponent('Edit2'));
E1:=TEdit.Create(MyPanel);
E2:=TEdit.Create(MyPanel);
if E1=nil then showmessage('E1=Nil');
if E2=nil then showmessage('E2=Nil');
If MyRadioGroup.Name='RD1' then {with TEDIT}
begin {If}
If Assigned(E1) and Assigned(E2) then
begin {2}
Case MyRadioGroup.ItemIndex of
0: begin
E1.Visible:=true;
E2.Visible:=False;
end;
1:begin
E1.Visible:=False;
E2.Visible:=true;
end;
end;{Case}
end;{2}
end;{If}
end;{1}
procedure TForm1.FormCreate(Sender: TObject);
begin
{Pannel Left,Top,Hieght,Width}
CreationPanel(80,100,300,180,'Panel1','Panel 1');
Creation_Edit(30,184,10,'Edit1','Panel1',true);
Creation_Edit(30,234,10,'Edit2','Panel1',false);
{RadioGroup Left,Top,Height,Width,FontSize,Tab}
Creation_RadioGroup(30,12,90,120,12,'RD1','Panel1');
end;
end.
OK your code is a complete mess, but I can tell you what your underlying problem is (i.e. the one that caused you to ask the question.)
Firstly Nasreddine is right about removing
E1:=TEdit.Create(MyPanel);
E2:=TEdit.Create(MyPanel);
That is a complete floundering (try anything) attempt because E1 and E2 return nil.
You should not be trying to stop them being nil. Instead you should ask yourself why they are nil.
And the answer to that is that Edit1 and Edit2 are not owned by Form1. They are owned by MyPanel.
MyEdit:=TEdit.Create(MyPanel);
So your code should read
procedure TForm1.RadioGroupClick(Sender: TObject);
var
E1,E2:TEdit;
begin {1}
E1:=TEdit( MyPanel.FindComponent('Edit1'));
E2:=TEdit(MyPanel.FindComponent('Edit2'));
if E1=nil then showmessage('E1=Nil');
if E2=nil then showmessage('E2=Nil');
//etc...
end;{1}
Related
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!
i want to ask how to retain controlls when im making a copy of a control. for example i have an edit box that can be controlled with a slider for value change. when i make a copy using this code i achieve a copy of the items but the slider stops controlling editbox values. how can i fix that?
TypInfo;
procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
ms: TMemoryStream;
OldName: string;
begin
OldName := Source.Name;
Source.Name := ''; // needed to avoid Name collision
try
ms := TMemoryStream.Create;
try
ms.WriteComponent(Source);
ms.Position := 0;
ms.ReadComponent(Dest);
finally
ms.Free;
end;
finally
Source.Name := OldName;
end;
end;
procedure CloneEvents(Source, Dest: TControl);
var
I: Integer;
PropList: TPropList;
begin
for I := 0 to GetPropList(Source.ClassInfo, [tkMethod], #PropList) - 1 do
SetMethodProp(Dest, PropList[I], GetMethodProp(Source, PropList[I]));
end;
procedure DuplicateChildren(const ParentSource: TWinControl;
const WithEvents: Boolean = True);
var
I: Integer;
CurrentControl, ClonedControl: TControl;
begin
for I := ParentSource.ControlCount - 1 downto 0 do
begin
CurrentControl := ParentSource.Controls[I];
ClonedControl := TControlClass(CurrentControl.ClassType).Create(CurrentControl.Owner);
ClonedControl.Parent := ParentSource;
CloneProperties(CurrentControl, ClonedControl);
ClonedControl.Name := CurrentControl.Name + '_';
if WithEvents then
CloneEvents(CurrentControl, ClonedControl);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DuplicateChildren(Panel1);
end;
Unless I'm misunderstanding you, your CloneProperties doesn't seem to have anything to do with the question you're asking. In your example of an edit control E1 and a slider S1, you can clone both of them to produce E2 and S2, but somewhere in your code there must be a statement that changes the value in E1 depending on the value of S1. However, in the way you've most likely written it, that statement doesn't apply to E2 and S2.
The simplest way around that is to write a method which takes the component instances and links the operation of the two together. e.g.
procedure TForm1.SetEditControlFromSlider(AnEdit : TEdit; ASlider : { TWhatever the slider actually is);
begin
// Set AnEdit's value from ASlider's properties
end;
Then, you can call this with Edit/Slider pairs like this
SetEditControlFromSlider(E1, S1);
[...]
SetEditControlFromSlider(E2, S2);
I can imagine you might not like having to do that.
IMO, the cleanest solution is to avoid attempting to clone components altogether and create a TFrame containing the Edit, Slider and the code that connects them, and then add to your form as many instances of the frame as you need. It's as easy as falling off a log.
type
TEditFrame = class(TFrame) // needs to be in its own unit, Used by your form
Edit1: TEdit;
TrackBar1: TTrackBar;
procedure TrackBar1Change(Sender: TObject);
private
public
end;
[...]
procedure TEditFrame.TrackBar1Change(Sender: TObject);
begin
Edit1.Text := IntToStr(TrackBar1.Position)
end;
Then, you can add clones of the frame to TForm1 by
procedure TForm1.Button1Click(Sender: TObject);
var
AFrame : TEditFrame;
begin
Inc(FrameCount); // Field of TForm1
AFrame := TEditFrame.Create(Self);
AFrame.Name := AFrame.Name + IntToStr(FrameCount);
AFrame.Parent := Self;
AFrame.Top := AFrame.Height * FrameCount;
end;
Note that because the code which links the two components, TrackBar1Change, it compiled into the frame's unit, it is automatically shared by every instance of the frame you create, without any need to "clone" the code.
i am trying to add items to TListBox and TComboBox from text given in TEdit
My code is working fine while adding item in TListBox TComboBox but when i try to remove the selected item in TListBox from itself and from TComobBox it shows Access Violation.
Below is procedure from my code:-
procedure TMainForm.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add(Edit1.Text);
ComboBox1.Items.Add(Edit1.Text);
end;
procedure TMainForm.Button2Click(Sender: TObject);
begin
ListBox1.Items.Delete(ListBox1.Selected.Index);
ComboBox1.Items.Delete(ComboBox1.Items.IndexOf(ListBox1.Selected.Text));
end;
Solved: Did a Kiddish Mistake now Solved. Here it is working code:
procedure TMainForm.Button2Click(Sender: TObject);
begin
ComboBox1.Items.Delete(ComboBox1.Items.IndexOf(ListBox1.Selected.Text));
ListBox1.Items.Delete(ListBox1.Selected.Index);
end;
A safe(r) way to do the deletions is
procedure TForm1.DeleteItems(const TextToFind : String);
var
i1,
i2 : Integer;
begin
i1 := ListBox1.Items.IndexOf(TextToFind);
i2 := ComboBox1.Items.IndexOf(TextToFind);
if i1 >=0 then
ListBox1.Items.Delete(i1);
if i2 >=0 then
ComboBox1.Items.Delete(i2);
end;
Usage:
DeleteItems(Edit1.Text);
because this does not make assumptions about which items are selected in the two lists.
I leave you to find out using the debugger why you are getting the AV. It will be more instructive for you to find out than me to tell you.
This line removes the item from the listbox
ListBox1.Items.Delete(ListBox1.Selected.Index);
This line is trying to remove the item from the combobox
ComboBox1.Items.Delete(ComboBox1.Items.IndexOf(ListBox1.Selected.Text));
But in it you refer to ListBox1.Selected.Text.
This is referring the item you just removed in the first delete.
Swapping the order of execution around should work:
begin
ComboBox1.Items.Delete(ComboBox1.Items.IndexOf(ListBox1.Selected.Text));
ListBox1.Items.Delete(ListBox1.Selected.Index);
end
procedure TMainForm.Button2Click(Sender: TObject);
begin
if ListBox1.Selected.Index > -1 then ListBox1.Items.Delete(ListBox1.Selected.Index);
if ComboBox1.ItemIndex > - 1 then ComboBox1.Items.Delete(ComboBox1.ItemIndex);
end;
I am hiding a field so that when it is shown (on checkbox checked) it preforms a certain calculation.
procedure TForm1.cxCheckBox1Click(Sender: TObject);
var
C:TcxGridDBColumn;
begin
if ABSTable1.FieldByName('CENIK_IME').AsString = 'PAK' then begin
C := cxGrid2dbtableview1.GetColumnByFieldName('TT');
if Assigned(C) then C.Visible := not C.Visible;
ABSQuery2.Edit;
ABSQuery2.FieldByName('TOTAL').AsCurrency := (ABSQuery2.FieldByName('TOTAL').AsCurrency) + (ABSQuery2.FieldByName('TT').AsCurrency);
ABSQuery2.Refresh;
end;
end;
Problem is that every time I check or uncheck the checkbox my TOTAL gets bigger and bigger. Any way to prevent the checkbox from summing every time it gets checked or unchecked ?
Also I have this on calculate fields of the query ;
procedure TForm1.ABSQuery2CalcFields(DataSet: TDataSet);
begin
ABSQuery2.FieldByName('TT').Value:= (ABSQuery2.FieldByName('DAYS').AsCurrency) * 1.01 ;
end;
This is all done on a Temp table which is used just for the occassion. Contents get deleted all the time....
Won't you need to subtract ABSQuery2.FieldByName('TT').AsCurrency if the column is hidden?
And also only change Total if the TT is found?
So:
procedure TForm1.cxCheckBox1Click(Sender: TObject);
var
C:TcxGridDBColumn;
begin
if ABSTable1.FieldByName('CENIK_IME').AsString = 'PAK' then begin
C := cxGrid2dbtableview1.GetColumnByFieldName('TT');
if Assigned(C) then
begin
C.Visible := not C.Visible;
ABSQuery2.Edit;
if C.Visible then
ABSQuery2.FieldByName('TOTAL').AsCurrency := (ABSQuery2.FieldByName('TOTAL').AsCurrency) + (ABSQuery2.FieldByName('TT').AsCurrency)
else
ABSQuery2.FieldByName('TOTAL').AsCurrency := (ABSQuery2.FieldByName('TOTAL').AsCurrency) - (ABSQuery2.FieldByName('TT').AsCurrency);
ABSQuery2.Post;
ABSQuery2.Refresh;
end;
end;
end;
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have a button and when clicked, i would like for a TMachine (aka TShape) to show up on the form. Currenty i get no errors, but it never shows up on the form.
Code for button click
procedure TfDeptLayout.bAddMachineClick(Sender: TObject);
var
machine: TMachine;
shapeAsset,
shapeShape,
shapeNumber,
shapeName: string;
begin
if not OkToAdd() then
begin
ShowMessage('Please fill out form correctly!');
Exit;
end;
ShapeAsset := Edit2.text;
ShapeShape := Combobox1.Text;
ShapeNumber := Edit3.Text;
ShapeName := Edit1.Text;
if sub = false then
begin
machine := TMachine.Create(self);
machine.Parent := Self;
machine.PlaceShape(0, FDB.GetWW(ShapeShape), FDB.GethW(ShapeShape),
'20', '20', ShapeName, ShapeNumber, ShapeAsset)
//show save button
//lockout add machine button
//let user place machine top / left.
//save all locations
//save top and left for each tmachine to database
//lockout save button
//show add machine button
end;
if sub then
ShowMessage('auto save form');
ShowMessage('congrats you added a machine');
end;
if needed i can show the TMachine unit?..
type
TMachine = class(TShape)
private
FOnMouseEnter: TNotifyEvent;
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
protected
procedure DoMouseEnter; virtual;
published
property OnMouseEnter: TNotifyEvent Read FOnMouseEnter write FOnMouseEnter;
public
mnName: string;
mnAsset: string;
mnNumber: string;
mnIsPanel: string;
mnBasicName: string;
mnLShape: string;
procedure PlaceShape(AM, sizeW, sizeH: Integer; ptop, pleft, name, order,
asset: string);
end;
implementation
uses
deptlayout;
procedure TMachine.CMMouseEnter(var Message: TMessage);
begin
DoMouseEnter;
inherited;
end;
procedure TMachine.DoMouseEnter;
begin
if Assigned(FOnMouseEnter) then
FOnMouseEnter(Self);
end;
procedure TMachine.PlaceShape(AM, sizeW, sizeH: Integer; ptop, pleft, name,
order, asset: string);
var
myLabel: TLabel;
begin
if ptop = '0' then
Top := 136
else
Top := StrToInt(ptop);
Width := sizeW;
Height := sizeH;
if pleft = '0' then
Left := MyDataModule.fDB.LastX + 2 //set left
else
Left := StrToInt(pleft);
MyDataModule.fDB.lastx := Left + sizeW;
if AM = 1 then //if in edit mode..
begin
//create label put inside the shape.
myLabel := TLabel.Create(FDeptLayout);
mylabel.Parent := FDeptLayout;
mylabel.Left := Left;
mylabel.Top := Top + 8;
mylabel.Caption := '#' + mnNumber;
end;
end;
end.
Of course it doesn't work!
The code that adds the machine is inside if not OkToAdd() then, so it will only run if not OkToAdd. BUT! Even if this is the case, you Exit before you run the code! Hence, the code will never run!
Probably you mean it to be like this:
if not OkToAdd then
begin
ShowMessage('Please fill out form correctly!');
Exit;
end; //END!!!!!!
To summarise my comments above:
Change the refer to fDeptLayout to Self, as you have done in your edit:
procedure TfDeptLayout.bAddMachineClick(Sender: TObject);
var
machine : TMachine;
shapeAsset,
shapeShape,
shapeNumber,
shapeName : string;
begin
if not OkToAdd() then
begin
showmessage('Please fill out form correctly!');
Exit;
End;
shapeAsset := edit2.text;
ShapeShape := Combobox1.Text;
ShapeNumber := Edit3.Text;
ShapeName := Edit1.Text;
if sub = false then
begin
machine := TMachine.Create(self);
machine.Parent := Self;
machine.PlaceShape(0,FDB.GetWW(ShapeShape),FDB.GethW(ShapeShape),'20','20',ShapeName,ShapeNumber,ShapeAsset)
//show save button
//lockout add machine button
//let user place machine top / left.
//save all locations
//save top and left for each tmachine to database
//lockout save button
//show add machine button
end;
if sub then
showmessage('auto save form');
showmessage('congrats you added a machine');
end;
To avoid confusion in future, delete the global form variables that the Delphi IDE creates for all but the main form, and any other autocreated forms - they are rarely if ever needed, and "pollute the namespace"
Unknown why this solved it, but after trying to find the parent for Machine by putting
showmessage('Machine Parent: '+Machine.parent.name);
it was giving access errors.
Deleted
Machine.parent := self;
Compile, build. Then reaadded
Machine.parent := self;
and everything worked.