kvisthor
kvisthor

Reputation: 146

Cannot change TEdit Text in Delphi

I am adding components to a form at run time and I am also adding events that change properties of these components in a dictionary to call them later.

TEventBeforeInsert = function(var AComponent: TComponent; AForm: TForm): Boolean of Object;

TFieldBase = class
private
  FEvent:TEventBeforeInsert;
....
function TFieldBase.EventBeforeInsert: TEventBeforeInsert;
begin
  Result:=FEvent;
end;

function TFieldBase.EventBeforeInsert(AEvent: TEventBeforeInsert): TFieldBase ;
begin
  FEvent:=AEvent;
  Result:=Self;
end;

....

The Form Call

TFormBase.New
.addStringField
(
    TFieldBase.New
    .Enabled(True)
    .Description('User')
    .EventBeforeInsert(TEvents.New.EditFillUser), TTabsNames.Tab1
).Show();

The Form Class

TFormBase = class(TForm)
private
FDictionary: TDictionary<String, TEventBeforeInsert>;
...
function TFormBase.addStringField(AField: TFieldBase; ATab: TTabsNames): TFormBase;
var
  FLink: TLinkControlToField;
  FEdit: TEdit;
begin
  Result := Self;
  FEdit := TEdit.Create(Self);
  FEdit.Visible := True;
  FEdit.Parent := TPanel(PanelParent.FindComponent('PanelTab' + Ord(ATab).ToString));
  FEdit.Enabled:=AField.Enabled;

 
  if Assigned(AField.EventBeforeInsert) then
  begin
    FDictionary.Add(FEdit.Name,AField.EventBeforeInsert);
  end;
end;
...
procedure TFormBase.rectInsertClick(Sender: TObject);
var
    Item:String;
begin
  for Item in FDictionary.Keys do
  begin
    if Not FDictionary.Items[Item](Self.FindComponent(Item),Self) then
      Exit;
  end;
end;

I'm having a problem here, when debugging I see the text property being changed correctly, but no changes are made to the form being displayed.

TEvents = class
...
function TEvents.EditFillUser(AComponent: TComponent;AForm: TForm): Boolean;
begin
  TEdit(AComponent).Text:=IntToStr(0);
  Result:=True;
end

I'm thinking it may be a problem that the variable is being passed by value ... Can someone help me?

Edit 1: I've tried with the dictionary declared like this:

FDictionary: TDictionary<TComponent, TEventBeforeInsert>;
...
if Not FDictionary.Items[Item](Item,Self) then //call

And I also tried use TForm reference like this:

function TEvents.EditFillUser(AComponent: String;AForm: TForm): Boolean;
begin
  TEdit(AForm.FindComponent(AComponent)).Text:=IntToStr(0);
  Result:=True;
end

Upvotes: 2

Views: 938

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597941

In TFormBase.addStringField(), you are not assigning a Name value to the newly create TEdit object before inserting it into FDictionary.. Only components created at design-time have auto-generated Names. Components created at run-time do not. So, you are tracking your objects using blank Names. If you want to track the objects by Name, you need to actually assign your own value to FEdit.Name, eg:

function TFormBase.addStringField(AField: TFieldBase; ATab: TTabsNames): TFormBase;
var
  ...
  FEdit: TEdit;
  FEvent: TEventBeforeInsert;
begin
  ...
  FEdit := TEdit.Create(Self);
  FEdit.Name := 'SomeUniqueNameHere'; // <-- for you to decide on...
  ...
 
  FEvent := AField.EventBeforeInsert;
  if Assigned(FEvent) then
    FDictionary.Add(FEdit.Name, FEvent);
end;

However, in this particular case, I see no reason to use a TDictionary at all. Consider using a TList instead, then you don't need the Names at all. This will also boost the performance of the iteration in TFormBase.rectInsertClick() since it won't have to hunt for every TComponent object using FindComponent() anymore:

TFormBase = class(TForm)
private
  type TEventBeforeInsertPair = TPair<TComponent, TEventBeforeInsert>;
  FBeforeInsertEvents: TList<TEventBeforeInsertPair>;
  ...
public
  constructor Create;
  destructor Destroy; override;
  ...
end;

...

constructor TFormBase.Create;
begin
  inherited;
  FBeforeInsertEvents := TList<TEventBeforeInsertPair>.Create;
end;

destructor TFormBase.Destroy;
begin
  FBeforeInsertEvents.Free;
  inherited;
end;

function TFormBase.addStringField(AField: TFieldBase; ATab: TTabsNames): TFormBase;
var
  ...
  FEdit: TEdit;
  FEvent: TEventBeforeInsert;
begin
  ...
  FEdit := TEdit.Create(Self);
  ...

  FEvent := AField.EventBeforeInsert;
  if Assigned(FEvent) then
    FBeforeInsertEvents.Add(TEventBeforeInsertPair.Create(FEdit, FEvent));
end;

procedure TFormBase.rectInsertClick(Sender: TObject);
var
  Item: TEventBeforeInsertPair;
begin
  for Item in FBeforeInsertEvents do
  begin
    if not Item.Value(Item.Key, Self) then
      Exit;
  end;
end;

...

Also, your TEvents.EditFillUser() method does not match the definition of TEventBeforeInsert. The 1st parameter of TEventBeforeInsert is declared as passing the TComponent pointer by var reference (why?), but the 1st parameter of EditFillUser() is not doing that. Unless you want your event handlers to alter what the TComponent pointers are pointing at (which won't work the way you are currently using TEventBeforeInsert with TDictionary), then there is no reason to pass around the TComponent pointers by var reference at all:

TEventBeforeInsert = function(AComponent: TComponent; AForm: TForm): Boolean of Object;

Also, your use of TEvents.New appears to be a memory leak, as nobody is taking ownership of the newly created TEvents object (unless its constructor is adding the object to some internal list that we can't see). Same with TFieldBase.New. And even TFormBase.New (assuming there is no OnClose event that sets Action=caFree when the Form is closed). At some point, you need to call Free() any class object that you Create().

Upvotes: 5

Related Questions