How to untangle 2 components whose constructors depend on each other?
I am using this answer to force my own TPopupMenu
via the standard Cut / Copy / Paste context menu. The problem is I don't know how to lay out an OO structure to resolve this.
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 RamEdPopup
, defined in BaseRamEditor.dfm
. Note what is OnClick
referring to the method TfrmBaseRamEditor
:
BaseRamEditor.dfm
=================
object frmBaseRamEditor: TfrmBaseRamEditor
object RamEdPopup: TPopupMenu
object MenuItem1: TMenuItem
Caption = 'Diagramm 1'
OnClick = MenuItem1Click
end
end
end
So, a general overview:
- The constructor
TfrmBaseRamEditor
depends on the overriddenTStringGrid
- The constructor
TStringGrid
dependsTPopupMenu
onTfrmBaseRamEditor
. - This one
TPopupMenu
points to methodsTfrmBaseRamEditor
.
How can I untangle this mess so TStringGrid
that the used in frmBaseRamEditor
is used TPopupMenu
?
source to share
Use the owner TStringGrid
(TfrmBaseRamEditor) as a reference and cast it into the form to access the object RamEdPopup
:
TMaskEdit(Result).PopupMenu := TfrmBaseRamEditor(Self.Owner).RamEdPopup;
If you want to test the cast at runtime, use as
intrinsic:
TMaskEdit(Result).PopupMenu := (Self.Owner as TfrmBaseRamEditor).RamEdPopup;
source to share
You can create an additional property in yours TStringGrid
that will allow you to set the editor popup menu:
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 a default override for a class TStringGrid
, the published property approach and design- EditorPopup
time setting might not work for you, but there is nothing stopping you from setting the property EditorPopup
on an event FormCreate
before the user can start using the inline grid.
source to share