Search code examples
delphicustom-componentlazarus

OnClick event handler for control in custom component not working (Lazarus)


Using: Lazarus 1.2.0; Windows 32-bit application

I have written a custom component derived from TPanel and it conains 4 TEdit controls. I've written the OnClick event handler code for the TEdits. However its not working at runtime ie the events are not firing. I'm not sure what I missed. Please can you tell me what I'm doing wrong?

Component code follows:

unit uEditPanel;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;

type

  { TEditPanel }

  TEditPanel = class(TCustomPanel)
    Edit0: TEdit;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;

    procedure SetEdit1OnClick(const AEvent: TNotifyEvent);
    procedure SetEdit2OnClick(const AEvent: TNotifyEvent);
    procedure SetEdit3OnClick(const AEvent: TNotifyEvent);
    procedure SetEdit4OnClick(const AEvent: TNotifyEvent);

  private
    { Private declarations }
    FEdit1OnClick: TNotifyEvent;
    FEdit2OnClick: TNotifyEvent;
    FEdit3OnClick: TNotifyEvent;
    FEdit4OnClick: TNotifyEvent;

    function GetEdit0Text: string;
    procedure SetEdit0Text(AText: string);
    function GetEdit1Text: string;
    procedure SetEdit1Text(AText: string);
    function GetEdit2Text: string;
    procedure SetEdit2Text(AText: string);
    function GetEdit3Text: string;
    procedure SetEdit3Text(AText: string);
    function GetEdit4Text: string;
    procedure SetEdit4Text(AText: string);

  protected
    { Protected declarations }
    procedure DoEdit1Click;
    procedure DoEdit2Click;
    procedure DoEdit3Click;
    procedure DoEdit4Click;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    property Edit0Text: string read GetEdit0Text write SetEdit0Text;
    property Edit1Text: string read GetEdit1Text write SetEdit1Text;
    property Edit2Text: string read GetEdit2Text write SetEdit2Text;
    property Edit3Text: string read GetEdit3Text write SetEdit3Text;
    property Edit4Text: string read GetEdit4Text write SetEdit4Text;

    property OnEdit1Click: TNotifyEvent read FEdit1OnClick write SetEdit1OnClick;
    property OnEdit2Click: TNotifyEvent read FEdit2OnClick write SetEdit2OnClick;
    property OnEdit3Click: TNotifyEvent read FEdit3OnClick write SetEdit3OnClick;
    property OnEdit4Click: TNotifyEvent read FEdit4OnClick write SetEdit4OnClick;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Standard', [TEditPanel]);
end;

{ TEditPanel }

procedure TEditPanel.SetEdit1OnClick(const AEvent: TNotifyEvent);
begin
  FEdit1OnClick := AEvent;
end;

procedure TEditPanel.SetEdit2OnClick(const AEvent: TNotifyEvent);
begin
  FEdit2OnClick := AEvent;
end;


procedure TEditPanel.SetEdit3OnClick(const AEvent: TNotifyEvent);
begin
  FEdit3OnClick := AEvent;
end;


procedure TEditPanel.SetEdit4OnClick(const AEvent: TNotifyEvent);
begin
  FEdit4OnClick := AEvent;
end;

function TEditPanel.GetEdit0Text: string;
begin
  Result := Edit0.Text;
end;

procedure TEditPanel.SetEdit0Text(AText: string);
begin
  Edit0.Text := AText;
end;

function TEditPanel.GetEdit1Text: string;
begin
  Result := Edit1.Text;
end;

procedure TEditPanel.SetEdit1Text(AText: string);
begin
  Edit1.Text := AText;
end;

function TEditPanel.GetEdit2Text: string;
begin
  Result := Edit2.Text;
end;

procedure TEditPanel.SetEdit2Text(AText: string);
begin
  Edit2.Text := AText;
end;

function TEditPanel.GetEdit3Text: string;
begin
  Result := Edit3.Text;
end;

procedure TEditPanel.SetEdit3Text(AText: string);
begin
  Edit3.Text := AText;
end;

function TEditPanel.GetEdit4Text: string;
begin
  Result := Edit4.Text;
end;

procedure TEditPanel.SetEdit4Text(AText: string);
begin
  Edit4.Text := AText;
end;

