Here is our issue. The TdxLayoutControl
can store its configuration in three ways: an INI file, a Windows Registry, or a TStream
(which can be used to store a BLOB in the database). The INI file stores the data in a user-consumable format. The Windows Registry and TStream
options store the data in a binary format. We (being the daredevils we are) would like to have our cake and eat it too.
We would like to:
- Store
TdxLayoutControl
customization into a database BLOB field. - Store that data in a text-based INI format (or be able to retrieve/manipulate that data in a text-based format).
- Be able to specify which database we want to store/retrieve that data from (this is a multi-tenant/multi-database situation with certain applications that span tenants/databases).
Here is what we can seem to do:
- I can accomplish points 1 & 3 using
StoreToStream
.
- The challenge with this solution is that the data is stored in a binary format
- I can accomplish points 1 & 2 using
StoreToStorage
with custom overriddenTcxIniFileReader
andTcxIniFileWriter
classes that can write to the database instead of a file but can only use a global variable to get the database connection information.
- The challenge with this solution is that because the container is responsible for instantiating the
TcxIniFileReader/Writer
descendant, we cannot pass database connection parameters via anything other than theAStorageName
parameter – which gets unusable really quickly.
The options that I have thought of (and there likely are other ones) are:
- Have a way to specify that the
StoreToStream
functionality to choose which format to store it in (binary/text INI) - Have a way to pass in an instantiated
TcxIniFileReader/Writer
descendant into theStoreToStorage
method. - Have the
TcxIniFileReader/Writer
descendant class be able to access the callingTdxLayoutContainer
(we are using the tag to store an object with instantiation information). - Have some sort of parsing helper that would allow us to retrieve the binary data stored in the BLOB field, present it in INI format, and then re-parse it for storage back into the BLOB field.
If anyone has any other ideas, I’m game.
Here is the code for my mock-up of a StoreToStream solution (which does not save in an INI text format, but does save in binary).
Test.dpr
program Test;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, dxSkinsCore,
dxSkinBasic,
// DevExpress Skins omitted ...
dxLayoutcxEditAdapters, cxContainer, cxEdit, dxLayoutContainer, cxTextEdit, Data.DB,
Data.Win.ADODB, cxClasses, dxLayoutControl, Vcl.Menus, Vcl.StdCtrls, cxButtons, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
lcMainGroup_Root: TdxLayoutGroup;
lcMain: TdxLayoutControl;
cxTextEdit1: TcxTextEdit;
dxLayoutItem1: TdxLayoutItem;
cxTextEdit2: TcxTextEdit;
dxLayoutItem2: TdxLayoutItem;
cxTextEdit3: TcxTextEdit;
dxLayoutItem3: TdxLayoutItem;
cxTextEdit4: TcxTextEdit;
dxLayoutItem4: TdxLayoutItem;
dxLayoutGroup1: TdxLayoutGroup;
dxLayoutGroup2: TdxLayoutGroup;
Panel1: TPanel;
cxButton1: TcxButton;
cxButton2: TcxButton;
procedure FormCreate(Sender: TObject);
procedure cxButton1Click(Sender: TObject);
procedure cxButton2Click(Sender: TObject);
private
FADOConnection: TADOConnection;
FADOQuery: TADOQuery;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
LayoutName: string = 'TestLayout';
const
FilterFormat: string = 'Name = ''%s''';
procedure TForm1.cxButton1Click(Sender: TObject);
begin
lcMain.Customization := True;
end;
procedure TForm1.cxButton2Click(Sender: TObject);
var
LStream: TMemoryStream;
LField: TBlobField;
begin
FADOQuery.Open;
FADOQuery.Edit;
LStream := TMemoryStream.Create;
try
lcMain.Container.StoreToStream(LStream);
LField := FADOQuery.FieldByName('DataMemo') as TBlobField;
LField.LoadFromStream(LStream);
FADOQuery.Post;
finally
FADOQuery.Close;
LStream.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
LStream: TStream;
LField: TField;
begin
FADOConnection := TADOConnection.Create(Self);
FADOConnection.ConnectionString := 'Provider=SQLOLEDB.1;' + 'Integrated Security=SSPI;' +
'Persist Security Info=False;' + 'Initial Catalog=Test;' + 'Data Source=localhostSQLEXPRESS';
FADOQuery := TADOQuery.Create(Self);
FADOQuery.Connection := FADOConnection;
FADOQuery.SQL.Add('SELECT * FROM [Layouts] WHERE [Name] = ''TestLayout''');
FADOQuery.Open;
try
if not FADOQuery.FieldByName('DataMemo').IsNull then
begin
LField := FADOQuery.FieldByName('DataMemo');
LStream := FADOQuery.CreateBlobStream(LField, bmRead);
lcMain.Container.RestoreFromStream(LStream);
end;
finally
FADOQuery.Close;
end;
end;
end.
Unit1.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 441
ClientWidth = 624
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
OnCreate = FormCreate
TextHeight = 15
object lcMain: TdxLayoutControl
Left = 0
Top = 0
Width = 624
Height = 384
Align = alClient
TabOrder = 0
object cxTextEdit1: TcxTextEdit
Left = 92
Top = 33
Style.BorderColor = clWindowFrame
Style.BorderStyle = ebs3D
Style.HotTrack = False
Style.TransparentBorder = False
TabOrder = 0
Text = 'cxTextEdit1'
Width = 121
end
object cxTextEdit2: TcxTextEdit
Left = 92
Top = 63
Style.BorderColor = clWindowFrame
Style.BorderStyle = ebs3D
Style.HotTrack = False
Style.TransparentBorder = False
TabOrder = 1
Text = 'cxTextEdit2'
Width = 121
end
object cxTextEdit3: TcxTextEdit
Left = 92
Top = 128
Style.BorderColor = clWindowFrame
Style.BorderStyle = ebs3D
Style.HotTrack = False
Style.TransparentBorder = False
TabOrder = 2
Text = 'cxTextEdit3'
Width = 121
end
object cxTextEdit4: TcxTextEdit
Left = 92
Top = 158
Style.BorderColor = clWindowFrame
Style.BorderStyle = ebs3D
Style.HotTrack = False
Style.TransparentBorder = False
TabOrder = 3
Text = 'cxTextEdit4'
Width = 121
end
object lcMainGroup_Root: TdxLayoutGroup
AlignHorz = ahLeft
AlignVert = avTop
Hidden = True
ItemIndex = 1
ShowBorder = False
Index = -1
end
object dxLayoutItem1: TdxLayoutItem
Parent = dxLayoutGroup1
CaptionOptions.Text = 'cxTextEdit1'
Control = cxTextEdit1
ControlOptions.OriginalHeight = 23
ControlOptions.OriginalWidth = 121
ControlOptions.ShowBorder = False
Index = 0
end
object dxLayoutItem2: TdxLayoutItem
Parent = dxLayoutGroup1
CaptionOptions.Text = 'cxTextEdit2'
Control = cxTextEdit2
ControlOptions.OriginalHeight = 23
ControlOptions.OriginalWidth = 121
ControlOptions.ShowBorder = False
Index = 1
end
object dxLayoutItem3: TdxLayoutItem
Parent = dxLayoutGroup2
CaptionOptions.Text = 'cxTextEdit3'
Control = cxTextEdit3
ControlOptions.OriginalHeight = 23
ControlOptions.OriginalWidth = 121
ControlOptions.ShowBorder = False
Index = 0
end
object dxLayoutItem4: TdxLayoutItem
Parent = dxLayoutGroup2
CaptionOptions.Text = 'cxTextEdit4'
Control = cxTextEdit4
ControlOptions.OriginalHeight = 23
ControlOptions.OriginalWidth = 121
ControlOptions.ShowBorder = False
Index = 1
end
object dxLayoutGroup1: TdxLayoutGroup
Parent = lcMainGroup_Root
CaptionOptions.Text = 'New Group'
Index = 0
end
object dxLayoutGroup2: TdxLayoutGroup
Parent = lcMainGroup_Root
CaptionOptions.Text = 'New Group'
ItemIndex = 1
Index = 1
end
end
object Panel1: TPanel
Left = 0
Top = 384
Width = 624
Height = 57
Align = alBottom
Caption = 'Panel1'
ShowCaption = False
TabOrder = 1
object cxButton1: TcxButton
Left = 16
Top = 16
Width = 75
Height = 25
Caption = 'Customize'
TabOrder = 0
OnClick = cxButton1Click
end
object cxButton2: TcxButton
Left = 488
Top = 16
Width = 123
Height = 25
Caption = 'Save Customization'
TabOrder = 1
OnClick = cxButton2Click
end
end
end