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

发布于 2025-01-14 10:26:42 字数 2838 浏览 1 评论 0原文

我有一组组件,它们共享一些全局变量来控制公共属性,例如样式功能。

目前,这些是在运行时通过全局类访问的,例如 MyCompsSettings().SomeProperty。

我认为允许用户在设计时配置其中一些属性可能很有用,因此我将全局类转换为组件,并且因为这些属性需要在 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 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 也会维护变量状态,因此当我:

  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;

I have a set of components, that share some global variables to control common properties, e.g. style features.

These are currently accessed at run-time via a global class, e.g. MyCompsSettings().SomeProperty.

I thought it might be useful to allow users to configure some of these properties at design-time, so I converted the global class to a component, and because these properties need to be shared between MyCompsSettings() and instances of my TMyCompsSettings component(s), I used global vars to store the state, e.g.

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;

However, I overlooked that the IDE will also maintain the var states, so when I:

  1. Add a TMyCompsSettings component to a form
  2. Set MyCompsSettings1.TitleText to 'ABC' in the object inspector
  3. Open a different project
  4. Add a TMyCompsSettings component to a form

-> MyCompsSettings1.TitleText is already 'ABC'!

Obvious of course, but I didn't consider that, and it breaks my whole model.

Is there a correct way to do this? e.g. Fields at design-time, vars at run-time, e.g.

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

英雄似剑 2025-01-21 10:26:42

由于IDE是一个进程,进程中的全局变量会保留在进程中。

如果您希望能够跟踪 IDE 中不同项目之间的设置(如果它们位于项目组中,则两个项目可能同时打开表单),那么您将需要找到一种跟踪它们的方法。

也许最简单的方法是将设置保存在对象中 - 可以在初始化部分中加载全局对象,并在最终化部分中释放全局对象。基于表单的 TComponent 可以检查它们是否处于设计模式,如果处于设计模式,则它们会创建对象的新单独副本,如果不是,它们会连接到对象的全局实例。

然后访问这些设置的其他组件都将使用全局对象 - 为了确保对象的内容与设计时版本匹配,您需要使用任何表单加载的版本覆盖全局对象。您可以在 TComponentLoaded 例程中执行此操作。

该代码未经检查,但应该为您提供其工作原理的概述。

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;

As the IDE is a process, global variables in the process will remain in the process.

If you want to be able to track the settings between different projects in the IDE (which, if they're in a project group, could both have forms open at the same time) then you will need to find a way of tracking them.

Probably the simplest way is to have the settings held in an object - there can be a global object loaded in an initialization section and freed in a finalization section. Your form based TComponents can check if they are in design mode or not and if they are in design mode then they create a new separate copy of the object, if not they connect to the global instance of the object.

Other components that then access those settings will all use the global object - to ensure that the contents of the object match the design time version you would need to overwrite the global object with any form loaded version. You can do this in the TComponent's Loaded routine.

This code is unchecked, but should give you an outline of how it might work.

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);

You would also want to ensure that in your TMySettingsComponent you update the global object when the user is changing the properties. This could be as simple as:

  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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文