disqlite3_buffered_grid_form_main.pas

来自「DELPHI 访问SQLITE3 数据库的VCL控件」· PAS 代码 · 共 606 行 · 第 1/2 页

PAS
606
字号
{ This project shows how to manage data retrieved from a DISQLite3 database
  in a grid like fashion. It uses a cache mechanism to buffer a certain number
  of database rows and reduce the number of database reads during data display.
  This keeps memory requirements low and increases performance.

  The grid is realized by a TVirtualStringTree, an open source treeview control
  which can work both as a tree as well as a grid. TVirtualStringTree is part
  of the Virtual Trees package available from:

    http://www.soft-gems.net

  This demo accesses the Demo.db3 database and displays the RandomTable data.

  You must compile and run the DISQLite3_Bind_Params example project to create
  the Demo.db3 database and fill the RandomTable before you run this project.

  Visit the DISQLite3 Internet site for latest information and updates:

    http://www.yunqa.de/delphi/

  Copyright (c) 2005-2007 Ralf Junker, The Delphi Inspiration <delphi@yunqa.de>

------------------------------------------------------------------------------ }

unit DISQLite3_Buffered_Grid_Form_Main;

{$I DI.inc}
{$I DISQLite3.inc}

interface

uses
  Windows, SysUtils, Classes, Graphics, Controls, Forms, ToolWin, StdCtrls, ComCtrls, ExtCtrls,
  VirtualTrees, // Open source from http://www.soft-gems.net, read introduction above.
  DISQLite3Api, DISQLite3Cache;

type

  { Type to store the data as returned from one table row. }
  TRowData = record
    RandomText: AnsiString;
    RandomInt: AnsiString;
  end;
  PRowData = ^TRowData;

  //------------------------------------------------------------------------------

  { Customized cache class which initializes and finalizes TRowData as defined
    above. }
  TOurCache = class(TDISQLite3Cache)
  protected
    { Virtual method called to initialize an item which is no longer being used. }
    procedure DoInitializeItem(const AItem: Pointer); override;
    { Virtual method called to finalize an item which is no longer being used. }
    procedure DoFinalizeItem(const AItem: Pointer); override;
  end;

  //------------------------------------------------------------------------------

  { The main form. }
  TfrmMain = class(TForm)
    vt: TVirtualStringTree;
    ToolBar: TToolBar;
    btnReload: TToolButton;
    StatusBar: TStatusBar;
    timer: TTimer;
    btnEdit: TToolButton;
    btnDelete: TToolButton;
    btnAdd: TToolButton;
    cbxMemory: TCheckBox;
    procedure Form_Create(Sender: TObject);
    procedure Form_Destroy(Sender: TObject);
    procedure btnReload_Click(Sender: TObject);
    procedure Timer_Timer(Sender: TObject);
    procedure btnAdd_Click(Sender: TObject);
    procedure btnDelete_Click(Sender: TObject);
    procedure btnEdit_Click(Sender: TObject);
    procedure cbxMemory_Click(Sender: TObject);
    procedure vt_FocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
    procedure vt_GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
    procedure vt_HeaderDragging(Sender: TVTHeader; Column: TColumnIndex; var Allowed: Boolean);
    procedure vt_NewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: WideString);
    procedure vt_BeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect);
    procedure vt_MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; x, y: Integer);
  private
    FCache: TOurCache;
    FDatabaseReads: Cardinal;
    FReadDataStmt: TDISQLite3StatementHandle;
  protected
    function AddRow: Int64;
    procedure DeleteRow(const AID: Int64);
    procedure LoadTable;
    function ReadRow(const AID: Int64): PRowData;
    procedure WriteRow_Int(const AID: Int64; const AInt: Integer);
    procedure WriteRow_Text(const AID: Int64; const AText: AnsiString);
  end;

const
  APP_TITLE = 'DISQLite3 Buffered Grid Demo';

var
  frmMain: TfrmMain;

implementation

uses
  Dialogs,
  DISQLite3_Demos_Common;

{$R *.dfm}

type
  { The data type associated with each node of the VirtualStringTree:
    Stores a RowID returned from DISQLite3. }
  TNodeData = Int64;
  PNodeData = ^TNodeData;

procedure TfrmMain.Form_Create(Sender: TObject);
begin
  Caption := APP_TITLE;

  { Set up some VirtualStringTree properties to have it work as a grid.
    This is of course also possible using the Object Inspector, but
    doing so in code allows for some comments. }

  vt.DefaultText := '';
  { Allocated memory with each node to store the RowID of a single table record. }
  vt.NodeDataSize := SizeOf(TNodeData);

  { Add the columns. }
  with vt.Header.Columns.Add do
    begin
      Alignment := taRightJustify;
      Options := Options + [coFixed];
      Text := 'RowID';
      Width := 60;
    end;
  with vt.Header.Columns.Add do
    begin
      Text := 'RandomText';
      Width := 200;
    end;
  with vt.Header.Columns.Add do
    begin
      Alignment := taRightJustify;
      Text := 'RandomInt';
      Width := 100;
    end;

  { Focusing column 0 is not allowd, so focus on 1st column. }
  vt.FocusedColumn := 1;

  { Show the header. }
  vt.Header.Options := vt.Header.Options + [hoVisible];

  vt.TreeOptions.MiscOptions := vt.TreeOptions.MiscOptions +
    [toEditable, toGridExtensions];
  vt.TreeOptions.PaintOptions := vt.TreeOptions.PaintOptions -
    [toShowRoot, toShowTreeLines];
  vt.TreeOptions.PaintOptions := vt.TreeOptions.PaintOptions +
    [toShowHorzGridLines, toShowVertGridLines];
  vt.TreeOptions.SelectionOptions := vt.TreeOptions.SelectionOptions +
    [toExtendedFocus];

  { Create our cache which buffers the most recently accessed data. }
  FCache := TOurCache.Create(SizeOf(TRowData));
  { Limit the amount of buffered data to keep memory requirements low.
    Larger numbers will result in fewer database reads at the cost of
    increased memory requirements. A reasonable number is about three to four
    times the number of items visible in the grid. This allows some scrolling
    without reading the database. }
  FCache.MaxCount := 256;

  Open_Demo_Database;
  Create_Demo_Tables;
  LoadTable;
