创建组件更新控件的全局属性

问题描述 投票:0回答:1

我有一组组件,它们共享一些全局变量来控制公共属性,例如风格特点。

这些当前通过全局类在运行时访问,例如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 状态,所以当我:

  1. 将 TMyCompsSettings 组件添加到表单
  2. 在对象检查器中将 MyCompsSettings1.TitleText 设置为“ABC”
  3. 打开一个不同的项目
  4. 将 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 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;
delphi components
1个回答
1
投票

由于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;
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.