WriteComponentResFile is not including components dynamically added to TTabSheet - delphi

I am trying to convert a set of forms that were created with a custom tool to Delphi forms. I am trying to add all the necessary components at runtime and then use WriteComponentResFile to create the DFM file.
All of my initial tests looked good until I tried adding a TPageControl and TabSheets. The current forms can have multiple pages so I was going to mirror this using the PageControl. The problem is any components I add to a TabSheet are not streamed out to the DFM. It looks good if I show the form but something is missing for WriteComponentResFile.
I am writing out a corresponding pas file so I can open this up in the IDE once they are done. The goal is to move away from the custom form designer and start to use the Delphi IDE for our form designer.
Here is some sample code showing how I am creating the components:
procedure WriteFormAsDFM(OutputFileName: string);
var
PageIndex: integer;
PageCount: Integer;
OutputForm: TForm;
Pages: TPageControl;
NewPage: TTabSheet;
NewLabel: TLabel;
begin
OutputForm := TForm.Create(nil);
OutputForm.Name := ChangeFileExt(ExtractFileName(OutputFileName), '');
OutputForm.Caption := OutputForm.Name;
OutputForm.Height := 300;
OutputForm.Width := 300;
Pages := TPageControl.Create(OutputForm);
Pages.Parent := OutputForm;
Pages.Top := 50;
Pages.Left := 0;
Pages.Height := 200;
Pages.Width := 200;
NewLabel := TLabel.Create(OutputForm);
NewLabel.Parent := OutputForm;
NewLabel.Caption := 'Label on Form';
//write pages
PageCount := 2;
for PageIndex := 0 to PageCount - 1 do
begin
NewPage := TTabSheet.Create(Pages);
NewPage.Parent := Pages;
NewPage.PageControl := Pages;
NewPage.Caption := 'Page ' + IntToStr(PageIndex);
NewPage.Name := 'tsPage' + IntToStr(PageIndex);
NewLabel := TLabel.Create(NewPage);
NewLabel.Parent := NewPage;
NewLabel.Caption := 'Label on ' + NewPage.Caption;
end;
WriteComponentResFile(OutputFileName, OutputForm);
//WritePasFile(OutputFileName, OutputForm);
OutputForm.ShowModal;
FreeAndNil(OutputForm);
end;
and here is the DFM file that is output. You can see the label on the form is created but not the labels added to the TabSheets.
object Form123: TForm
Left = 69
Top = 69
Caption = 'Form123'
ClientHeight = 264
ClientWidth = 284
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object TLabel
Left = 0
Top = 0
Width = 67
Height = 13
Caption = 'Label on Form'
end
object TPageControl
Left = 0
Top = 50
Width = 200
Height = 200
ActivePage = tsPage0.Owner
TabOrder = 0
object tsPage0: TTabSheet
Caption = 'Page 0'
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
end
object tsPage1: TTabSheet
Caption = 'Page 1'
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
end
end
end

Try to use the form as owner of the components.
NewPage := TTabSheet.Create(OutputForm);
NewLabel := TLabel.Create(OutputForm);

Related

Vertically drawn text on TCanvas not visible when drawn from initial FormResize