end;

//------------------------------------------------------------------------------

procedure TfrmMain.Form_Destroy(Sender: TObject);
begin
  { Finalize the prepared statement before closing the database. }
  sqlite3_check(sqlite3_finalize(FReadDataStmt), DB);
  Close_Demo_Database;
  FCache.Free;
end;

//------------------------------------------------------------------------------

procedure TfrmMain.LoadTable;
var
  Stmt: TDISQLite3StatementHandle;
  Node: PVirtualNode;
  NodeData: PNodeData;
  TC: Cardinal;
begin
  { Prevent the grid from updating while adding new records. }
  vt.BeginUpdate;
  try
    { Clear items from the Virtual String Tree. }
    vt.Clear;
    { Invalidate our cache. This will delete all items without actually freeing
      their memory. The memory will be reused when adding new items. }
    FCache.Invalidate;
    FDatabaseReads := 0;

    { Simple time measurement. }
    TC := GetTickCount;

    { Prepare an SQL statement which retrieves all records and returns the RowID
      for each of them. The block below adds all records to the grid and stores
      the RowID with each node. The grid does reuse the RowID during painting
      to retrieve the associated record data from the cache. }
    sqlite3_check(sqlite3_prepare(
      DB, // Database handle.
      'SELECT RowID FROM RandomTable;', // SQL to prepare.
      -1, // Length of SQL or -1 if null-terminated.
      @Stmt, // Pointer to store statement to.
      nil), DB);
    try
      { Iterate all matching records of the SQL query. }
      while sqlite3_check(sqlite3_step(Stmt), DB) = SQLITE_ROW do
        begin
          { Add a new row to the grid. }
          Node := vt.AddChild(nil);
          { Retrieve the data associated with that row. }
          NodeData := vt.GetNodeData(Node);
          { Retrieve the RowID from the database and store it to the row's data. }
          NodeData^ := sqlite3_column_int64(Stmt, 0);
        end;
    finally
      sqlite3_check(sqlite3_finalize(Stmt), DB);
    end;

    { Simple time measurement. }
    TC := GetTickCount - TC;
    StatusBar.Panels[0].Text :=
      Format('%d rows loaded in %d ms', [vt.RootNodeCount, TC]);

      if not assigned (FReadDataStmt) then
try  { Prepare a statment to read a row of data from the database. This will
    be used if the data is not already in the cache. }
  sqlite3_check(sqlite3_prepare(
    DB, // Database handle.
    'SELECT RandomText, RandomInt FROM RandomTable WHERE RowID=?;', // SQL to prepare.
    -1, // Length of SQL or -1 if null-terminated.
    @FReadDataStmt, // Pointer to store statement to.
    nil), DB);
   except
   freaddatastmt := nil;
   raise;
   end;
  finally
    vt.EndUpdate;
  end;
end;

//------------------------------------------------------------------------------

function TfrmMain.AddRow: Int64;
var
  Stmt: TDISQLite3StatementHandle;
begin
  sqlite3_check(sqlite3_prepare(
    DB, // Database handle.
    'INSERT INTO RandomTable VALUES (NULL, NULL);', // SQL to prepare.
    -1, // Length of SQL or -1 if null-terminated.
    @Stmt, // Pointer to store statement to.
    nil), DB);
  try
    { Invalidate this item in the cache. This removes the record from the cache
      but does not free its memory which can be reused later. }
    sqlite3_check(sqlite3_step(Stmt), DB);
    Result := sqlite3_last_insert_rowid(DB);
  finally
    sqlite3_check(sqlite3_finalize(Stmt), DB);
  end;
end;

//------------------------------------------------------------------------------

procedure TfrmMain.DeleteRow(const AID: Int64);
var
  Stmt: TDISQLite3StatementHandle;
begin
  sqlite3_check(sqlite3_prepare(
    DB, // Database handle.
    'DELETE FROM RandomTable WHERE RowID=?;', // SQL to prepare.
    -1, // Length of SQL or -1 if null-terminated.
    @Stmt, // Pointer to store statement to.
    nil), DB);
  try
    { Invalidate this item in the cache. This removes the record from the cache
      but does not free its memory which can be reused later. }
    FCache.InvalidateItem(AID);
    sqlite3_check(sqlite3_bind_int64(Stmt, 1, AID), DB);
    sqlite3_check(sqlite3_step(Stmt), DB);
  finally
    sqlite3_check(sqlite3_finalize(Stmt), DB);
  end;
end;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?