如何提高Delphi框架的使用

发布于 2024-08-31 09:08:29 字数 483 浏览 6 评论 0 原文

我在 Delphi 中使用框架已有多年,它们是 VCL 最强大的功能之一,但标准使用它们似乎存在一些风险,例如:

  1. 很容易意外移动或编辑框架子 -框架的主机窗体上的组件,而没有意识到您正在对框架进行“调整” - 我知道这不会影响原始框架代码,但这通常不是您想要的。

  2. 在使用框架时,您仍然会接触到其子组件以进行可视化编辑,即使该框架已有多年历史并且不应触摸。

所以我开始思考......

  1. 有没有一种方法可以“分组”组件,以便“锁定”它们的位置?这对于成品表格和框架很有用。通常,其他开发人员向我返回的代码中只有表单边界发生了更改,甚至他们并没有打算进行任何更改。

  2. 有没有办法将框架及其组件变成单个 Delphi 组件?如果是这样,框架的内部结构将被完全隐藏,其可用性将进一步提高。

我对任何想法都感兴趣......

布莱恩。

I've used frames in Delphi for years, and they are one of the most powerful features of the VCL, but standard use of them seems to have some risk such as:

  1. It's easy to accidentally move or edit the frame sub-components on a frame's host form without realising that you are 'tweaking' with the frame - I know this does not affect the original frame code, but it's generally not what you would want.

  2. When working with the frame you are still exposed to its sub-components for visual editing, even when that frame is years old and should not be touched.

So I got to thinking....

  1. Is there a way of 'grouping' components such that their positions are 'locked'? This would be useful for finished forms as well as frames. Often other developers return code to me where only the form bounds have changed and even they did not intend any change.

  2. Is there any way of turning a frame and its components into a single Delphi component? If so, the frame internals would be completely hidden and its useability would increase further.

I'm interested in any thoughts...

Brian.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

为你鎻心 2024-09-07 09:08:29

将框架注册为组件可以解决 1. 和 2. 问题:

  1. 当您将该框架控件放在窗体或其他框架上时,框架上的组件将被锁定,
  2. 您将获得一个可以直观设计的组件(实际上:控件)

但是:有一些问题(可以解决,请参阅文章链接),其中最重要的是这个:

当您将组件放在框架上,然后将该框架作为组件放在 Delphi 窗体或框架上时,组件在结构窗格中可见。

问题在于,由于它们在结构窗格中可见,因此您可以删除它们,从而导致访问冲突。

解决这个问题的技巧 别忘了“小枝”
我在 Ray Konopka 那里学到了宝贵的一课/" rel="noreferrer">DelphiLive 2009。

由于该课程非常有价值,我写了一个 博客文章详细描述了它。

最重要的部分是这段小代码(更多详细信息请参见博客文章):

procedure RegisterFramesAsComponents(const Page: string; const FrameClasses: array of TFrameClass);
var
  FrameClass: TFrameClass;
begin
  for FrameClass in FrameClasses do
  begin
    RegisterComponents(Page, [FrameClass]);
    RegisterSprigType(FrameClass, TComponentSprig);
  end;
end;

希望这会有所帮助。

——杰罗恩

Registering your frames as a component solves both 1. and 2.:

  1. the components on the frame are locked when you put that frame control on a form or other frame
  2. you will get a component (actually: control) that you can design visually

But: there are a few catches (which can be solved, see article link), of which the most important is this one:

When you put components on your frame, and later drop that frame as a component on a Delphi form or frame, the components are visible in the Structure Pane.

The problem is that because they are visible in the structure pane, you can delete them, causing access violations.

The trick to solve this to not forget the 'sprig'.
I learned that valuable lesson from Ray Konopka during DelphiLive 2009.

Since the lesson is so valuable, I wrote a blog post on it that describes it in detail.

The essential portion is this little piece of code (more details in the blog post):

procedure RegisterFramesAsComponents(const Page: string; const FrameClasses: array of TFrameClass);
var
  FrameClass: TFrameClass;
begin
  for FrameClass in FrameClasses do
  begin
    RegisterComponents(Page, [FrameClass]);
    RegisterSprigType(FrameClass, TComponentSprig);
  end;
end;

Hope this helps.

--jeroen

烟若柳尘 2024-09-07 09:08:29

是的,只需将它们注册为组件即可。 :-)

正常设计你的框架,然后注册它。还要确保对不同单元没有不必要的依赖,因为在使用“组件”时这些单元是链接的。您还可以添加 published 属性,以便稍后在对象检查器中使用它们。例如,请参阅 IDE 生成的以下代码(另请参阅我的评论):

