【问题标题】:How to untangle 2 components whose constructors depend on each other?如何解开构造函数相互依赖的 2 个组件?
【发布时间】:2015-05-07 08:00:39
【问题描述】:

我正在使用this answer 在标准窗口“剪切/复制/粘贴”上下文菜单上强制使用我自己的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;

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

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

所以概述是:

  1. TfrmBaseRamEditor 构造函数依赖于被覆盖的TStringGrid
  2. 这个TStringGrid 构造函数依赖于TfrmBaseRamEditorTPopupMenu
  3. 这个TPopupMenu指向TfrmBaseRamEditor的方法。

我怎样才能解开这个混乱,以便frmBaseRamEditor 中使用的TStringGrid 使用TPopupMenu

【问题讨论】:

  • 你的事情过于复杂了。 TStringGrid 有一个可以使用的 PopupMenu 属性。
  • @Peter 我知道它有,但我必须使用链接答案的方法,因为如果我只设置TStringGrid.PopupMenu 然后在单元格内左键单击以编辑一些文本,然后右键单击:上下文菜单将是标准的 Windows 不是我自己的TPopupMenu。我的TPopupMenu 只会在我取消选择单元格(即单击其他位置)然后右键单击该单元格时显示。
  • TMaskEdit(Result).PopupMenu := TfrmBaseRamEditor(Self.Owner).RamEdPopup; 工作吗?
  • @LURD 哇,是的。我不知道你可以做这样的参考;这太酷了!非常感谢您帮助我
  • 您说构造函数相互依赖,但您的代码中没有显示任何构造函数。我了解表单的构造函数如何依赖网格(因为表单加载 DFM 并创建其子项,从而构建网格控件),但我不知道网格的构造函数将如何依赖表单。

标签: delphi delphi-xe2


【解决方案1】:

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

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

如果您想在运行时检查演员表,请使用 as 内在函数:

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

【讨论】:

  • 您能解释一下为什么需要进行类型转换吗?没有它我得到编译器错误:“未声明的标识符:RamEdPopup”
  • 没有类型转换,所有者只是任何TComponent,不知道TfrmBaseRamEditor 的细节。在这种情况下,您知道所有者是TfrmBaseRamEditor,并且演员表没问题。
  • Owner 的编译时间类型为 TComponent。但是运行时类型是一个更加派生的类。我希望看到as 在此处用于在运行时检查演员表。
  • @DavidHeffernan,是的,这可能是最好的办法。
【解决方案2】:

您可以在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;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    相关资源
    最近更新 更多