【问题标题】:OnClick event handler for control in custom component not working (Lazarus)自定义组件中控件的 OnClick 事件处理程序不起作用(Lazarus)
【发布时间】:2014-04-13 18:36:13
【问题描述】:

使用:Lazarus 1.2.0; Windows 32 位应用程序

我编写了一个从 TPanel 派生的自定义组件,它包含 4 个 TEdit 控件。我已经为 TEdits 编写了 OnClick 事件处理程序代码。但是它在运行时不起作用,即事件没有触发。我不确定我错过了什么。请问你能告诉我我做错了什么吗?

组件代码如下:

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.

在运行时,我的组件如下所示:

【问题讨论】:

  • 为什么你有额外的事件变量?只需从编辑控件事件属性读取和存储事件,一切都很好

标签: delphi custom-component lazarus


【解决方案1】:

我建议改用以下方法。它不仅可以确保各种OnClick 事件正常工作,而且还可以整合所有重复代码以便于管理:

type
  TEditPanel = class(TCustomPanel)
  private
    { Private declarations }
    FEdits: array[0..4] of TEdit;
    FEditOnClick: array[1..4] of TNotifyEvent;

    function GetEditOnClick(Index: Index): TNotifyEvent;
    procedure SetEditOnClick(Index: Index; const AEvent: TNotifyEvent);

    function GetEditText(Index: Integer): string;
    procedure SetEditText(Index: Integer; const AText: string);

  protected
    { Protected declarations }
    procedure DoEditClick(Sender: TObject);

  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;

  published
    { Published declarations }
    property Edit0Text: string read GetEditText write SetEditText index 0;
    property Edit1Text: string read GetEditText write SetEditText index 1;
    property Edit2Text: string read GetEditText write SetEditText index 2;
    property Edit3Text: string read GetEditText write SetEditText index 3;
    property Edit4Text: string read GetEditText write SetEditText index 4;

    property OnEdit1Click: TNotifyEvent read GetEditOnClick write SetEditOnClick index 1;
    property OnEdit2Click: TNotifyEvent read GetEditOnClick write SetEditOnClick index 2;
    property OnEdit3Click: TNotifyEvent read GetEditOnClick write SetEditOnClick index 3;
    property OnEdit4Click: TNotifyEvent read GetEditOnClick write SetEditOnClick index 4;
  end;

procedure TEditPanel.GetEditOnClick(Index: Integer): TNotifyEvent;
begin
  Result := FEditOnClick[Index];
end;

procedure TEditPanel.SetEditOnClick(Index: Integer; const AEvent: TNotifyEvent);
begin
  FEditOnClick[Index] := AEvent;
end;

function TEditPanel.GetEditText(Index: Integer): string;
begin
  Result := FEdits[Index].Text;
end;

procedure TEditPanel.SetEditText(Index: Integer; const AText: string);
begin
  FEdits[Index].Text := AText;
end;

procedure TEditPanel.DoEditClick(Sender: TObject);
var
  Evt: TNotifyEvent;
begin
  Evt := FEditOnClick[TEdit(Sender).Tag];
  if Assigned(Evt) then
    Evt(Self);
end;

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

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

  for I := 0 To 4 do
  begin
    FEdits[I] := TEdit.Create(Self);
    FEdits[I].Parent := Self;    
    FEdits[I].SetSubComponent(True);
    FEdits[I].ReadOnly := True;
    FEdits[I].Left := 0;
    FEdits[I].Height := 21;
    FEdits[I].Top := 24 * I;
    FEdits[I].Width := 288;
    FEdits[I].BorderStyle := bsNone;
    FEdits[I].TabOrder := I;
    if I > 0 then
    begin
      FEdits[I].Tag := I;
      FEdits[I].Font.Color := clGray;
      FEdits[I].OnClick := DoEditClick;
    end;
  end;
end;

【讨论】:

    【解决方案2】:

    您将事件存储到一个字段中,但您没有设置控件事件。

    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;
    

    字段和编辑控件事件处理程序没有神奇的值传输。您还必须设置控制事件的值。


    但您可以像使用Text 属性一样直接从控件访问事件属性。

    你应该看看重复代码来简化/缩短它。

    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.
    

    如果您使用数组管理编辑控件,您的代码可以写得很短,甚至会变得更短。

    【讨论】:

    • 有效!但是为什么这不起作用: property OnEdit1Click: TNotifyEvent read FEdit1OnClick write FEdit1OnClick; ?
    • 我在回答的第一部分告诉过你
    • 是的,我现在明白了。因为一开始该字段为nil,后面需要设置为事件处理函数的地址。
    • 让TEditPanel 事件直接访问TEdit 事件的唯一缺点是事件的Sender 将是单独的TEdit 控件,而不是TEditPanel。通常,将TEditPanelassign 内部处理程序分配给TEdit 事件,然后让它们触发TEditPanel 事件,Self 作为Sender。
    • @Remy:明白。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2021-08-13
    • 1970-01-01
    • 2012-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多