Internal types and scoping

Internal types and scoping

The code below shows something that is a bit of a mystery to me.

  TQuerySetToGrid = class(TQuerySet); // simplified

  TQuerySetXML = class(TQuerySetToGrid)


    Id: TQuerySet.TFieldInteger;

    TimeStamp: TQuerySet.TFieldDateTime;

    PID: TQuerySet.TFieldInteger;

    TID: TQuerySet.TFieldInteger;

    Level: TQuerySet.TFieldEnum;

    Category: TQuerySet.TFieldEnum;

    App: TQuerySet.TFieldString;

    Client: TQuerySet.TFieldString;

    Title: TQuerySet.TFieldString;

    constructor Create; override;


Apart from “it works that way” – is there a reasonable explanation of why I have to qualify the TFieldxxxx types which all are public types declared inside TQuerySet, when referring the types inside a descendant class? I wonder!


FYI, this is actual code and part of a log viewer in our monitoring and maintenance tool, and to give you an idea of what I am trying to achieve…

This is the scaffolding code needed for link a grid and a query to display the results from a TDataSet.

Initialize and set up the look of the grid:

  XMLQueryLog := TQuerySetXML.Create;

  XMLQueryLog.Grid := sgLog;

sgLog is a TAdvStringGrid dropped on a frame, and there is some grid specific code to get the desired look, and ofcourse code to handle grid operations – but for the content, this is the only scaffolding needed.

Execute the query and fill the grid:


The “real” scaffolding happens in that Create you saw in the initial class.

constructor TQuerySetXML.Create;



  Id := Add(‘Id’, 64);

  TimeStamp := Add(‘TimeStamp’, ‘Dato og tid’, 120);

  PID := Add(‘ProcessId’, ‘PID’, 64);

  TID := Add(‘ThreadId’, ‘TID’, 64);

  Level := Add<TFieldEnum>(‘XLevel’, ‘Nivå’, 70);

  Level.Formatter :=

    function (const Lvl: TLogEventLevel):String


      Result := LogEventLevelToString(Lvl);


  Category := Add<TFieldEnum>(‘XCategory’, ‘Kategori’, 70);

  Category.Formatter :=

    function (const Cat: TLogEventCategory):String


      Result := LogEventCategoryToString(Cat);


  App := Add(‘AppShortName’, ‘App’, 30);

  Client := Add(‘ClientName’, ‘Client’, 140);

  Client.HAlighment := taRightJustify;

  Title := Add(‘Title’, ‘Beskrivelse’, 800);


which is a lot less code than I needed before.

Basically, I Add(‘sqlcolumn’, ‘Grid Col Title’, width in px);

If I don’t add any field specifications to the QuerySet, they will be generated based on the query results and the name/TFieldType of each column.

The old code also read individual log objects from the database and then moved them to the grid – while this uses SQL -> Grid – so it is quite a bit faster.

I could also use owner draw and a virtual grid to avoid duplicating the query results as strings in the grid – but that would rob me of some of the nice features of TAdvStringGrid.

Right now, there are only two conversions for each cell – first DB result to native, and then native to string – and there are no case statements 😉

Still work in progress, but I am reasonably happy so far – except from that internal type scope thing?

2 thoughts on “Internal types and scoping