在组件的持久化中实现`Auto`属性

时间:2011-11-30 15:16:43

标签: delphi properties components delphi-7 persistent

我有一个具有相应持久性的组件。这个持久性当然是作为组件的属性发布的,在对象检查器中显示。它有一些不同的属性,主要是4个整数(左,上,右,下)。我在这个持久化的Auto中也有一个属性,它用于根据组件的大小自动计算4个整数。

更具体地说,此图形组件周围有一个边框,每个边可以有不同大小的边框。此持久性及其属性指定每侧边框的粗细。启用Auto时,将根据组件的大小计算4个边框边缘。

现在它似乎在大多数情况下工作正常,除了,不知何故,这个Auto属性会随机返回False。此外,在设计时,在启用Auto的情况下调整组件大小时,事实上它确实相应地计算了相应的内容。但是,在运行时,它会回到False,不再计算它们。在启用Auto保存后,关闭表单,然后重新打开它,此Auto属性将再次返回False。

4个整数属性具有setter,如果设置它们,则会将此Auto属性设置为false。我假设这是导致它再次变为假的原因,除了,我没有告诉它在任何地方设置这些属性。

这是持久性:

  TJDGlassBorder = class(TPersistent)
  private
    fOwner: TJDGlass; //This is the parent component
    fGlow: Integer;
    fBottom: Integer;
    fLeft: Integer;
    fTop: Integer;
    fRight: Integer;
    fColor: TColor;
    fOnEvent: TNotifyEvent;
    fAuto: Bool;
    procedure SetBottom(const Value: Integer);
    procedure SetColor(const Value: TColor);
    procedure SetGlow(const Value: Integer);
    procedure SetLeft(const Value: Integer);
    procedure SetRight(const Value: Integer);
    procedure SetTop(const Value: Integer);
    function GetBottom: Integer;
    function GetLeft: Integer;
    function GetRight: Integer;
    function GetTop: Integer;
    procedure SetAuto(const Value: Bool);
  public
    constructor Create(AOwner: TJDGlass);
    destructor Destroy; override;
    procedure Event;
    procedure Assign(Source: TPersistent); override;
  published
    property Auto: Bool read fAuto write SetAuto default True;
    property Left: Integer read GetLeft write SetLeft default 3;
    property Top: Integer read GetTop write SetTop default 2;
    property Right: Integer read GetRight write SetRight default 3;
    property Bottom: Integer read GetBottom write SetBottom default 4;
    property Color: TColor read fColor write SetColor;
    property Glow: Integer read fGlow write SetGlow default 1;
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
  end;

/////////////

{ TJDGlassBorder }

procedure TJDGlassBorder.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  Event;
end;

constructor TJDGlassBorder.Create(AOwner: TJDGlass);
begin
  fOwner:= AOwner;
  fAuto:= True;
  fColor:= clBlack;
  fGlow:= 1;
  Event;
end;

destructor TJDGlassBorder.Destroy;
begin

  inherited;
end;

procedure TJDGlassBorder.Event;
begin
  if assigned(fOwner) then 
    if fOwner <> nil then
      fOwner.Invalidate;
  if assigned(fOnEvent) then
    fOnEvent(Self);
end;

function TJDGlassBorder.GetBottom: Integer;
begin
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= Max(2, fOwner.Height div 10);
        fBottom:= Result;
      end;
    end;
  end else begin
    Result:= fBottom;
  end;
end;

function TJDGlassBorder.GetLeft: Integer;
begin
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= (Top + Bottom) div 2;
        fLeft:= Result;
      end;
    end;
  end else begin
    Result:= fLeft;
  end;
end;

function TJDGlassBorder.GetRight: Integer;
begin
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= (Top + Bottom) div 2;
        fRight:= Result;
      end;
    end;
  end else begin
    Result:= fRight;
  end;
end;

function TJDGlassBorder.GetTop: Integer;
begin
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= Max(1, fOwner.Height div 30);
        fTop:= Result;
      end;
    end;
  end else begin
    Result:= fTop;
  end;
end;

procedure TJDGlassBorder.SetAuto(const Value: Bool);
begin
  fAuto := Value;
  Event;
end;

