【发布时间】:2012-01-09 19:44:34
【问题描述】:
我有一个具有相应持久性的组件。这个持久化当然是作为组件的属性发布的,显示在对象检查器中。它有自己的几个不同的属性,主要是 4 个整数(左、上、右、下)。我在这个持久化中还有一个名为Auto 的属性,旨在根据组件的大小自动计算 4 个整数。
更具体地说,这个图形组件周围有一个边框,每条边都可以有不同大小的边框。这个持久性及其属性指定了每边边框的厚度。当Auto开启时,4个边框边是根据组件的大小来计算的。
现在它似乎在大多数情况下都可以正常工作,但不知何故,这个 Auto 属性会随机返回 False。此外,在设计时,在启用Auto 的情况下调整组件大小时,它实际上会完美地相应地进行计算。但是,在运行时,它会返回 False,不再计算它们。在启用Auto 保存后,关闭表单,然后重新打开它,这个Auto 属性又回到了False。
这 4 个整数属性具有设置器,如果设置了它们,它会将 Auto 属性设置为 false。我假设这是导致它再次变为 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;
同样,这些尝试都没有奏效,我仍然有这个问题。
【问题讨论】:
-
阅读我对您发布的答案所做的评论。此外,您已经了解到
default会阻止存储该属性,除非该值与默认值不同,但它不会将该属性设置为默认值。为什么你会认为整数属性的处理方式与布尔值不同? -
@KenWhite:我从来没有提到治疗上有什么不同。但我确实知道发布属性的顺序很重要。保存到 DFM 时,我相信它会按照它们在类中发布的顺序保存它们,同时,它们会按照它们在 DFM 中保存的顺序加载。如果先加载
Auto,然后加载4个整数,那么整数设置会触发Auto再次切换回false,即使它被保存为True。移动Auto在整数之后发布,使其在整数之后从 DFM 加载,这是部分原因 -
顺序没有区别。它们是根据 RTTI 加载的(这就是为什么存储在 DFM 中的所有属性都必须是
published,并且会自动添加{$M+}编译器选项。您的逻辑是基于对流式传输工作原理的误解。您可能有setter 过程搞砸了,但流式机制不可能这样做。 -
我在标准 VCL 库中看到了很多地方(不知道具体示例),在代码中明确注释了某些属性必须在某些其他属性之前/之后发布,因为某些原因.我不完全确定保存/加载东西的顺序,但我确信从 DFM 加载/设置属性的顺序确实有很大的不同。成像 1) 将
Auto设置为 True,2) 将Left设置为 20,然后 3) 将Left设置为 20(上面是我的代码)将Auto设置回 false。 -
(也就是说,即使不考虑 DFM,也可以考虑按不同的顺序设置这些属性)
标签: delphi properties components delphi-7 persistent