我有一组组件,它们共享一些全局变量来控制公共属性,例如风格特点。
这些当前通过全局类在运行时访问,例如MyCompsSettings().SomeProperty.
我认为允许用户在设计时配置其中一些属性可能很有用,所以我将全局类转换为一个组件,并且因为这些属性需要在 MyCompsSettings() 和我的 TMyCompsSettings 组件实例之间共享( s),我使用全局变量来存储状态,例如
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 TMyCompsSettings.GetBackgroundColor(): TColor;
begin
Result := gBackgroundColor;
end;
procedure TMyCompsSettings.SetBackgroundColor(const v: TColor);
begin
gBackgroundColor := v;
end;
function TMyCompsSettings.GetTitleText(): string;
begin
Result := gTitleText;
end;
procedure TMyCompsSettings.SetTitleText(const v: string);
begin
gTitleText := v;
end;
但是,我忽略了 IDE 也会维护 var 状态,所以当我:
-> 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 TMyCompsSettings.GetBackgroundColor(): TColor;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FBackgroundColor
else
Result := MyCompsSettings().BackgroundColor;
end;
procedure TMyCompsSettings.SetBackgroundColor(const v: TColor);
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
FBackgroundColor := v
else
MyCompsSettings().BackgroundColor := v;
end;
function TMyCompsSettings.GetTitleText(): string;
begin
if FAuthoritative or ( csDesigning in ComponentState ) then
Result := FTitleText
else
Result := MyCompsSettings().TitleText;
end;
procedure TMyCompsSettings.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;