unit myUnit;

uses
 ...

type
  TmyComp = class(TFrame) //set your frame name to be the name your component 
    ToolBar1: TToolBar; //different components added in the form designer
    aliMain: TActionList;
    ...
  published //this section is added by hand
    property DataSource: TDataSource read FDataSource write SetDataSource; //some published properties added just for exemplification
    property DefFields: string read FDefFields write SetDefFields;
    ...
  end;


procedure Register; //added by hand

implementation

{$R *.DFM}

procedure Register;
begin
  RegisterComponents('MyFrames', [TmyComp]); //register the frame in the desired component category
end;

将上述代码编译到您选择的包中,安装它并检查组件选项板。 :-)

HTH

Yes, just register them as components. :-)

Design your frame normally and after this register it. Also be sure to not have unwanted dependencies on different units since these are linked when your 'component' is used. Also you can add published properties in order to use them in the Object Inspector later. See for example the following code generated by the IDE (see also my comments):

unit myUnit;

uses
 ...

type
  TmyComp = class(TFrame) //set your frame name to be the name your component 
    ToolBar1: TToolBar; //different components added in the form designer
    aliMain: TActionList;
    ...
  published //this section is added by hand
    property DataSource: TDataSource read FDataSource write SetDataSource; //some published properties added just for exemplification
    property DefFields: string read FDefFields write SetDefFields;
    ...
  end;


procedure Register; //added by hand

implementation

{$R *.DFM}

procedure Register;
begin
  RegisterComponents('MyFrames', [TmyComp]); //register the frame in the desired component category
end;

Compile the above in a package of your choice, install it and check you component palette. :-)

HTH

如梦 2024-09-07 09:08:29

我几乎总是在代码中创建框架实例。这很简单,到目前为止对我来说效果很好。

I'm almost always creating frame instances in code. This is easy and worked well for me so far.

撕心裂肺的伤痛 2024-09-07 09:08:29

只是为了增加贡献,请注意,如果您转到“结构”窗口并右键单击您选择的 TFrame 名称,然后单击“添加到调色板”菜单选项。
这将从您的框架中创建一个组件,并且您不需要创建任何Register过程。 ;-)

Just for increasing contribution, note that if you go to Structure window and right-click on the TFrame name that you chose, and click on the Add to Palete menu option.
This will make a component out of your Frame and you don't need to create any Register procedure. ;-)

来世叙缘 2024-09-07 09:08:29

当我尝试使用框架作为组件时,我也遇到了这个问题。有多种可能性可以解决明显的问题,但它们都破坏了信息隐藏的原则(框架的所有子组件都作为已发布的属性公开,这意味着每个人都可以访问它们)。

我通过实现通用的“框架控制”组件解决了这个问题:

unit RttiBrow.Cbde.FrameControl;

interface

uses
  Classes, Controls, Forms, Messages, ExtCtrls;

type
  TFrameClass = class of TFrame;

  TComponentFrame = class (TFrame)
  private
    function GetClientHeight: Integer;
    function GetClientWidth: Integer;
    procedure SetClientHeight(const Value: Integer);
    procedure SetClientWidth(const Value: Integer);
    function GetOldCreateOrder: Boolean;
    procedure SetOldCreateOrder(const Value: Boolean);
    function GetPixelsPerInch: Integer;
    procedure SetPixelsPerInch(const Value: Integer);
    function GetTextHeight: Integer;
    procedure SetTextHeight(const Value: Integer);
  published
    { workarounds for IDE bug }
    property ClientWidth: Integer read GetClientWidth write SetClientWidth stored False;
    property ClientHeight: Integer read GetClientHeight write SetClientHeight stored False;
    property OldCreateOrder: Boolean read GetOldCreateOrder write SetOldCreateOrder stored False;
    property PixelsPerInch: Integer read GetPixelsPerInch write SetPixelsPerInch stored False;
    property TextHeight: Integer read GetTextHeight write SetTextHeight stored False;
  end;

  TComponentFrame<TFrameControl: class { TControl }> = class (TComponentFrame)
  private
    function GetController: TFrameControl; inline;
  protected
    property Controller: TFrameControl read GetController;
  public
    constructor Create (AOwner: TComponent); override;
  end;

  TFrameControl<T: TFrame> = class (TWinControl)
  private
    FFrame: T;
    function PlainFrame: TFrame;
  protected
    procedure CreateParams (var Params: TCreateParams); override;
    property Frame: T read FFrame;
  public
    constructor Create (AOwner: TComponent); override;
    property DockManager;
  published
    property Align;
    property Anchors;
    property BiDiMode;
    property Color;
    property Constraints;
    property Ctl3D;
    property UseDockManager default True;
    property DockSite;
    property DoubleBuffered;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property ParentBiDiMode;
    property ParentBackground;
    property ParentColor;
    property ParentCtl3D;
    property ParentDoubleBuffered;
    property ParentFont;
    property ParentShowHint;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Touch;
    property Visible;
    property OnAlignInsertBefore;
    property OnAlignPosition;
    property OnCanResize;
    property OnConstrainedResize;
    property OnDockDrop;
    property OnDockOver;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGesture;
    property OnGetSiteInfo;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;


