Как улучшить использование Delphi Frames

Я использую фреймы в Delphi в течение многих лет, и они являются одной из самых мощных функций VCL, но стандартное их использование, похоже, имеет некоторый риск, например:

  • Легко случайно переместить или отредактировать подкомпоненты фрейма в форме хоста фрейма, не понимая, что вы "настраиваете" кадр - я знаю, что это не влияет на исходный код кадра, но, как правило, что бы вы хотели.

  • При работе с фреймом вы по-прежнему подвергаетесь его подкомпонентам для визуального редактирования, даже если этот кадр лет и не должен касаться.

Итак, я подумал...

  • Есть ли способ "группировать" компоненты, чтобы их позиции были "заблокированы"? Это было бы полезно для готовых форм, а также для фреймов. Часто другие разработчики возвращают мне код, где изменяются только границы форм, и даже они не намеревались никаких изменений.

  • Есть ли способ превратить фрейм и его компоненты в один компонент Delphi? Если это так, внутренности фрейма будут полностью скрыты, и его пригодность будет еще больше увеличиваться.

Меня интересуют любые мысли...

Брайан.

Ответ 1

Регистрация ваших кадров в качестве компонента решает как 1. и 2.:

  • компоненты на фрейме блокируются, когда вы помещаете этот элемент управления рамкой в ​​форму или другой кадр
  • вы получите компонент (на самом деле: control), который вы можете проектировать визуально

Но: есть несколько уловов (которые можно решить, см. ссылку статьи), из которых наиболее важно следующее:

Когда вы помещаете компоненты в свой фрейм, а затем отбрасываете этот кадр в качестве компонента в форме или фрейме Delphi, компоненты видны в панели структуры.

Проблема заключается в том, что, поскольку они видны в панели структуры, вы можете удалить их, вызывая нарушения доступа.

Трюк, чтобы решить эту проблему, не забывайте о "веточке" .
Я узнал этот ценный урок из Ray Konopka во время DelphiLive 2009.

Поскольку урок настолько ценен, я написал

Ответ 2

Да, просто зарегистрируйте их как компоненты.: -)

Создайте свой кадр, как обычно, и после этого зарегистрируйте его. Также не забудьте не иметь нежелательных зависимостей от разных устройств, поскольку они связаны, когда используется ваш "компонент". Также вы можете добавить свойства published, чтобы впоследствии использовать их в Object Inspector. См. Например, следующий код, сгенерированный 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;

Скомпилируйте вышеуказанное в пакете по вашему выбору, установите его и проверьте палитру компонентов.: -)

НТН

Ответ 3

Только для увеличения вклада обратите внимание: если вы перейдете в окно Structure и щелкните правой кнопкой мыши по имени TFrame, которое вы выбрали, и нажмите на пункт меню Add to Palete. Это сделает компонент из вашего фрейма, и вам не нужно создавать какую-либо процедуру Register.; -)

Ответ 4

Я почти всегда создаю экземпляры кадров в коде. Это легко и хорошо работает для меня до сих пор.

Ответ 5

Я также столкнулся с этой проблемой при попытке использовать фреймы в качестве компонентов. Существуют различные возможности для устранения очевидных проблем, но все они подрывают принцип скрытия информации (все подкомпоненты кадра отображаются как опубликованные свойства, что означает, что каждый может получить к ним доступ).

Я решил это, выполнив общий компонент "Управление рамкой":

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 public interface
  published
    // the component 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;
...

При использовании этого подхода пользователь вашего компонента не увидит ваши подкомпоненты.

Ответ 6

Для альтернативного решения, можете ли файлы .dfm читать только в вашем исходном элементе управления?