26

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

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

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

于是我开始思考......

  1. 有没有办法“分组”组件以使其位置被“锁定”?这对于完成的表格和框架都很有用。通常其他开发人员将代码返回给我,其中只有表单边界发生了变化,甚至他们也不打算进行任何更改。

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

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

布赖恩。

4

5 回答 5

24

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

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

但是:有几个问题(可以解决,见文章链接),其中最重要的是这个:

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

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

解决这个问题的诀窍是不要忘记 'sprig'在DelphiLive 2009期间,我从Ray Konopka
那里学到了宝贵的一课。

由于这节课很有价值,我写了一篇关于它的博客文章,详细描述了它。

最重要的部分是这段代码(更多细节在博客文章中):

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;

希望这可以帮助。

——杰伦

于 2010-04-29T10:27:24.227 回答
19

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

正常设计你的框架,然后注册它。还要确保不要对不同的单元有不必要的依赖,因为在使用“组件”时这些是链接的。您也可以添加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;

在您选择的包中编译上述内容,安装它并检查您的组件调色板。:-)

高温高压

于 2010-04-29T08:28:27.240 回答
8

只是为了增加贡献,请注意,如果您转到Structure窗口并右键单击您选择的 TFrame 名称,然后单击Add to Palete菜单选项。这将从您的框架中创建一个组件,您无需创建任何Register过程。;-)

于 2012-07-04T03:01:43.907 回答
7

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

于 2010-04-29T08:21:58.923 回答
4

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

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

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;
...

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

于 2010-04-29T13:05:18.293 回答