I'm using the DrawTextRotatedB function from Josef Ċ vejk's excellent answer to the question How to draw text in a canvas vertical + horizontal with Delphi 10.2
to draw text vertically on a TPanel (full code below, Win 32 program).
This is done in a message handler (message WM_DRAWTEXT). The PostMessage call is in the FormResize (which in the real program does a lot more).
Issue:
FormResize is called from FormShow, all relevant code is executed, but the vertical text does not show.
If I then resize the form, the same code gets executed again and it is visible.
How can this be, and how to fix it?
Structure view of form components (drawing on PnlLeftLeft):
Full test code below. Note that this logs to a text file specified by the cLogFileName constant at the top. This log contains (initial resize + one subsequent resize).
FormShow start
FormShow end
FormResize start
PostMessage left sent
FormResize end
RedrawMessage left: test text
(X,Y): (51,284)
RedrawMessage ends
FormResize start
PostMessage left sent
FormResize end
RedrawMessage left: test text
(X,Y): (51,285)
RedrawMessage ends
uFrmTest.Pas
unit uFrmTest;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Imaging.pngimage, Vcl.ExtCtrls, System.UITypes;
const
WM_DRAWTEXT = WM_USER + 100;
cLogFileName = 'd:\temp\log.lst';
type
TFrmTest = class(TForm)
PnlClient: TPanel;
PnlLeft: TPanel;
PnlRight: TPanel;
PnlLeftLeft: TPanel;
procedure FormShow(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
private
FTextFile: TextFile; // Debugging do cLogFileName
procedure RedrawMessage(var Msg: TMessage); message WM_DRAWTEXT;
public
end;
var
FrmTest: TFrmTest;
implementation
{$R *.dfm}
procedure DrawTextRotated(ACanvas: TCanvas; Angle, X, Y: Integer; AText: String);
// DrawTextRotatedB from https://stackoverflow.com/a/52923681/512728
var
Escapement: Integer;
LogFont: TLogFont;
NewFontHandle: HFONT;
OldFontHandle: HFONT;
begin
if not Assigned(ACanvas) then
Exit;
// Get handle of font and prepare escapement
GetObject(ACanvas.Font.Handle, SizeOf(LogFont), #LogFont);
while Angle > 360 do Angle := Angle - 360;
while Angle < -360 do Angle := Angle + 360;
Escapement := Angle * 10;
// We must initialise all fields of the record structure
LogFont.lfWidth := 0;
LogFont.lfHeight := ACanvas.Font.Height;
LogFont.lfEscapement := Escapement;
LogFont.lfOrientation := 0;
if fsBold in ACanvas.Font.Style then
LogFont.lfWeight := FW_BOLD
else
LogFont.lfWeight := FW_NORMAL;
LogFont.lfItalic := Byte(fsItalic in ACanvas.Font.Style);
LogFont.lfUnderline := Byte(fsUnderline in ACanvas.Font.Style);
LogFont.lfStrikeOut := Byte(fsStrikeOut in ACanvas.Font.Style);
LogFont.lfCharSet := ACanvas.Font.Charset;
LogFont.lfOutPrecision := OUT_DEFAULT_PRECIS;
LogFont.lfClipPrecision := CLIP_DEFAULT_PRECIS;
LogFont.lfQuality := DEFAULT_QUALITY;
LogFont.lfPitchAndFamily := DEFAULT_PITCH;
StrPCopy(LogFont.lfFaceName, ACanvas.Font.Name);
// Create new font with rotation
NewFontHandle := CreateFontIndirect(LogFont);
try
// Select the new font into the canvas
OldFontHandle := SelectObject(ACanvas.Handle, NewFontHandle);
try
// Output result
ACanvas.Brush.Style := VCL.Graphics.bsClear;
try
ACanvas.TextOut(X, Y, AText);
finally
ACanvas.Brush.Style := VCL.Graphics.bsSolid;
end;
finally
// Restore font handle
NewFontHandle := SelectObject(ACanvas.Handle, OldFontHandle);
end;
finally
// Delete the deselected font object
DeleteObject(NewFontHandle);
end;
end;
procedure TFrmTest.FormCreate(Sender: TObject);
begin
AssignFile(FTextFile,cLogFileName);
Rewrite(FTextFile);
end;
procedure TFrmTest.FormDestroy(Sender: TObject);
begin
CloseFile(FTextFile);
end;
procedure TFrmTest.FormResize(Sender: TObject);
begin
WriteLn(FTextFile,'FormResize start');
PostMessage(Handle,WM_DRAWTEXT,0,0);
WriteLn(FTextFile,'PostMessage left sent');
WriteLn(FTextFile,'FormResize end');
end;
procedure TFrmTest.FormShow(Sender: TObject);
begin
WriteLn(FTextFile,'FormShow start');
WriteLn(FTextFile,'FormShow end');
end;
type
THackPanel = class(TPanel);
procedure TFrmTest.RedrawMessage(var Msg: TMessage);
const cLeftVertText = 'test text';
var lHorDrawOffset, lVertDrawOffset: Integer;
begin
WriteLn(FTextFile,'RedrawMessage left: ' + cLeftVertText);
THackPanel(PnlLeftLeft).Canvas.Font := PnlLeftLeft.Font;
lVertDrawOffset := (PnlLeftLeft.Height - THackPanel(PnlLeftLeft).Canvas.TextHeight(cLeftVertText)) DIV 2;
lHorDrawOffset := (PnlLeftLeft.Width - THackPanel(PnlLeftLeft).Canvas.TextWidth(cLeftVertText)) DIV 2;
DrawTextRotated(THackPanel(PnlLeftLeft).Canvas , 90, lHorDrawOffset, lVertDrawOffset, cLeftVertText);
WriteLn(FTextFile,Format('(X,Y): (%d,%d)',[lHorDrawOffset,lVertDrawOffset]));
WriteLn(FTextFile,'RedrawMessage ends');
WriteLn(FTextFile,'');
end;
end.
uFrmTest.dfm
object FrmTest: TFrmTest
Left = 0
Top = 0
Caption = 'FrmTest'
ClientHeight = 592
ClientWidth = 905
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
OnResize = FormResize
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object PnlClient: TPanel
Left = 0
Top = 0
Width = 905
Height = 592
Align = alClient
BevelOuter = bvNone
ParentColor = True
TabOrder = 0
ExplicitLeft = 88
ExplicitTop = 8
object PnlLeft: TPanel
AlignWithMargins = True
Left = 20
Top = 10
Width = 367
Height = 582
Margins.Left = 20
Margins.Top = 10
Margins.Right = 20
Margins.Bottom = 0
Align = alLeft
BevelOuter = bvNone
ParentColor = True
TabOrder = 0
object PnlLeftLeft: TPanel
Tag = -20
Left = 0
Top = 0
Width = 145
Height = 582
Align = alLeft
BevelOuter = bvNone
ParentColor = True
TabOrder = 0
end
end
object PnlRight: TPanel
AlignWithMargins = True
Left = 427
Top = 10
Width = 458
Height = 582
Margins.Left = 20
Margins.Top = 10
Margins.Right = 20
Margins.Bottom = 0
Align = alClient
BevelOuter = bvNone
TabOrder = 1
end
end
end

How to have an absolute row between two percentage rows when adding to a TGridPanel from code?

I am trying to create a Form with a TGridPanel from code.
It contains:
A memo at the top (which is set to 50%)
A navigator at the center (which is set to 24 pixels)
A grid at the bottom (which is set to 50%)
This is the code I wrote:
uses
Winapi.Messages, Winapi.Windows, System.Classes, System.SysUtils,
System.UITypes, System.Variants, Vcl.Controls, Vcl.DBCtrls, Vcl.DBGrids,
Vcl.Dialogs, Vcl.ExtCtrls, Vcl.Forms, Vcl.Graphics, Vcl.Grids, Vcl.StdCtrls,
Data.DB;
procedure Test;
var
View: TForm;
GridPanel: TGridPanel;
Grid: TDBGrid;
DataSource: TDataSource;
Navigator: TDBNavigator;
Memo: TMemo;
begin
View := TForm.Create(Application);
try
View.Name := 'Form2';
// SystemFont(View.Font);
View.Width := 640;
View.Height := 480;
View.Position := TPosition.poOwnerFormCenter;
GridPanel := TGridPanel.Create(View);
GridPanel.Name := 'GridPanel';
GridPanel.Caption := '';
GridPanel.BevelOuter := TBevelCut.bvNone;
GridPanel.FullRepaint := False;
GridPanel.Parent := View;
GridPanel.Align := TAlign.alClient;
GridPanel.ColumnCollection.BeginUpdate;
GridPanel.ColumnCollection.Delete(1);
GridPanel.ColumnCollection[0].Value := 100;
GridPanel.ColumnCollection.EndUpdate;
GridPanel.RowCollection.BeginUpdate;
GridPanel.RowCollection.Add;
TCellItem(GridPanel.RowCollection[0]).Value := 50;
TCellItem(GridPanel.RowCollection[0]).SizeStyle := TSizeStyle.ssPercent;
TCellItem(GridPanel.RowCollection[1]).Value := 24;
TCellItem(GridPanel.RowCollection[1]).SizeStyle := TSizeStyle.ssAbsolute;
TCellItem(GridPanel.RowCollection[2]).Value := 50;
TCellItem(GridPanel.RowCollection[2]).SizeStyle := TSizeStyle.ssPercent;
GridPanel.RowCollection.EndUpdate;
Memo := TMemo.Create(View);
Memo.Name := 'Memo';
Memo.Parent := GridPanel;
Memo.Lines.Clear;
Memo.Align := TAlign.alClient;
DataSource := TDataSource.Create(View);
Navigator := TDBNavigator.Create(View);
Navigator.Name := 'Navigator';
Navigator.DataSource := DataSource;
Navigator.Parent := GridPanel;
Navigator.Align := TAlign.alClient;
Grid := TDBGrid.Create(View);
Grid.Name := 'Grid';
Grid.Parent := GridPanel;
Grid.Align := TAlign.alClient;
Grid.DataSource := DataSource;
{
GridPanel.ControlCollection.BeginUpdate;
GridPanel.ControlCollection.AddControl(Memo, 0, 0);
GridPanel.ControlCollection.AddControl(Navigator, 0, 1);
GridPanel.ControlCollection.AddControl(Grid, 0, 2);
GridPanel.ControlCollection.EndUpdate;
}
// ShowMessage(ComponentToString(View));
View.ShowModal;
finally
View.Free;
end;
end
The result looks like this:
The Problem: There is a gap at the bottom of the form and no DB navigator to be seen!
A dump of the DFM looks fine to me:
object Form2: TForm
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 441
ClientWidth = 624
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poOwnerFormCenter
PixelsPerInch = 96
TextHeight = 13
object GridPanel: TGridPanel
Left = 0
Top = 0
Width = 624
Height = 441
Align = alClient
BevelOuter = bvNone
ColumnCollection = <
item
Value = 100.000000000000000000
end>
ControlCollection = <
item
Column = 0
Control = Memo
Row = 0
end
item
Column = 0
Control = Navigator
Row = 1
end
item
Column = 0
Control = Grid
Row = 2
end>
FullRepaint = False
RowCollection = <
item
Value = 50.000000000000000000
end
item
SizeStyle = ssAbsolute
Value = 24.000000000000000000
end
item
Value = 50.000000000000000000
end>
TabOrder = 0
object Memo: TMemo
Left = 0
Top = 0
Width = 624
Height = 208
Align = alClient
TabOrder = 0
ExplicitLeft = 219
ExplicitTop = 59
ExplicitWidth = 185
ExplicitHeight = 89
end
object Navigator: TDBNavigator
Left = 0
Top = 208
Width = 624
Height = 18
Align = alClient
TabOrder = 1
ExplicitTop = 0
ExplicitWidth = 240
ExplicitHeight = 25
end
object Grid: TDBGrid
Left = 0
Top = 208
Width = 624
Height = 209
Align = alClient
TabOrder = 2
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
end
object TDataSource
end
end
When I change the position, the Navigator is at the correct position, but I want it to be absolute.
TCellItem(GridPanel.RowCollection[1]).Value := 5;
TCellItem(GridPanel.RowCollection[1]).SizeStyle := TSizeStyle.ssPercent;
Why does TGridPanel behave so strange in this case? What can I do about it?
Change the order of setting SizeStyle and Value.
TCellItem(GridPanel.RowCollection[0]).SizeStyle := TSizeStyle.ssPercent;
TCellItem(GridPanel.RowCollection[0]).Value := 50;
TCellItem(GridPanel.RowCollection[1]).SizeStyle := TSizeStyle.ssAbsolute;
TCellItem(GridPanel.RowCollection[1]).Value := 24;
TCellItem(GridPanel.RowCollection[2]).SizeStyle := TSizeStyle.ssPercent;
TCellItem(GridPanel.RowCollection[2]).Value := 50;

how do i align a tpanel to a very bottom of listview item

what i try to do is same as this image
thing i have tried panel1.top := ListView1.Items[i].position.Y;
but it didnt success with this trick , is there possibly way to aligned Tpanel at a bottom of some items
Actual code added
procedure Ttestthreading.streamClick(Sender: TObject);
var
i, R: integer;
begin
if stream.Caption = 'stream' then
begin
for i := 0 to ListView1.Items.Count - 1 do
if ListView1.Items[i].SubItems[3] = IntToStr(UniqueID) then
begin
R := ListView1.Items[i].Index;
panel2.Top := ListView1.Items[i].Position.Y;
end;
ExchangeItems(ListView1, R, 0);
stream.Caption := 'stopstream';
panel2.Visible := true;
// start stream
end
else if stream.Caption = 'stopstream' then
begin
ExchangeItems(ListView1, R, 0);
stream.Caption := 'stream';
panel2.Visible := false;
// stopstream
end;
end;
If you check the documentation http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.ComCtrls.TListItem.Position you will see that TListitem.Position only works when ListView view style is either vsIcon or vsSmallIcon.
So instead of using Position property you should rather use DisplayRect method http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.ComCtrls.TListItem.DisplayRect which returns the rectangle in which the List item is rendered.
Take a new form, copy this code and paste it onto the form
object ListView1: TListView
Left = 0
Top = 40
Width = 250
Height = 296
Anchors = [akLeft, akTop, akRight, akBottom]
Columns = <>
TabOrder = 0
end
object Panel1: TPanel
Left = 0
Top = 0
Width = 250
Height = 41
Anchors = [akLeft, akTop, akRight]
Caption = 'Panel1'
TabOrder = 1
end
First I arrange the two objects at designtime. Then I set anchors on both objects.

How to manage constantly changing project run-time parameters in the IDE?

In my (XE2) IDE I constantly have to switch the settings for Project Options/ Debugger / Parameters because I'm testing for different client configurations, databases etc.
The Parameters dropdown list is becoming unmanageable. Since these have no descriptions either, it's even hard to figure out which ones to remove (How can I clean the Parameters field in the Run -> Parameters menu?).
Any smart ideas on managing these?
In an ideal word I would like to give them a tag/description, reorder them, delete some...
Not ideal but a workaround would be to add a redundant tag parameter as first parameter.
That way at least, when you use the dropdown list, you'll have some indication on what parameter combination you are using.
In addition to Lievens answer I'm adding an answer to my own question.
I have started using [identifier] at the beginning of the run-time parameter, and I can put in there what I want. In addition I have written a small app that lets me clean up the parameters that are stored in the registry (at HKEY_CURRENT_USER\Software\Embarcadero\BDS\9.0\History Lists\hlRunParameters).
Here is the code for that Win32 app. It will let you delete and sort the registry values. It is written in Delphi XE2. Just create a new VCL project and use this as the main form.
uCleanIDEParams.pas
unit uCleanIDEParams;
// https://stackoverflow.com/questions/27502689/how-to-manage-constantly-changing-project-run-time-parameters-in-the-ide
// All 32 bit stuff, the Delphi IDE is 32 bit too
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.CheckLst, Registry,
Vcl.ExtCtrls;
type
TFrmCleanIDEParams = class(TForm)
PnlTop: TPanel;
BtnLoad: TButton;
EdtRegKey: TEdit;
Label1: TLabel;
ChkRedundant: TCheckBox;
PnlBottom: TPanel;
GBxSelection: TGroupBox;
BtnSelectAll: TButton;
BtnSelectNone: TButton;
BtnStartWith: TButton;
ChkNot: TCheckBox;
EdtStartWith: TEdit;
BtnInvert: TButton;
Label2: TLabel;
BtnWrite: TButton;
ChkSort: TCheckBox;
PnlCenter: TPanel;
CLBParams: TCheckListBox;
procedure BtnLoadClick(Sender: TObject);
procedure BtnSelectAllClick(Sender: TObject);
procedure BtnSelectNoneClick(Sender: TObject);
procedure BtnInvertClick(Sender: TObject);
procedure BtnStartWithClick(Sender: TObject);
procedure BtnWriteClick(Sender: TObject);
private
public
end;
var
FrmCleanIDEParams: TFrmCleanIDEParams;
implementation
{$R *.dfm}
procedure TFrmCleanIDEParams.BtnInvertClick(Sender: TObject);
var i: integer;
begin
for i := 0 to CLBParams.Items.Count-1 do CLBParams.Checked[i] := not CLBParams.Checked[i];
end;
procedure TFrmCleanIDEParams.BtnLoadClick(Sender: TObject);
var
lReg : TRegistry;
lValue,
lItem,
lKey : String;
i,
lNrRegVals: Integer;
begin
lKey := Trim(EdtRegKey.Text);
if lKey = '' then Exit;
if lKey[1] = '\' then lKey := Copy(lKey,2);
lReg := TRegistry.Create(KEY_READ);
lReg.RootKey := HKEY_CURRENT_USER;
if not lReg.OpenKey(lKey,false) then
begin
MessageDlg('Key not found', mtError, mbOKCancel, 0);
Exit;
end;
if not lReg.ValueExists('Count') then
begin
MessageDlg('Value ''Count'' not found', mtError, mbOKCancel, 0);
Exit;
end;
lNrRegVals := lReg.ReadInteger('Count');
for i := 0 to lNrRegVals-1 do
begin
lValue := 'Item' + IntToStr(i);
if lReg.ValueExists(lValue) then
begin
lItem := lReg.ReadString(lValue);
if ChkRedundant.Checked then
lItem := Trim(StringReplace(lItem,' ',' ',[rfReplaceAll]));
CLBParams.Items.Add(lItem);
end;
end;
end;
procedure TFrmCleanIDEParams.BtnSelectAllClick(Sender: TObject);
var i: integer;
begin
for i := 0 to CLBParams.Items.Count-1 do CLBParams.Checked[i] := true;
end;
procedure TFrmCleanIDEParams.BtnSelectNoneClick(Sender: TObject);
var i: integer;
begin
for i := 0 to CLBParams.Items.Count-1 do CLBParams.Checked[i] := true;
end;
procedure TFrmCleanIDEParams.BtnStartWithClick(Sender: TObject);
var
i : integer;
lStart,
lItem : string;
begin
lStart := Lowercase(Trim(EdtStartWith.Text));
if lStart = '' then Exit;
for i := 0 to CLBParams.Items.Count-1 do
begin
lItem := lowercase(CLBParams.Items[i]);
if (not ChkNot.Checked) and (Pos(lStart,lItem) = 1)
or (ChkNot.Checked) and (Pos(lStart,lItem) <> 1) then
CLBParams.Checked[i] := true;
end;
end;
procedure TFrmCleanIDEParams.BtnWriteClick(Sender: TObject);
var
lReg : TRegistry;
lValue,
lKey : String;
i,
lNrToWrite,
lNrRegVals: Integer;
begin
for i := CLBParams.Items.Count-1 downto 0 do
if not CLBParams.Checked[i] then
CLBParams.Items.Delete(i);
if CLBParams.Items.Count = 0 then
begin
MessageDlg('Nothing to do', mtInformation, mbOKCancel, 0);
Exit;
end;
if ChkSort.Checked then
CLBParams.Sorted := true;
// Now writing back
lKey := Trim(EdtRegKey.Text);
if lKey = '' then Exit;
if lKey[1] = '\' then lKey := Copy(lKey,2);
lReg := TRegistry.Create(KEY_READ or KEY_WRITE);
lReg.RootKey := HKEY_CURRENT_USER;
if not lReg.OpenKey(lKey,false) then
begin
MessageDlg('Key not found', mtError, mbOKCancel, 0);
Exit;
end;
if not lReg.ValueExists('Count') then
begin
MessageDlg('Value ''Count'' not found', mtError, mbOKCancel, 0);
Exit;
end;
lNrRegVals := lReg.ReadInteger('Count');
lNrToWrite := CLBParams.Items.Count;
lReg.WriteInteger('Count',lNrToWrite);
// Write TCheckListBox items:
for i := 0 to lNrToWrite-1 do
begin
lValue := 'Item' + IntToStr(i);
lReg.WriteString(lValue,CLBParams.Items[i]);
end;
// Remove the rest:
for i := lNrToWrite to lNrRegVals-1 do
lReg.DeleteValue('Item' + IntToStr(i));
end;
end.
uCleanIDEParams.dfm
object FrmCleanIDEParams: TFrmCleanIDEParams
Left = 0
Top = 0
Caption = 'Clean Delphi IDE runtime parameters'
ClientHeight = 560
ClientWidth = 549
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object PnlTop: TPanel
Left = 0
Top = 0
Width = 549
Height = 58
Align = alTop
BevelOuter = bvNone
TabOrder = 0
object Label1: TLabel
Left = 19
Top = 11
Width = 308
Height = 13
Caption = 'HKEY_CURRENT_USER registry key for IDE runtime parameters:'
end
object BtnLoad: TButton
Left = 496
Top = 30
Width = 40
Height = 25
Caption = 'Load'
TabOrder = 0
OnClick = BtnLoadClick
end
object EdtRegKey: TEdit
Left = 16
Top = 32
Width = 473
Height = 21
TabOrder = 1
Text = '\Software\Embarcadero\BDS\9.0\History Lists\hlRunParameters'
end
object ChkRedundant: TCheckBox
Left = 388
Top = 10
Width = 151
Height = 17
Hint = 'Removes leading, trailing, and duplicate spaces'
Caption = 'Remove redundant spaces'
Checked = True
State = cbChecked
TabOrder = 2
end
end
object PnlBottom: TPanel
Left = 0
Top = 471
Width = 549
Height = 89
Align = alBottom
BevelOuter = bvNone
TabOrder = 1
object Label2: TLabel
Left = 74
Top = 62
Width = 287
Height = 13
Caption = 'Click button to write the selected items back to the registry:'
end
object GBxSelection: TGroupBox
Left = 16
Top = -3
Width = 520
Height = 51
Caption = ' Select '
TabOrder = 0
object BtnSelectAll: TButton
Left = 348
Top = 18
Width = 50
Height = 25
Caption = 'All'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 0
OnClick = BtnSelectAllClick
end
object BtnSelectNone: TButton
Left = 404
Top = 18
Width = 50
Height = 25
Caption = 'None'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 1
OnClick = BtnSelectNoneClick
end
object BtnStartWith: TButton
Left = 51
Top = 18
Width = 79
Height = 25
Hint = 'Selection is additive'
Caption = 'starting with:'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
ParentShowHint = False
ShowHint = True
TabOrder = 2
OnClick = BtnStartWithClick
end
object ChkNot: TCheckBox
Left = 11
Top = 22
Width = 40
Height = 17
Caption = 'NOT'
TabOrder = 3
end
object EdtStartWith: TEdit
Left = 136
Top = 20
Width = 121
Height = 21
Hint = 'Case insensitive match'
ParentShowHint = False
ShowHint = True
TabOrder = 4
Text = '['
end
object BtnInvert: TButton
Left = 460
Top = 18
Width = 50
Height = 25
Caption = 'Invert'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 5
OnClick = BtnInvertClick
end
end
object BtnWrite: TButton
Left = 364
Top = 58
Width = 89
Height = 25
Hint = 'Write the selected items back to the registry'
Caption = 'Write back'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
ParentShowHint = False
ShowHint = True
TabOrder = 1
OnClick = BtnWriteClick
end
object ChkSort: TCheckBox
Left = 459
Top = 62
Width = 57
Height = 17
Caption = '(sorted)'
Checked = True
State = cbChecked
TabOrder = 2
end
end
object PnlCenter: TPanel
Left = 0
Top = 58
Width = 549
Height = 413
Align = alClient
BevelOuter = bvNone
TabOrder = 2
object CLBParams: TCheckListBox
AlignWithMargins = True
Left = 15
Top = 10
Width = 522
Height = 393
Margins.Left = 15
Margins.Top = 10
Margins.Right = 12
Margins.Bottom = 10
Align = alClient
ItemHeight = 13
TabOrder = 0
end
end
end
Notes:
Do not forget to change the version number in the key if you are using anything other than XE2. For old Delphi versions you may even have to change Embarcadero to Borland.
Do not run this app from within the IDE. When it closes, Delphi will overwrite any changes you made to that registry key, and it looks like your program does not work ;=)

Creating and deleting objects in Fast Report VCL (Delphi)

I'm using FastReport 4 to display some dynamically generated data and rearrange it in the report.
I use a "template" object in the report to get the initial position (in my real program I copy font properties, alignment, etc.)
I've managed to create a small project so I can create a memo component in the report, preview the report, and then remove the component so I can reuse the report with different data.
However, when I free the created object, I lose other objects from the report (in this case, my template object is not found the second time I preview the report).
What is the right way to create and remove objects from a Fast Report report?
Here is the pascal unit:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, frxClass;
type
TForm1 = class(TForm)
frxReport1: TfrxReport;
btn1: TButton;
procedure btn1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
var
modelObj: TfrxComponent;
newObj: TfrxMemoView;
begin
modelObj := frxReport1.FindObject('modelObj');
newObj := TfrxMemoView.Create(modelObj.Parent);
newObj.CreateUniqueName;
newObj.Text := 'Whee';
newObj.SetBounds(modelObj.Left, modelObj.Top + modelObj.Height,
modelObj.Width, modelObj.Height);
frxReport1.PrepareReport;
frxReport1.ShowPreparedReport;
newObj.Free;
end;
end.
Here's the DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 299
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object btn1: TButton
Left = 224
Top = 48
Width = 75
Height = 25
Caption = 'btn1'
TabOrder = 0
OnClick = btn1Click
end
object frxReport1: TfrxReport
Version = '4.15'
DotMatrixReport = False
IniFile = '\Software\Fast Reports'
PreviewOptions.Buttons = [pbPrint, pbLoad, pbSave, pbExport, pbZoom, pbFind, pbOutline, pbPageSetup, pbTools, pbEdit, pbNavigator, pbExportQuick]
PreviewOptions.Zoom = 1.000000000000000000
PrintOptions.Printer = 'Por defecto'
PrintOptions.PrintOnSheet = 0
ReportOptions.CreateDate = 41905.757295162040000000
ReportOptions.LastChange = 41905.757295162040000000
ScriptLanguage = 'PascalScript'
ScriptText.Strings = (
'begin'
''
'end.')
Left = 72
Top = 32
Datasets = <>
Variables = <>
Style = <>
object Data: TfrxDataPage
Height = 1000.000000000000000000
Width = 1000.000000000000000000
end
object Page1: TfrxReportPage
PaperWidth = 216.000000000000000000
PaperHeight = 279.000000000000000000
PaperSize = 1
LeftMargin = 10.000000000000000000
RightMargin = 10.000000000000000000
TopMargin = 10.000000000000000000
BottomMargin = 10.000000000000000000
object PageHeader1: TfrxPageHeader
Height = 279.685220000000000000
Top = 18.897650000000000000
Width = 740.787880000000000000
object modelObj: TfrxMemoView
Left = 166.299320000000000000
Top = 30.236240000000000000
Width = 264.567100000000000000
Height = 18.897650000000000000
ShowHint = False
Color = clYellow
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlack
Font.Height = -13
Font.Name = 'Arial'
Font.Style = []
Memo.UTF8W = (
'Model')
ParentFont = False
end
end
end
end
end
Sorry, for my misleading first answer.
It looks like an internal bug in PrepareReport, the objects seem to be exchanged.
var
modelObj: TfrxComponent;
newObj: TfrxMemoView;
cn:String;
begin
modelObj := frxReport1.FindObject('modelObj');
newObj := TfrxMemoView.Create(modelObj.Parent);
newObj.CreateUniqueName;
cn := newObj.Name; // keep for dirty workaround
newObj.Text := 'Whee';
newObj.SetBounds(modelObj.Left, modelObj.Top + modelObj.Height,
modelObj.Width, modelObj.Height);
Showmessage('New: ' + newObj.Name + ' modelObj: ' + modelObj.Name);
frxReport1.PrepareReport;
Showmessage('New: ' + newObj.Name + ' modelObj: ' + modelObj.Name);
frxReport1.ShowPreparedReport;
newObj := TfrxMemoView(frxReport1.FindObject(cn)); // dirty workaround
newObj.Free;
end;
Output:
New: Memo1 modelObj: modelObj
New: modelObj modelObj: Memo1
The workaround shown here will not be a usable way, so loading reports from a file or placing the TfrxReport component on a datamodule which will be created before printing and destroyed afterwards might be the better workarounds until this bug is fixed.

Resources