Как улучшить использование 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 читать только в вашем исходном элементе управления?