procedure TEditPanel.DoEdit1Click;
begin
  if Assigned(FEdit1OnClick) then
    FEdit1OnClick(Self);
end;

procedure TEditPanel.DoEdit2Click;
begin
  if Assigned(FEdit2OnClick) then
    FEdit2OnClick(Self);
end;

procedure TEditPanel.DoEdit3Click;
begin
  if Assigned(FEdit3OnClick) then
    FEdit3OnClick(Self);
end;

procedure TEditPanel.DoEdit4Click;
begin
  if Assigned(FEdit4OnClick) then
    FEdit4OnClick(Self);
end;

constructor TEditPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Edit0 := TEdit.Create(Self);
  Edit1 := TEdit.Create(Self);
  Edit2 := TEdit.Create(Self);
  Edit3 := TEdit.Create(Self);
  Edit4 := TEdit.Create(Self);

  Edit0.Parent := Self;
  Edit1.Parent := Self;
  Edit2.Parent := Self;
  Edit3.Parent := Self;
  Edit4.Parent := Self;

  Edit0.SetSubComponent(True);
  Edit1.SetSubComponent(True);
  Edit2.SetSubComponent(True);
  Edit3.SetSubComponent(True);
  Edit4.SetSubComponent(True);

  Edit1.ReadOnly := True;
  Edit2.ReadOnly := True;
  Edit3.ReadOnly := True;
  Edit4.ReadOnly := True;

  Edit1.OnClick := FEdit1OnClick;
  Edit2.OnClick := FEdit2OnClick;
  Edit3.OnClick := FEdit3OnClick;
  Edit4.OnClick := FEdit4OnClick;

  Caption := EmptyStr;
  Height := 117;
  Width := 289;
  BevelOuter := bvNone;
  ClientHeight := 117;
  ClientWidth := 289;

  Edit0.Left := 0;
  Edit0.Height := 21;
  Edit0.Top := 0;
  Edit0.Width := 288;
  Edit0.BorderStyle := bsNone;
  Edit0.TabOrder := 0;

  Edit1.Left := 0;
  Edit1.Height := 21;
  Edit1.Top := 24;
  Edit1.Width := 288;
  Edit1.BorderStyle := bsNone;
  Edit1.TabOrder := 1;
  Edit1.Font.Color := clGray;

  Edit2.Left := 0;
  Edit2.Height := 21;
  Edit2.Top := 48;
  Edit2.Width := 288;
  Edit2.BorderStyle := bsNone;
  Edit2.TabOrder := 2;
  Edit2.Font.Color := clGray;

  Edit3.Left := 0;
  Edit3.Height := 21;
  Edit3.Top := 72;
  Edit3.Width := 288;
  Edit3.BorderStyle := bsNone;
  Edit3.TabOrder := 3;
  Edit3.Font.Color := clGray;

  Edit4.Left := 0;
  Edit4.Height := 21;
  Edit4.Top := 96;
  Edit4.Width := 288;
  Edit4.BorderStyle := bsNone;
  Edit4.TabOrder := 4;
  Edit4.Font.Color := clGray;

end;



end.

At runtime my component looks like this:

enter image description here


