1. Computing
RTL referenceGlossary|Tips/Tricks|FREE App/VCL|Best'O'Net|Books|Link To
 
Using Delphi objects to store configuration information
The purpose of this article is to explain how to use objects to substitute the INI files (and other similar techniques) to store configuration information.
 More of this Feature
• Download CODE
 Join the Discussion
"Post your questions, concerns, views and comments to this article..."
Discuss!
 Related Resources
• Working with INI files
• Working with Registry
• OOP in Delphi
• Creating custom components

Article submitted by: Sebastián Mayorá

   Introduction
The purpose of this article is to explain how to use objects to substitute the old INI files (and other similar techniques) to store configuration information. Object advantages:

  • Less coding (save your time)
  • Less error possibilities
  • Encapsulation (all parameters together)
  • Use files or registry
  • etc.

    Some problems with ini files

  • We should declare a variable for each option to keep, which implies to have 20 (or more) variables lost in the code of the program.
  • When we need to store another parameter more we should remember to write the instructions IniFile.ReadXXX and IniFile.WriteXXX for loading and saving its values.
  • If we change the type of a variable we should remember to change the readXXX and writeXXX instructions.
  • We should be careful with literal strings. In the following situation:
    IniFile.WriteString( 'Main','LastUser', UserName);  
    ... 
    UserName := IniFile.ReadString('Main', 'LatsUser', ''); 
    

    literal strings are different, so we are writing one value and reading another.
  • Users may be able to modify the inifile (I don't like it).
  • For validations we must write procedures and functions, which means more and more code, indirect references, etc.
  • We can not store TStrings (Lines of a Memo, items of a ComboBox, Database parameters) in a direct way, we must declare more functions and/or procedures to adapt them.
  • We can not store TPoint, TFont, TRect, etc without writing more and more code, more and more validations.
  • When we change the name of a variable (UserName for LastUserName) we should also change many internal references.

    The solution is...

       Delphi objects
    NOTE: Attached demo contains full source code with more details and comments, that have been omitted in the text of this article.

    Take a look at this declaration

    TOptions = class(TComponent)   
      //.... 
      public 
        constructor Create(aOwner: TComponent); override; 
        destructor Destroy; override; 
    
        procedure ReadConfig; 
        procedure SaveConfig; 
        property ScrennRes: TPoint read GetScrennRes; 
      published 
        property NoSplash    : Boolean  read fNoSpl    write fNoSpl; 
        property UserName    : string   read FUserName write SetUserName; 
        property Lines       : TStrings read fLin      write fLin; 
        property Font        : TFont    read FFont     write SetFont; 
        property LastUse     : TDateTime read fLastUse write flastUse; 
      end; 
    

    The main idea is create a class (which inherits from TComponent) with the properties and methods needed.

       How long will our object live?
    For small and medium applications only one configuration object is needed. This object may exist from the beginning to the end of your application.

    To make sure that the object will always be available, we write at the end of the unit (before the end.)

    initialization 
      Options := TOptions.Create(nil); 
      Options.ReadConfig; 
    
    finalization 
      Options.Free; 
    //This is the "end." of the unit 
    end.
    

    This will allow us to use the object from any part of our application (even from the .dpr file! - See demo app-)

       Properties
    Now I'll show you some alternatives to declare the properties, we will use one or another depending on the restrictions that we have.
    Basic Declaration: We don't have control on what is reading or writing;
    Declaration with Restrictions: We can evaluate the values that are to be to assigned to the property, before the assignment.
    Declaration with Masks: It allows us to work with the value of the property while reading it.
    Combinations of the previous ones.

    The properties can be declared in the protected, public and published sections, only those declared inside published will be stored with the object.

    Basic Declaration
    Basic declaration means we have no control on what is assigned or how to read a property value. Read and write sections refers to an internal variable:

    property NoSplash : Boolean read fNoSplash write fNoSplash;
    

    (pressing Ctrl + Shift + C, Delphi generates a private variable FnoSplash: boolean)

    When we write:
    Options.NoSplash := False;
    Ch.checked := Options.NoSplash;

    the compiler translates it in:
    Options.FNoSplash := False;
    ch.checked := Options.FNoSplash;

    Declaring properties with restrictions (write)
    Here we have changed the write section, now it refers to a private procedure named SetLines

     property Lines : TStrings read fLines write SetLines;
    

    (pressing Ctrl + Shift + C, Delphi generates a private variable fLines: TStrings and a private procedure SetLines with its body)

    procedure TOptions.SetLines(const Value: TStrings);  
    begin  
     //Never use fLines := Value;
     fLines.Assign(Value);   
    end;  
    

    Note: Assigning fLines := Value would overwrite the internal pointer for FLines, lose memory, and create a number of ownership problems.

    Note: This type of declarations of properties should ALWAYS be used to make a local copy of objects (any TObject descendant). For objects we usually will use Assign();

    The Value parameter contains the new value that is about to be assigned to the property as a result of an assignment. We can check and manipulate the new value before completing the assignment. Also we can change other properties, call other procedures and functions, etc. (--See UserName property in the attached demo--)

    When we write:
    Options.Lines := Memo1.Lines;
    cbx.Lines := Options.Lines;

    the compiler translates it in:
    Options.SetLines(Memo1.Lines);
    cbx.Lines := Options.FLines;

    Declaring properties with masks (read)
    Here we have changed the read section, now it refers to a private function named GetColor

     property Color : Integer read GetColor write fColor;
    

    (pressing Ctrl + Shift + C, Delphi generates a private variable fColor: Integer and a private function GetColor with their body)

     function TOptions.GetColor: Integer;  
     begin  
       Result := ABS(fColor);  
     end;  
    

    When execution of the function terminates, whatever value was last assigned to Result or to the function name becomes the function's return value. We must always set a function's return value. If execution terminates without an assignment being made to Result or the function name, then the function's return value is undefined.

    When we write:
    Options.Color := Panel1.Color;
    Label1.Color := Options.Color;

    the compiler translates it in:
    Options.FColor := Panel1.Color;
    Label1.Color := Options.GetColor;

       Storing the properties
    Only the properties defined in the published section will be stored. Delphi knows how to save and load the following types : Integer, Real, Char, Boolean, TStrings, TFont, and similar (TDateTime, Tcolor, Ansistring, etc).
    For special types (TRect, TPoint, Custom objects) whe should indicate to Delphi what and how to write (and read) them.

    Step 1
    In the protected section we should add:

    procedure DefineProperties(Filer: TFiler); override;
    

    Next, we implement the procedure as follows

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

    ReadRect and WriteRect are private or protected procedures of our class. For the DefineProperty method see Delphi help for additional information.

    Step 2
    Add this in the private or protected section

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

    (Pressing Ctrl + Shift + C Delphi generates the body for both procedures)

    Step 3
    We implement the procedures to write...

    procedure TOptions.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; 
    end;
    

    ... and read our property

    procedure TOptions.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; 
    end; 
    

    NOTE: When it is possible, always use internal variables (FARect) instead of referencing properties (ARect) in a direct way.

       Creating properties that are Objects
    When a property is an object we must create it when the component is created. We must override the constructor (in public section)

    Also we can initialize internal variables.

    constructor Create(aOwner: TComponent); override;
    

    Implementing ...

    constructor TOptions.Create(aOwner: TComponent); 
    begin 
      inherited Create(aOwner);   
      fLines   := TStringList.Create; 
    //NEVER fLines:= TStrings.Create; since TStrings is abstract 
      fFont    := TFont.Create; 
      fLastUse := Now; 
      fColor   := 65535;//clYellow 
    //... 
    end; 
    

    What we create, we should destroy it:

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

       Reading and storing our Object
    Our component inherits from TComponent for these reasons:

    · WriteComponentResFile() and ReadComponentResFile()
    · ComponentToString () and StringToComponent()

    To store and to read the object we will use public procedures

    procedure SaveConfig; 
    procedure ReadConfig; 
    

    Implementing...

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

    If we want to use the Windows Registry we should make some small changes:

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

    (ComponentToString and StringToComponent are implemented in the attached demo and they were taken from delphi help)

    Observe the attached code for more information. (It contains explanatory comments)

  • All graphics (if any) in this feature created by Zarko Gajic.

     More Delphi
    · Learn another routine every day - RTL Quick Reference.
    · Download free source code applications and components.
    · Talk about Delphi Programming, real time.
    · Link to the Delphi Programming site from your Web pages.
    · Tutorials, articles, tech. tips by date: 2001|2000|1999|1998 or by TOPIC.
    · NEXT ARTICLE: More articles.
    Delphi programming articles by date...
     Stay informed with all new and interesting things about Delphi (for free).
    Subscribe to the Newsletter
    Name
    Email

     Got some code to share? Got a question? Need some help?

    ©2014 About.com. All rights reserved.