Usando Objetos para guardar la configuración del programa (2ª parte)

Declaración con Máscaras:

En esta declaración la parte de lectura (read) de la propiedad se cambia por una función. (El nombre de la función puede ser cualquiera, por convención es GET + nombre de la propiedad). Luego de escribir

property Color: Integer   read GetColor write fColor;

presionamos Ctrl + Shift + C y Delphi crea una variable privada fColor del tipo Integer y un procedimiento privado GetColor más el cuerpo del mismo.

function TOpciones.GetColor: Integer;
begin
  Result := ABS(fColor); //Es equivalente a GetColor := ABS(fColor);
end;

Dentro de la función debemos asignar al resultado de la función (usando el nombre o « result ») el valor que está almacenado.

¿Cómo funciona?

Cuando escribimos… el compilador lo traduce en …

Opciones.Color := Panel1.Color;    Opciones.FColor := Panel1.Color;
Label1.Color := Opciones.Color;   Label1.Color := Opciones.GetColor;

¿Cómo se guardan las propiedades? Sólo se guardarán las propiedades declaradas en la sección published. Las propiedades publicadas de tipo Integer, Char, Boolean, Real, String, TFont, TStrings, TDateTime y sus símiles (TColor, Byte, AnsiString, WideBool, etc), se almacenan sin inconvenientes y no requieren de nuestra intervención. En cambio, las propiedades de tipo TPoint, TRect (y la mayoría de los descendientes de TObject) necesitan que les indiquemos cómo deben guardarse. Para esto debemos seguir los siguientes pasos:

Paso 1 En la sección protected debemos agregar:

procedure DefineProperties(Filer: TFiler); override; 

En el cuerpo del procedimiento indicamos cómo se guardarán y leerán los valores

procedure TOpciones.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('ARECT', ReadRect, WriteRect, True);
end;

El primer parámetro es el nombre con que se guarda la propiedad en el objeto, ReadRect y WriteRect son procedimientos privados o protegidos de nuestra clase. El último parámetro lo dejamos en verdadero.

Paso 2 Declaramos (en private o protected) los procedimientos de lectura y escritura:

procedure ReadRect(Reader:Treader);
procedure WriteRect(Writer:Twriter);

(Si presionamos Ctrl + Shift + C, Delphi completa las declaraciones)

Paso 3 Implementamos los procedimientos:

procedure TOpciones.WriteRect(Writer: Twriter); 
begin 
  Writer.WriteListBegin;
  Writer.WriteInteger(FARect.Left);
  Writer.WriteInteger(FARect.Top);
  Writer.WriteInteger(FARect.Right);
  Writer.WriteInteger(FARect.Bottom);
  Writer.WriteListEnd;
end;

procedure TOpciones.ReadRect(Reader: Treader);
begin
  Reader.ReadListBegin;
  FARect.Left   := Reader.ReadInteger;
  FARect.Top    := Reader.ReadInteger;
  FARect.Right  := Reader.ReadInteger;
  FARect.Bottom := Reader.ReadInteger;
  Reader.ReadListEnd;
end;

En estos procedimientos conviene siempre hacer referencia a las variables privadas en lugar de hacer referencia a las propiedades.

Propiedades que son objetos:

Debemos sobrescribir el constructor para Crear nuestros objetos internos (Font, Lines y otros) e inicializar las variables internas. La declaración será (en la sección public)

constructor Create(aOwner: TComponent); override;

y el cuerpo …

constructor TOpciones.Create(aOwner: TComponent); 
begin 
  inherited Create(aOwner);   
  fLines   := TStringList.Create; //Nunca usar fLines:= TStrings.Create; 
  fFont    := TFont.Create; 

  fLastUse := Now; 
  fColor   := 65535; //clYellow 
//... 
end; 

Todo lo que hemos creamos, debemos destruirlo

destructor TOpciones.Destroy; 
begin 
//... 
  fLines.Free; 
  fFont.Free; 
  inherited Destroy; 
//... 
end;

El corazón de la bestia. Leyendo y guardando el objeto Nuestro componente hereda de TComponent por estos motivos:

  • WriteComponentResFile() y ReadComponentResFile(): para guardar un componente a un archivo y leerlo
  • ComponentToString () y StringToComponent(): para convertir un componente a texto y viceversa.

Para guardar y leer nos valemos de dos procedimientos públicos

procedure SaveConfig;
procedure ReadConfig;

que (luego de presionar Ctrl + Shift +C) implementamos así

procedure TOpciones.SaveConfig; 
begin 
  WriteComponentResFile('C:\Config.dat',Self);
end; 

procedure TOpciones.ReadConfig; 
begin 
  if fileexists('C:\Config.dat') then 
    try 
{»»}  Self := TOpciones(ReadComponentResFile('C:\Config.dat', Self)); 
    except 
    end 
end; 

Si queremos utilizar el registro de windows debemos realizar unos pequeños cambios:

procedure TOpciones.SaveConfig; 
begin 
  with TRegistry.Create do 
    try 
     RootKey := HKEY_LOCAL_MACHINE; 
     if OpenKey('\Software\NiceDemo', True) then 
{»»}     WriteString('SillySection', ComponentToString(Self)); 
     CloseKey; 
    finally 
      Free; 
    end; 
end; 

procedure TOpciones.LoadConfig;
begin 
  with TRegistry.Create do 
    try 
      RootKey := RegKey; 
      if OpenKey('\Software\NiceDemo', False) then 
        begin 
        S := ReadString('SillySection'); 
        if S <> '' then 
{»»}       Self := TOpciones(StringToComponent( S )); 
        CloseKey; 
        end; 
    finally 
      Free; 
    end; 
end;