procedure TJDGlassBorder.SetBottom(const Value: Integer);
begin          
  fAuto:= False;
  fBottom := Value;  
  Event;
end;

procedure TJDGlassBorder.SetColor(const Value: TColor);
begin
  fColor := Value;  
  Event;
end;

procedure TJDGlassBorder.SetGlow(const Value: Integer);
begin
  fGlow := Value;  
  Event;
end;

procedure TJDGlassBorder.SetLeft(const Value: Integer);
begin         
  fAuto:= False;
  fLeft := Value;  
  Event;
end;

procedure TJDGlassBorder.SetRight(const Value: Integer);
begin       
  fAuto:= False;
  fRight := Value; 
  Event;
end;

procedure TJDGlassBorder.SetTop(const Value: Integer);
begin           
  fAuto:= False;
  fTop := Value;
  Event;
end;

修改

我在上面的代码中尝试了3件事,但仍有问题。这就是我所做的:

1:在其他4个属性之后发布Auto属性,考虑检索这些属性的顺序。

published
  property Auto: Bool read fAuto write SetAuto default True;
  property Left: Integer read GetLeft write SetLeft default 3;
  property Top: Integer read GetTop write SetTop default 2;
  property Right: Integer read GetRight write SetRight default 3;
  property Bottom: Integer read GetBottom write SetBottom default 4;

更改为:

published
  property Left: Integer read GetLeft write SetLeft default 3;
  property Top: Integer read GetTop write SetTop default 2;
  property Right: Integer read GetRight write SetRight default 3;
  property Bottom: Integer read GetBottom write SetBottom default 4;
  property Auto: Bool read fAuto write SetAuto default True;

2:在这些整数的属性设置器中,我正在检查新值是否与现有值不同...

procedure TJDGlassBorder.SetTop(const Value: Integer);
begin         
  if Value <> fTop then begin  
    fAuto:= False;
    fTop := Value;
    Event;
  end;
end;

3:在这些整数的属性getter中,我改变了它检查现有值的方式......

function TJDGlassBorder.GetTop: Integer;
begin
  Result:= fTop;
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= Max(1, fOwner.Height div 30);
        fTop:= Result;
      end;
    end;
  end;
end;

同样,这些尝试都没有奏效,我仍有这个问题。

2 个答案:

答案 0 :(得分:2)

<强>固定!

我在上面的编辑中进行的3次尝试部分是问题,但实际修复是删除default属性的Auto。问题是,我将此默认为True,在这种情况下,该属性未保存在DFM文件中。因此它甚至没有尝试设置此Auto属性。删除Default fixed,因为现在无论是true还是false,它总是保存在DFM文件中,因此始终设置此值。发布属性的顺序也是问题的一半。


以下是我在上面发布的内容的最终代码:

  TJDGlassBorder = class(TPersistent)
  private
    fOwner: TJDGlass; //This is the parent component
    fGlow: Integer;
    fBottom: Integer;
    fLeft: Integer;
    fTop: Integer;
    fRight: Integer;
    fColor: TColor;
    fOnEvent: TNotifyEvent;
    fAuto: Bool;
    procedure SetBottom(const Value: Integer);
    procedure SetColor(const Value: TColor);
    procedure SetGlow(const Value: Integer);
    procedure SetLeft(const Value: Integer);
    procedure SetRight(const Value: Integer);
    procedure SetTop(const Value: Integer);
    function GetBottom: Integer;
    function GetLeft: Integer;
    function GetRight: Integer;
    function GetTop: Integer;
    procedure SetAuto(const Value: Bool);
  public
    constructor Create(AOwner: TJDGlass);
    destructor Destroy; override;
    procedure Event;
    procedure Assign(Source: TPersistent); override;
  published
    property Left: Integer read GetLeft write SetLeft default 3;
    property Top: Integer read GetTop write SetTop default 2;
    property Right: Integer read GetRight write SetRight default 3;
    property Bottom: Integer read GetBottom write SetBottom default 4;
    property Color: TColor read fColor write SetColor;
    property Glow: Integer read fGlow write SetGlow default 1;
    property OnEvent: TNotifyEvent read fOnEvent write fOnEvent;
    property Auto: Bool read fAuto write SetAuto;
  end;

/////////////

{ TJDGlassBorder }