Solution

  • You store the events into a field but you do not set the controls event.

    constructor TEditPanel.Create(AOwner: TComponent);
    begin
      ...
      // Assign the current value, but is nil at this moment
      Edit1.OnClick := FEdit1OnClick;
      ...
    end;
    
    procedure TEditPanel.SetEdit1OnClick(const AEvent: TNotifyEvent);
    begin
      // set a new value only to the field
      FEdit1OnClick := AEvent;
    end;
    

    There is no magic value transport from the field and the edit control event handler. You have to set the value also the the control event.


    But you can access the event properties direct from the controls as you do with the Text properties.

    And you should have a look at repeating code to simplify/shorten it.

    unit uEditPanel;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
    
    type
    
      { TEditPanel }
    
      TEditPanel = class( TCustomPanel )
      private
        Edit0 : TEdit;
        Edit1 : TEdit;
        Edit2 : TEdit;
        Edit3 : TEdit;
        Edit4 : TEdit;
        { Private declarations }
        function GetEditOnClick( const Index : Integer ) : TNotifyEvent;
        function GetEditText( const Index : Integer ) : string;
        procedure SetEditOnClick( const Index : Integer; const Value : TNotifyEvent );
        procedure SetEditText( const Index : Integer; const Value : string );
        procedure InitEdit( AEdit : TEdit; ATop, ATabOrder : Integer; AReadOnly : Boolean );
      protected
        { Protected declarations }
      public
        { Public declarations }
        constructor Create( AOwner : TComponent ); override;
      published
        { Published declarations }
        property Edit0Text : string index 0 read GetEditText write SetEditText;
        property Edit1Text : string index 1 read GetEditText write SetEditText;
        property Edit2Text : string index 2 read GetEditText write SetEditText;
        property Edit3Text : string index 3 read GetEditText write SetEditText;
        property Edit4Text : string index 4 read GetEditText write SetEditText;
    
        property OnEdit1Click : TNotifyEvent index 1 read GetEditOnClick write SetEditOnClick;
        property OnEdit2Click : TNotifyEvent index 2 read GetEditOnClick write SetEditOnClick;
        property OnEdit3Click : TNotifyEvent index 3 read GetEditOnClick write SetEditOnClick;
        property OnEdit4Click : TNotifyEvent index 4 read GetEditOnClick write SetEditOnClick;
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents( 'Standard', [TEditPanel] );
    end;
    
    { TEditPanel }
    
    function TEditPanel.GetEditOnClick( const Index : Integer ) : TNotifyEvent;
    begin
      case index of
        0 :
          Result := Edit0.OnClick;
        1 :
          Result := Edit1.OnClick;
        2 :
          Result := Edit2.OnClick;
        3 :
          Result := Edit3.OnClick;
        4 :
          Result := Edit4.OnClick;
      end;
    end;
    
    function TEditPanel.GetEditText( const Index : Integer ) : string;
    begin
      case index of
        0 :
          Result := Edit0.Text;
        1 :
          Result := Edit1.Text;
        2 :
          Result := Edit2.Text;
        3 :
          Result := Edit3.Text;
        4 :
          Result := Edit4.Text;
      end;
    end;
    
    procedure TEditPanel.SetEditOnClick( const Index : Integer; const Value : TNotifyEvent );
    begin
      case index of
        0 :
          Edit0.OnClick := Value;
        1 :
          Edit1.OnClick := Value;
        2 :
          Edit2.OnClick := Value;
        3 :
          Edit3.OnClick := Value;
        4 :
          Edit4.OnClick := Value;
      end;
    end;
    
    procedure TEditPanel.SetEditText( const Index : Integer; const Value : string );
    begin
      case index of
        0 :
          Edit0.Text := Value;
        1 :
          Edit1.Text := Value;
        2 :
          Edit2.Text := Value;
        3 :
          Edit3.Text := Value;
        4 :
          Edit4.Text := Value;
      end;
    end;
    
    procedure TEditPanel.InitEdit( AEdit : TEdit; ATop, ATabOrder : Integer; AReadOnly : Boolean );
    begin
      AEdit.Parent := Self;
      AEdit.SetSubComponent( True );
      AEdit.ReadOnly := AReadOnly;
      AEdit.Left := 0;
      AEdit.Top := ATop;
      AEdit.Height := 21;
      AEdit.Width := 288;
      AEdit.BorderStyle := bsNone;
      AEdit.TabOrder := ATabOrder;
      if AReadOnly
      then
        AEdit.Color := clGray;
    end;
    
    constructor TEditPanel.Create( AOwner : TComponent );
    begin
      inherited Create( AOwner );
    
      Caption := EmptyStr;
      Height := 117;
      Width := 289;
      BevelOuter := bvNone;
      ClientHeight := 117;
      ClientWidth := 289;
    
      Edit0 := TEdit.Create( Self );
      Edit1 := TEdit.Create( Self );
      Edit2 := TEdit.Create( Self );
      Edit3 := TEdit.Create( Self );
      Edit4 := TEdit.Create( Self );
    
      InitEdit( Edit0, 0, 0, False );
      InitEdit( Edit1, 24, 1, True );
      InitEdit( Edit2, 48, 2, True );
      InitEdit( Edit3, 72, 3, True );
      InitEdit( Edit4, 96, 4, True );
    end;
    
    end.
    

    Your code can be written very short and will even become shorter if you manage the edit controls with an array.