implementation

uses
  Windows;

{ TFrameControl<T> }

constructor TFrameControl<T>.Create(AOwner: TComponent);
begin
  inherited;
  FFrame := T (TFrameClass (T).Create (Self));
  PlainFrame.Parent := Self;
  PlainFrame.Align := alClient;
end;

procedure TFrameControl<T>.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style := Params.Style or WS_CLIPCHILDREN;
  Params.ExStyle := Params.ExStyle or WS_EX_CONTROLPARENT;
end;

function TFrameControl<T>.PlainFrame: TFrame;
begin
  Result := FFrame; // buggy compiler workaround
end;


{ TComponentFrame }

function TComponentFrame.GetOldCreateOrder: Boolean;
begin
  Result := False;
end;

function TComponentFrame.GetPixelsPerInch: Integer;
begin
  Result := 0;
end;

function TComponentFrame.GetTextHeight: Integer;
begin
  Result := 0;
end;

procedure TComponentFrame.SetClientHeight(const Value: Integer);
begin
  Height := Value;
end;

procedure TComponentFrame.SetClientWidth(const Value: Integer);
begin
  Width := Value;
end;

procedure TComponentFrame.SetOldCreateOrder(const Value: Boolean);
begin
end;

procedure TComponentFrame.SetPixelsPerInch(const Value: Integer);
begin
end;

procedure TComponentFrame.SetTextHeight(const Value: Integer);
begin
end;

function TComponentFrame.GetClientHeight: Integer;
begin
  Result := Height;
end;

function TComponentFrame.GetClientWidth: Integer;
begin
  Result := Width;
end;


{ TComponentFrame<TFrameControl> }

constructor TComponentFrame<TFrameControl>.Create(AOwner: TComponent);
begin
  inherited;
  Assert (AOwner <> nil);
  Assert (AOwner.InheritsFrom (TFrameControl));
end;

function TComponentFrame<TFrameControl>.GetController: TFrameControl;
begin
  Result := TFrameControl (Owner);
end;


end.

使用此类,添加框架作为组件变成了一个两阶段的过程:

  // frame unit
type
  TFilteredList = class;

  TFrmFilteredList = class (TComponentFrame<TFilteredList>)
    // lots of published sub-components and event methods like this one:
    procedure BtnFooClick(Sender: TObject);
  end;

  TFilteredList = class (TFrameControl<TFrmFilteredList>)
  private
    procedure Foo;
  public
    // the component's public interface
  published
    // the component's published properties
  end;

procedure Register;
...
procedure Register;
begin
  RegisterComponents ('CBDE Components', [TFilteredList]);
end;

procedure TFrmFilteredList.BtnFooClick(Sender: TObject);
begin
  Controller.Foo;
end;

procedure TFilteredList.Foo;
begin
end;
...

使用这种方法时,组件的用户将看不到您的子组件。

