如何解开构造函数相互依赖的2个组件?

时间:2021-09-01 14:12:18

I'm using this answer to force my own TPopupMenu over the standard windows "Cut/Copy/Paste" context menu. The problem is I don't know how to lay out the OO structure to allow this.

我正在使用这个答案强制我自己的TPopupMenu超过标准窗口“剪切/复制/粘贴”上下文菜单。问题是我不知道如何布置OO结构来实现这一点。

unit BaseRamEditor.pas
type
  { This will override the default TStringGrid. }
  TStringGrid = class(Grids.TStringGrid)
  protected
    function CreateEditor: TInplaceEdit; override;
  end;

  TfrmBaseRamEditor = class(TForm)
    sgrSync: TStringGrid;
    RamEdPopup: TPopupMenu;

    procedure MenuItem1Click(Sender: TObject);

implementation

{$R *.dfm}

function TStringGrid.CreateEditor: TInplaceEdit;
{ Use our TPopupMenu instead of Windows default. }
begin
  Result := inherited CreateEditor;
  // XXX: I don't know how to reference the `RamEdPopup` object that belongs to
  // `TfrmBaseRamEditor`. I can't reference the `TfrmBaseRamEditor` instance
  // because it hasn't been created yet because it depends on THIS new
  // `TStringGrid`.
  TMaskEdit(Result).PopupMenu := RamEdPopup;
end;

Here is the RamEdPopup defined in the BaseRamEditor.dfm. Notice that the OnClick references a method of TfrmBaseRamEditor:

这是BaseRamEditor.dfm中定义的RamEdPopup。请注意,OnClick引用了TfrmBaseRamEditor的方法:

BaseRamEditor.dfm
=================
object frmBaseRamEditor: TfrmBaseRamEditor
    object RamEdPopup: TPopupMenu
        object MenuItem1: TMenuItem
            Caption = 'Diagramm 1'
            OnClick = MenuItem1Click
        end
    end
end

So the overview is:

所以概述是:

  1. The TfrmBaseRamEditor constructor depends on the overridden TStringGrid
  2. TfrmBaseRamEditor构造函数依赖于重写的TStringGrid

  3. This TStringGrid constructor depends on the TPopupMenu of TfrmBaseRamEditor.
  4. 此TStringGrid构造函数依赖于TfrmBaseRamEditor的TPopupMenu。

  5. This TPopupMenu points to the methods of TfrmBaseRamEditor.
  6. 这个TPopupMenu指向TfrmBaseRamEditor的方法。

How can I untangle this mess so that the TStringGrid used in frmBaseRamEditor uses the TPopupMenu?

我如何解决这个混乱,以便frmBaseRamEditor中使用的TStringGrid使用TPopupMenu?

2 个解决方案

#1


Use the owner of the TStringGrid (the TfrmBaseRamEditor) as the reference and typecast it to the form to access the RamEdPopup object:

使用TStringGrid的所有者(TfrmBaseRamEditor)作为引用并将其类型转换为表单以访问RamEdPopup对象:

TMaskEdit(Result).PopupMenu := TfrmBaseRamEditor(Self.Owner).RamEdPopup;

If you want to check the cast at runtime, use the as intrinsic:

如果要在运行时检查强制转换,请使用as intrinsic:

TMaskEdit(Result).PopupMenu := (Self.Owner as TfrmBaseRamEditor).RamEdPopup;

#2


You can create additional property in your TStringGrid which will allow you to set editor popup menu:

您可以在TStringGrid中创建其他属性,以便设置编辑器弹出菜单:

  TStringGrid = class(Grids.TStringGrid)
  protected
    FEditorPopup: TPopupMenu;
    function CreateEditor: TInplaceEdit; override;
    procedure SetEditorPopup(Value: TPopupMenu);
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    property EditorPopup: TPopupMenu read FEditorPopup write SetEditorPopup;
  end;

function TStringGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
  TMaskEdit(Result).PopupMenu := FEditorPopup;
end;

procedure TStringGrid.SetEditorPopup(Value: TPopupMenu);
begin
  if Value <> FEditorPopup then
    begin
      if Assigned(FEditorPopup) then FEditorPopup.RemoveFreeNotification(Self);
      FEditorPopup := Value;
      if Assigned(FEditorPopup) then FEditorPopup.FreeNotification(Self);
      if Assigned(InplaceEditor) then TMaskEdit(InplaceEditor).PopupMenu := FEditorPopup;
    end;
end;

procedure TStringGrid.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = FEditorPopup) then EditorPopup := nil;
end;

Since you are creating inplace replacement for default TStringGrid class, approach with published property and setting EditorPopup at design time may not work for you, but nothing stops you from setting EditorPopup property in FormCreate event, before user can start using string grid.

由于您正在创建默认TStringGrid类的inplace替换,因此在设计时使用已发布属性和设置EditorPopup的方法可能不适合您,但在用户可以开始使用字符串网格之前,没有什么能阻止您在FormCreate事件中设置EditorPopup属性。

#1


Use the owner of the TStringGrid (the TfrmBaseRamEditor) as the reference and typecast it to the form to access the RamEdPopup object:

使用TStringGrid的所有者(TfrmBaseRamEditor)作为引用并将其类型转换为表单以访问RamEdPopup对象:

TMaskEdit(Result).PopupMenu := TfrmBaseRamEditor(Self.Owner).RamEdPopup;

If you want to check the cast at runtime, use the as intrinsic:

如果要在运行时检查强制转换,请使用as intrinsic:

TMaskEdit(Result).PopupMenu := (Self.Owner as TfrmBaseRamEditor).RamEdPopup;

#2


You can create additional property in your TStringGrid which will allow you to set editor popup menu:

您可以在TStringGrid中创建其他属性,以便设置编辑器弹出菜单:

  TStringGrid = class(Grids.TStringGrid)
  protected
    FEditorPopup: TPopupMenu;
    function CreateEditor: TInplaceEdit; override;
    procedure SetEditorPopup(Value: TPopupMenu);
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  published
    property EditorPopup: TPopupMenu read FEditorPopup write SetEditorPopup;
  end;

function TStringGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
  TMaskEdit(Result).PopupMenu := FEditorPopup;
end;

procedure TStringGrid.SetEditorPopup(Value: TPopupMenu);
begin
  if Value <> FEditorPopup then
    begin
      if Assigned(FEditorPopup) then FEditorPopup.RemoveFreeNotification(Self);
      FEditorPopup := Value;
      if Assigned(FEditorPopup) then FEditorPopup.FreeNotification(Self);
      if Assigned(InplaceEditor) then TMaskEdit(InplaceEditor).PopupMenu := FEditorPopup;
    end;
end;

procedure TStringGrid.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = FEditorPopup) then EditorPopup := nil;
end;

Since you are creating inplace replacement for default TStringGrid class, approach with published property and setting EditorPopup at design time may not work for you, but nothing stops you from setting EditorPopup property in FormCreate event, before user can start using string grid.

由于您正在创建默认TStringGrid类的inplace替换,因此在设计时使用已发布属性和设置EditorPopup的方法可能不适合您,但在用户可以开始使用字符串网格之前,没有什么能阻止您在FormCreate事件中设置EditorPopup属性。