创建组件以更新控件的全局属性
Create component to update global properties of controls
我有一组组件,它们共享一些全局变量来控制公共属性,例如风格特点。
这些当前在 运行 时间通过全局 class 访问,例如MyCompsSettings().SomeProperty.
我认为允许用户在设计时配置其中一些属性可能会有用,所以我将全局 class 转换为一个组件,并且因为这些属性需要在 MyCompsSettings( ) 和我的 TMyCompsSettings 组件的实例,我使用全局变量来存储状态,例如
type
TMyCompsSettings = class(TComponent)
private
function GetBackgroundColor(): TColor;
procedure SetBackgroundColor(const v: TColor);
function GetTitleText(): string;
procedure SetTitleText(const v: string);
published
property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
property TitleText: string read GetTitleText write SetTitleText;
end;
implementation
var
gBackgroundColor: TColor;
gTitleText: string;
function TIEGlobalSettings.GetBackgroundColor(): TColor;
begin
Result := gBackgroundColor;
end;
procedure TIEGlobalSettings.SetBackgroundColor(const v: TColor);
begin
gBackgroundColor := v;
end;
function TIEGlobalSettings.GetTitleText(): string;
begin
Result := gTitleText;
end;
procedure TIEGlobalSettings.SetTitleText(const v: string);
begin
gTitleText := v;
end;
但是,我忽略了 IDE 也会保持 var 状态,所以当我:
- 将 TMyCompsSettings 组件添加到表单
- 在对象检查器中将 MyCompsSettings1.TitleText 设置为 'ABC'
- 打开一个不同的项目
- 将 TMyCompsSettings 组件添加到表单
-> MyCompsSettings1.TitleText 已经是 'ABC'!
当然很明显,但我没有考虑到这一点,它破坏了我的整个模型。
有正确的方法吗?例如设计时的字段,运行 时的变量,例如
type
TMyCompsSettings = class(TComponent)
private
FAuthoritative: Boolean; // Set to true for first instance, which will be MyCompsSettings()
FBackgroundColor: TColor;
FTitleText: string;
function GetBackgroundColor(): TColor;
procedure SetBackgroundColor(const v: TColor);
function GetTitleText(): string;
procedure SetTitleText(const v: string);
published
property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
property TitleText: string read GetTitleText write SetTitleText;
end;
implementation
function TIEGlobalSettings.GetBackgroundColor(): TColor;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FBackgroundColor
else
Result := MyCompsSettings().BackgroundColor;
end;
procedure TIEGlobalSettings.SetBackgroundColor(const v: TColor);
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
FBackgroundColor := v
else
MyCompsSettings().BackgroundColor := v;
end;
function TIEGlobalSettings.GetTitleText(): string;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FTitleText
else
Result := MyCompsSettings().TitleText;
end;
procedure TIEGlobalSettings.SetTitleText(const v: string);
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
FTitleText := v
else
MyCompsSettings().TitleText := v;
end;
由于IDE是一个进程,进程中的全局变量会保留在进程中。
如果您希望能够跟踪 IDE 中不同项目之间的设置(如果它们在一个项目组中,则可以同时打开表单)那么您将需要找到一种跟踪它们的方法。
可能最简单的方法是将设置保存在对象中 - 可以在 initialization
部分加载全局对象并在 finalization
部分释放。基于表单的 TComponents 可以检查它们是否处于设计模式,如果它们处于设计模式,那么它们会创建对象的新的单独副本,如果不是,它们会连接到对象的全局实例。
然后访问这些设置的其他组件将全部使用全局对象 - 为确保对象的内容与设计时版本匹配,您需要用任何表单加载版本覆盖全局对象。您可以在 TComponent
的 Loaded
例程中执行此操作。
此代码未经检查,但应该向您简要说明其工作原理。
implementation
type
TMySettings = class(TPersistent) // so you can .Assign
protected
FOwner: TPersistent;
function GetOwner(): TPersistent; override;
public
constructor Create(AOwner: TPersistent); reintroduce;
property
Owner: TPersistent read GetOwner();
end;
TMySettingsComponent = class(TComponent)
protected
procedure Loaded(); override;
public
destructor Destroy(); override;
procedure AfterConstruction(); override;
end;
implementation
var
gpMySettings: TMySettings;
constructor TMySettings.Create(AOwner: TPersistent);
begin
Self.FOwner:=AOwner;
inherited Create();
end;
function TMySettins.GetOwner(): TPersistent;
begin
Result:=Self.FOwner;
end;
destructor TMySettingsComponent.Destroy;
begin
if(Self.FSettings.Owner = Self) then
FreeAndNIl(Self.FSettings);
inherited;
end;
procedure TMySettingsComponent.AfterConstruction();
begin
// our ComponentState will not yet be set
if( (Self.Owner <> nil) And
(csDesigning in Self.Owner.ComponentState) ) then
Self.FSettings:=TMySettings.Create(Self)
else
Self.FSettings:=gpMySettings;
inherited;
end;
procedure TMySettingsComponent.Loaded;
begin
if( (Self.FMySettings.Owner=Self) And
(gpMySettings<>nil) ) then
gpMySettings.Assign(Self.FMySettings);
end;
initialization
gpMySettings:=TMySettings.Create(nil);
finalization
FreeAndNIl(gpMySettings);
您还希望确保在您的 TMySettingsComponent
中当用户更改属性时更新全局对象。这可能很简单:
procedure TMyComponentSettings.SetBackgroundColour(FNewValue: TColor);
begin
if(Self.FSettings.FBkColour<>FNewValue) then
begin
Self.FSettings.FBkColour:=FNewValue;
if( (Self.FSettings.Owner=Self) And
(gpMySettings<>nil) ) then
gpMySettings.Assign(Self.FSettings);
// -- or use gpMySettings.FBkColour:=FNewValue;
end;
end;
我有一组组件,它们共享一些全局变量来控制公共属性,例如风格特点。
这些当前在 运行 时间通过全局 class 访问,例如MyCompsSettings().SomeProperty.
我认为允许用户在设计时配置其中一些属性可能会有用,所以我将全局 class 转换为一个组件,并且因为这些属性需要在 MyCompsSettings( ) 和我的 TMyCompsSettings 组件的实例,我使用全局变量来存储状态,例如
type
TMyCompsSettings = class(TComponent)
private
function GetBackgroundColor(): TColor;
procedure SetBackgroundColor(const v: TColor);
function GetTitleText(): string;
procedure SetTitleText(const v: string);
published
property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
property TitleText: string read GetTitleText write SetTitleText;
end;
implementation
var
gBackgroundColor: TColor;
gTitleText: string;
function TIEGlobalSettings.GetBackgroundColor(): TColor;
begin
Result := gBackgroundColor;
end;
procedure TIEGlobalSettings.SetBackgroundColor(const v: TColor);
begin
gBackgroundColor := v;
end;
function TIEGlobalSettings.GetTitleText(): string;
begin
Result := gTitleText;
end;
procedure TIEGlobalSettings.SetTitleText(const v: string);
begin
gTitleText := v;
end;
但是,我忽略了 IDE 也会保持 var 状态,所以当我:
- 将 TMyCompsSettings 组件添加到表单
- 在对象检查器中将 MyCompsSettings1.TitleText 设置为 'ABC'
- 打开一个不同的项目
- 将 TMyCompsSettings 组件添加到表单
-> MyCompsSettings1.TitleText 已经是 'ABC'!
当然很明显,但我没有考虑到这一点,它破坏了我的整个模型。
有正确的方法吗?例如设计时的字段,运行 时的变量,例如
type
TMyCompsSettings = class(TComponent)
private
FAuthoritative: Boolean; // Set to true for first instance, which will be MyCompsSettings()
FBackgroundColor: TColor;
FTitleText: string;
function GetBackgroundColor(): TColor;
procedure SetBackgroundColor(const v: TColor);
function GetTitleText(): string;
procedure SetTitleText(const v: string);
published
property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
property TitleText: string read GetTitleText write SetTitleText;
end;
implementation
function TIEGlobalSettings.GetBackgroundColor(): TColor;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FBackgroundColor
else
Result := MyCompsSettings().BackgroundColor;
end;
procedure TIEGlobalSettings.SetBackgroundColor(const v: TColor);
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
FBackgroundColor := v
else
MyCompsSettings().BackgroundColor := v;
end;
function TIEGlobalSettings.GetTitleText(): string;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FTitleText
else
Result := MyCompsSettings().TitleText;
end;
procedure TIEGlobalSettings.SetTitleText(const v: string);
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
FTitleText := v
else
MyCompsSettings().TitleText := v;
end;
由于IDE是一个进程,进程中的全局变量会保留在进程中。
如果您希望能够跟踪 IDE 中不同项目之间的设置(如果它们在一个项目组中,则可以同时打开表单)那么您将需要找到一种跟踪它们的方法。
可能最简单的方法是将设置保存在对象中 - 可以在 initialization
部分加载全局对象并在 finalization
部分释放。基于表单的 TComponents 可以检查它们是否处于设计模式,如果它们处于设计模式,那么它们会创建对象的新的单独副本,如果不是,它们会连接到对象的全局实例。
然后访问这些设置的其他组件将全部使用全局对象 - 为确保对象的内容与设计时版本匹配,您需要用任何表单加载版本覆盖全局对象。您可以在 TComponent
的 Loaded
例程中执行此操作。
此代码未经检查,但应该向您简要说明其工作原理。
implementation
type
TMySettings = class(TPersistent) // so you can .Assign
protected
FOwner: TPersistent;
function GetOwner(): TPersistent; override;
public
constructor Create(AOwner: TPersistent); reintroduce;
property
Owner: TPersistent read GetOwner();
end;
TMySettingsComponent = class(TComponent)
protected
procedure Loaded(); override;
public
destructor Destroy(); override;
procedure AfterConstruction(); override;
end;
implementation
var
gpMySettings: TMySettings;
constructor TMySettings.Create(AOwner: TPersistent);
begin
Self.FOwner:=AOwner;
inherited Create();
end;
function TMySettins.GetOwner(): TPersistent;
begin
Result:=Self.FOwner;
end;
destructor TMySettingsComponent.Destroy;
begin
if(Self.FSettings.Owner = Self) then
FreeAndNIl(Self.FSettings);
inherited;
end;
procedure TMySettingsComponent.AfterConstruction();
begin
// our ComponentState will not yet be set
if( (Self.Owner <> nil) And
(csDesigning in Self.Owner.ComponentState) ) then
Self.FSettings:=TMySettings.Create(Self)
else
Self.FSettings:=gpMySettings;
inherited;
end;
procedure TMySettingsComponent.Loaded;
begin
if( (Self.FMySettings.Owner=Self) And
(gpMySettings<>nil) ) then
gpMySettings.Assign(Self.FMySettings);
end;
initialization
gpMySettings:=TMySettings.Create(nil);
finalization
FreeAndNIl(gpMySettings);
您还希望确保在您的 TMySettingsComponent
中当用户更改属性时更新全局对象。这可能很简单:
procedure TMyComponentSettings.SetBackgroundColour(FNewValue: TColor);
begin
if(Self.FSettings.FBkColour<>FNewValue) then
begin
Self.FSettings.FBkColour:=FNewValue;
if( (Self.FSettings.Owner=Self) And
(gpMySettings<>nil) ) then
gpMySettings.Assign(Self.FSettings);
// -- or use gpMySettings.FBkColour:=FNewValue;
end;
end;