I also ran into that problem when trying to use frames as components. There are various possibilities to fix the obvious issues, but they all undermine the principle of information hiding (all the frame's subcomponents are exposed as published properties, which means everyone can access them).

I solved it by implementing a generic "frame control" component:

unit RttiBrow.Cbde.FrameControl;

interface

uses
  Classes, Controls, Forms, Messages, ExtCtrls;

type
  TFrameClass = class of TFrame;

  TComponentFrame = class (TFrame)
  private
    function GetClientHeight: Integer;
    function GetClientWidth: Integer;
    procedure SetClientHeight(const Value: Integer);
    procedure SetClientWidth(const Value: Integer);
    function GetOldCreateOrder: Boolean;
    procedure SetOldCreateOrder(const Value: Boolean);
    function GetPixelsPerInch: Integer;
    procedure SetPixelsPerInch(const Value: Integer);
    function GetTextHeight: Integer;
    procedure SetTextHeight(const Value: Integer);
  published
    { workarounds for IDE bug }
    property ClientWidth: Integer read GetClientWidth write SetClientWidth stored False;
    property ClientHeight: Integer read GetClientHeight write SetClientHeight stored False;
    property OldCreateOrder: Boolean read GetOldCreateOrder write SetOldCreateOrder stored False;
    property PixelsPerInch: Integer read GetPixelsPerInch write SetPixelsPerInch stored False;
    property TextHeight: Integer read GetTextHeight write SetTextHeight stored False;
  end;

  TComponentFrame<TFrameControl: class { TControl }> = class (TComponentFrame)
  private
    function GetController: TFrameControl; inline;
  protected
    property Controller: TFrameControl read GetController;
  public
    constructor Create (AOwner: TComponent); override;
  end;

  TFrameControl<T: TFrame> = class (TWinControl)
  private
    FFrame: T;
    function PlainFrame: TFrame;
  protected
    procedure CreateParams (var Params: TCreateParams); override;
    property Frame: T read FFrame;
  public
    constructor Create (AOwner: TComponent); override;
    property DockManager;
  published
    property Align;
    property Anchors;
    property BiDiMode;
    property Color;
    property Constraints;
    property Ctl3D;
    property UseDockManager default True;
    property DockSite;
    property DoubleBuffered;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property ParentBiDiMode;
    property ParentBackground;
    property ParentColor;
    property ParentCtl3D;
    property ParentDoubleBuffered;
    property ParentFont;
    property ParentShowHint;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Touch;
    property Visible;
    property OnAlignInsertBefore;
    property OnAlignPosition;
    property OnCanResize;
    property OnConstrainedResize;
    property OnDockDrop;
    property OnDockOver;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGesture;
    property OnGetSiteInfo;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;


implementation

uses
  Windows;

{ TFrameControl<T> }

constructor TFrameControl<T>.Create(AOwner: TComponent);
begin
  inherited;
  FFrame := T (TFrameClass (T).Create (Self));
  PlainFrame.Parent := Self;
  PlainFrame.Align := alClient;
end;

procedure TFrameControl<T>.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.Style := Params.Style or WS_CLIPCHILDREN;
  Params.ExStyle := Params.ExStyle or WS_EX_CONTROLPARENT;
end;

function TFrameControl<T>.PlainFrame: TFrame;
begin
  Result := FFrame; // buggy compiler workaround
end;


{ TComponentFrame }

function TComponentFrame.GetOldCreateOrder: Boolean;
begin
  Result := False;
end;

function TComponentFrame.GetPixelsPerInch: Integer;
begin
  Result := 0;
end;

function TComponentFrame.GetTextHeight: Integer;
begin
  Result := 0;
end;

procedure TComponentFrame.SetClientHeight(const Value: Integer);
begin
  Height := Value;
end;

procedure TComponentFrame.SetClientWidth(const Value: Integer);
begin
  Width := Value;
end;

procedure TComponentFrame.SetOldCreateOrder(const Value: Boolean);
begin
end;

procedure TComponentFrame.SetPixelsPerInch(const Value: Integer);
begin
end;

procedure TComponentFrame.SetTextHeight(const Value: Integer);
begin
end;

function TComponentFrame.GetClientHeight: Integer;
begin
  Result := Height;
end;

function TComponentFrame.GetClientWidth: Integer;
begin
  Result := Width;
end;


{ TComponentFrame<TFrameControl> }

constructor TComponentFrame<TFrameControl>.Create(AOwner: TComponent);
begin
  inherited;
  Assert (AOwner <> nil);
  Assert (AOwner.InheritsFrom (TFrameControl));
end;

function TComponentFrame<TFrameControl>.GetController: TFrameControl;
begin
  Result := TFrameControl (Owner);
end;


end.

With this class, adding a frame as a component becomes a two-stage process:

  // frame unit
type
  TFilteredList = class;

  TFrmFilteredList = class (TComponentFrame<TFilteredList>)
    // lots of published sub-components and event methods like this one:
    procedure BtnFooClick(Sender: TObject);
  end;

  TFilteredList = class (TFrameControl<TFrmFilteredList>)
  private
    procedure Foo;
  public
    // the component's public interface
  published
    // the component's published properties
  end;

procedure Register;
...
procedure Register;
begin
  RegisterComponents ('CBDE Components', [TFilteredList]);
end;

procedure TFrmFilteredList.BtnFooClick(Sender: TObject);
begin
  Controller.Foo;
end;

procedure TFilteredList.Foo;
begin
end;
...

When using this approach, the user of your component won't see your sub-components.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文