procedure TJDGlassBorder.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  Event;
end;

constructor TJDGlassBorder.Create(AOwner: TJDGlass);
begin
  fOwner:= AOwner;
  fAuto:= True;
  fColor:= clBlack;
  fGlow:= 1;
  Event;
end;

destructor TJDGlassBorder.Destroy;
begin

  inherited;
end;

procedure TJDGlassBorder.Event;
begin
  if assigned(fOwner) then 
    if fOwner <> nil then
      fOwner.Invalidate;
  if assigned(fOnEvent) then
    fOnEvent(Self);
end;

function TJDGlassBorder.GetBottom: Integer;
begin
  Result:= fBottom;
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= Max(2, fOwner.Height div 10);
        fBottom:= Result;
      end;
    end;
  end;
end;

function TJDGlassBorder.GetLeft: Integer;
begin
  Result:= fLeft;
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= (Top + Bottom) div 2;
        fLeft:= Result;
      end;
    end;
  end;
end;

function TJDGlassBorder.GetRight: Integer;
begin
  Result:= fRight;
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= (Top + Bottom) div 2;
        fRight:= Result;
      end;
    end;
  end;
end;

function TJDGlassBorder.GetTop: Integer;
begin
  Result:= fTop;
  if fAuto then begin
    if assigned(fOwner) then begin
      if fOwner <> nil then begin
        Result:= Max(1, fOwner.Height div 30);
        fTop:= Result;
      end;
    end;
  end;
end;

procedure TJDGlassBorder.SetAuto(const Value: Bool);
begin
  fAuto := Value;
  Event;
end;

procedure TJDGlassBorder.SetBottom(const Value: Integer);
begin
  if Value <> fBottom then begin
    fAuto:= False;
    fBottom := Value;  
    Event;
  end;
end;

procedure TJDGlassBorder.SetColor(const Value: TColor);
begin
  fColor := Value;  
  Event;
end;

procedure TJDGlassBorder.SetGlow(const Value: Integer);
begin
  fGlow := Value;  
  Event;
end;

procedure TJDGlassBorder.SetLeft(const Value: Integer);
begin         
  if Value <> fLeft then begin
    fAuto:= False;
    fLeft := Value;  
    Event;
  end;
end;

procedure TJDGlassBorder.SetRight(const Value: Integer);
begin       
  if Value <> fRight then begin
    fAuto:= False;
    fRight := Value; 
    Event;
  end;
end;

procedure TJDGlassBorder.SetTop(const Value: Integer);
begin        
  if Value <> fTop then begin   
    fAuto:= False;
    fTop := Value;
    Event;
  end;
end;

答案 1 :(得分:2)

首先,正如您已经注意到的那样,删除default。如果您希望属性为True,除非特别设置为其他值,请在构造函数中设置它。从您的可执行文件中流入DFM时,存储在其中的任何值都将替换构造函数中的一个值。

其次,您的问题部分基于错误的逻辑。 :)如果你想让Auto属性控制其他属性(意思是Auto = True然后忽略为其他属性设置的任何值),那么在setter中测试它:

procedure TJDGlassBorder.SetTop(const Value: Integer);
begin
  // Only change the value if Auto is not True         
  if (not FAuto) and (Value <> fTop) then 
  begin  
    fTop := Value;
    Event;
  end;
end;

procedure TJDGlassBorder.SetAuto(const Value: Boolean);
begin
  if (Value <> FAuto) then
  begin
    FAuto := Value;
    if FAuto then
    begin
      FTop := 0;     // Or whatever. Set field and not property to 
      FLeft := 0;    // avoid the setter's side effects
      FWidth := 0;
      FHeight := 0;
    end;
    // Whatever you need to do now.
  end;
end;

这样做意味着您可以通过调用setter来避免自动更改。

如果可以,我建议您在运行时使用Event将调用推迟到Loaded;在从.dfm完全流式传输组件之后调用此方法,并且可以在设置所有属性后执行任何计算或重绘。您可以通过检查ComponentState属性来确定您是在IDE中还是在运行时;如果它包含csDesigning,则您在设计时处于IDE中,如果设置了csLoading,则表示您处于运行时。 (ComponentState是一个集合,因此您使用if csDesigning in ComponentState进行检查。)