我有一个具有相应持久性的组件。这个持久性当然是作为组件的属性发布的,在对象检查器中显示。它有一些不同的属性,主要是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;
同样,这些尝试都没有奏效,我仍有这个问题。
答案 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
进行检查。)