Реализация свойства `Auto` в персистенте компонента - PullRequest
3 голосов
/ 30 ноября 2011

У меня есть компонент, который имеет соответствующий персистентный.Это постоянное, разумеется, публикуется как свойство компонента, которое отображается в инспекторе объектов.Он имеет несколько различных собственных свойств, в основном 4 целых числа (слева, сверху, справа, снизу).У меня также есть свойство в этом персистенте, называемое Auto, которое предназначено для автоматического вычисления 4 целых чисел на основе размера компонента.

Более конкретно, этот графический компонент имеет рамку вокруг него, каждое ребро можетимеют границу другого размера.Это постоянство и его свойства определяют толщину границы с каждой стороны.Когда Auto включен, 4 края границы вычисляются на основе размера компонента.

Теперь, похоже, он работает нормально по большей части, за исключением того, что каким-то образом это свойство Auto продолжает возвращаться кЛожно случайно.Кроме того, во время разработки при изменении размера компонента с включенной Auto он действительно рассчитывает все соответствующим образом, идеально.Но во время выполнения он возвращается к False и больше не вычисляет их.После сохранения с включенным Auto, закрытия формы, а затем ее повторного открытия, это свойство Auto снова возвращается в False.

4 целочисленных свойства имеют установщики, которые, если они установлены, превращаются вэто 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: Опубликовал свойство Auto после остальных 4 свойств, думая о порядке получения этих свойств.

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: в получателях свойствдля этих целых чисел я изменил способ проверки существующего значения ...

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 ]

2 голосов
/ 01 декабря 2011

Сначала, как вы уже заметили, удалите default. Если вы хотите, чтобы свойство имело значение True, если не указано другое значение, установите его в конструкторе. Когда DFM передается из вашего исполняемого файла, любое значение, сохраненное в нем, заменит значение, установленное в конструкторе.

Во-вторых, ваша проблема частично основана на неверной логике. :) Если вы хотите, чтобы свойство Auto управляло другими (то есть, если Auto = True, тогда игнорируйте любые значения, установленные для других свойств), то проверьте это в установщиках:

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;

Делая это таким образом, вы избегаете автоматических изменений, вызывая сеттер.

Я бы посоветовал, если вы можете, отложить вызов до Event во время выполнения с помощью Loaded; он вызывается после того, как компонент полностью передан из .dfm, и вы можете выполнить любые вычисления или перерисовать его после того, как все свойства установлены. Вы можете определить, находитесь ли вы в IDE или во время выполнения, изучив свойство ComponentState; если он содержит csDesigning, вы находитесь в IDE во время разработки, а если установлено значение csLoading, вы находитесь во время выполнения. (ComponentState является набором, поэтому вы проверяете, используя if csDesigning in ComponentState.)

2 голосов
/ 30 ноября 2011

FIXED!

3 попытки, которые я поместил в моё редактирование выше, были частично проблемой, но реальное исправление - удаление default свойства Auto Дело в том, что у меня по умолчанию установлено значение True, которое в данном случае это свойство не сохраняется в файле DFM. Поэтому он даже не пытался установить это свойство Auto. Удаление по умолчанию исправлено, потому что теперь, является ли оно истинным или ложным, оно всегда сохраняется в файле 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;
...