I am attempting to find a subset of records in a clientdataset by using a clone cursor to seek a records using an indexdef. In the example below I have created a button to implement the clone creation. The code works fine for a clientdataset named cdsData. However if I nest cdsData in another dataset (cdsMaster) then the button code fails to find the indexDef.
The Form: (including both clientdatasets showing both datasources and clientdatasets. Omit dsMaster & cdsMAster in first unit example below)
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 289
ClientWidth = 554
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 32
Top = 24
Width = 75
Height = 25
Caption = 'Clone'
TabOrder = 0
OnClick = Button1Click
end
object dsMaster: TDataSource
DataSet = cdsMaster
Left = 56
Top = 80
end
object cdsMaster: TClientDataSet
Aggregates = <>
Params = <>
Left = 56
Top = 144
end
object dsData: TDataSource
DataSet = cdsData
Left = 152
Top = 88
end
object cdsData: TClientDataSet
Aggregates = <>
Params = <>
Left = 152
Top = 144
end
end
The successful cdsData unit:
unit IndexFindTest;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Data.DB, Datasnap.DBClient;
type
TForm1 = class(TForm)
dsData: TDataSource;
cdsData: TClientDataSet;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Disp, Lbl : string;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Clone: TClientDataset;
begin
Clone := TClientDataset.Create(nil);
try
Clone.CloneCursor(cdsData,false);
cdsData.IndexDefs.Update;
clone.IndexName := cdsData.IndexDefs.find ('Lbl').Name;
//..added code to select a range of records using the IndexDef
finally
clone.free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
disp := 'Item 1';
lbl := 'a';
with TStringField.Create(Self) do begin
Name := 'MstrDisplays';
FieldKind := fkData;
FieldName := 'Displays';
Size := 10;
DataSet := cdsData;
end;
with TStringField.Create(Self) do begin
Name := 'MstrLabel';
FieldKind := fkData;
FieldName := 'Label';
Size := 10;
DataSet := cdsData;
end;
with cdsData.IndexDefs.AddIndexDef do begin
Name := 'Lbl';
Fields := 'Displays;Label';
Options := [ixCaseInsensitive];
end;
cdsData.CreateDataSet;
end;
end.
The nested version that fails:
unit IndexFindTest2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Data.DB, Datasnap.DBClient;
type
TForm1 = class(TForm)
dsMaster: TDataSource;
cdsMaster: TClientDataSet;
Button1: TButton;
dsData: TDataSource;
cdsData: TClientDataSet;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Disp, Lbl : string;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Clone: TClientDataset;
begin
Clone := TClientDataset.Create(nil);
try
Clone.CloneCursor(cdsData,false);
cdsData.IndexDefs.Update;
//Error generated in next line
clone.IndexName := cdsData.IndexDefs.find ('Lbl').Name;
//..added code to select a range of records using the IndexDef
finally
clone.free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
disp := 'Item 1';
lbl := 'a';
with TStringField.Create(Self) do begin
Name := 'MstrTitle';
FieldKind := fkData;
FieldName := 'Title';
Size := 10;
DataSet := cdsMaster;
end;
with TDataSetField.Create(Self) do
begin
Name := 'MstrDisplay';
FieldName := 'Displays';
DataSet := cdsMaster;
end;
cdsData.DataSetField := TDataSetField(cdsMaster.FieldByName('Displays'));
with TStringField.Create(Self) do begin
Name := 'ClientNested';
FieldKind := fkData;
FieldName := 'Notes';
Size := 10;
DataSet := cdsData;
end;
with TStringField.Create(Self) do begin
Name := 'kntLabel';
FieldKind := fkData;
FieldName := 'Label';
Size := 10;
DataSet := cdsData;
end;
cdsData.IndexDefs.Update;
with cdsData.IndexDefs.AddIndexDef do begin
Name := 'Lbl';
Fields := 'Notes;Label';
Options := [ixCaseInsensitive];
end;
cdsMaster.CreateDataSet;
end;
end.
When running the second program I receive the error
cdsData: index 'Lbl' not found.
The only difference I can identify between the two programs is the fact that cdsData is nested in the second version. I found a note in CaryJensen's Delphi in Depth:clientDatasets 2nd edition (pg128) stating that the erro may occur and can be fixed using update, but no matter where in the sequence I apply the update, it does not work in this situation.
Can anyone shed light on this problem? Are there additional steps with a nested dataset?
Related
I have never needed to do much COM, so have almost no experience with it. Nearly all of the documentation (certainly from MS) does not include Delphi examples.
In my code example, I can't see where I'm going wrong. The code was borrowed from snippets found in several locations on the web. Some were VB. I only found one thread for Free Pascal, and it was incomplete. This runs, but shows displays the same string for both name and value. I hope someone can see what I'm missing. I think the problem is with the line that reads:
PropValue := OleFolder.GetDetailsOf(OleFolderItem, i);
I don't know if I need to do something to initialize "OleFolderItem".
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ActiveX, ComObj, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
pnl1: TPanel;
btn1: TButton;
mmo1: TMemo;
OpenDialog1: TOpenDialog;
procedure btn1Click(Sender: TObject);
procedure getExtdProps(AFileName: string);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
Procedure TForm1.getExtdProps(AFileName: string);
var
Shell : Variant;
OleFolder : OleVariant;
OleFolderItem: OleVariant;
PropName, PropValue: string;
i: integer;
begin
Shell := CreateOleObject('Shell.Application');
OleFolder := Shell.Namespace(ExtractFilePath(AFileName));
i := 0;
PropName := 'Not an EmptyStr'; //So the next loop will work.
while PropName <> EmptyStr do
begin
PropName := OleFolder.GetDetailsOf(null, i); {null gets the name}
PropValue := OleFolder.GetDetailsOf(OleFolderItem, i); { OleFolderItem should get the value }
if PropName <> '' then
mmo1.Lines.Add(PropName + ': ' + PropValue);
inc(i);
end;
end;
procedure TForm1.btn1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
GetExtdProps(OpenDialog1.FileName);
end;
end;
end.
Try this complete example :
unit GetDetailsOfDemoMain;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Win.ComObj, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetExtdProps(AFileName: string);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.GetExtdProps(AFileName: string);
var
Shell : Variant;
OleFolder : OleVariant;
OleFolderItem : OleVariant;
ForlderName : String;
PropName : String;
PropValue : string;
I : integer;
begin
Shell := CreateOleObject('Shell.Application');
OleFolder := Shell.Namespace(ExtractFilePath(AFileName));
OleFolderItem := OleFolder.ParseName(ExtractFileName(AFileName));
for I := 0 to 999 do begin
PropName := OleFolder.GetDetailsOf(null, i);
PropValue := OleFolder.GetDetailsOf(OleFolderItem , I);
if (PropName <> '') and (PropValue <> '') then
Memo1.Lines.Add(Format('%3d) %-30s: %s',
[I, PropName, PropValue]));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
GetExtdProps(OpenDialog1.FileName);
end;
end.
DFM file:
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 = []
PixelsPerInch = 96
DesignSize = (
624
441)
TextHeight = 15
object Button1: TButton
Left = 40
Top = 32
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Memo1: TMemo
Left = 8
Top = 72
Width = 609
Height = 361
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Consolas'
Font.Style = []
ParentFont = False
ScrollBars = ssBoth
TabOrder = 1
end
object OpenDialog1: TOpenDialog
Left = 48
Top = 88
end
end
Using Delphi 10.4.
I am hoping someone can explain what I am doing wrong with my FMX TTreeView that is causing an EArgumentOutOfRangeException. I am trying to create a custom TTreeViewItem class that allows me to associate some data with each node, as well as provide an in-place editor to allowing changing the node text.
The code below is a stripped down version of what I am doing. The FMX form has a TTreeview and two buttons on it, with the form's Onshow set to FormShow and the buttons set to the two button events.
The TVLinkTreeViewItem is my custom TTreeViewItem where I add a background and edit component for my in-place editor, which is displayed when a node is double clicked.
When you run the code as is, the program will throw the exception when the logic gets to the TreeView1.EndUpdate call at the end of the FormShow routine. The exception is thrown in FMX.Controls in the TControl.EndUpdate procedure.
If you comment out the ExpandAll call, the exception is not thrown, but if you mess with the expanding and collapsing of the nodes and resizing of the form, sooner or later the exception gets thrown. I left the ExpandAll line in the code below, as I assume the exception is being caused by the same error.
From what I can tell, the problem appears to be how I am setting up the fBackground and fEditor. If I don't call the AddObject routine and not set the Parent properties, I get no exception.
So can anybody tell me what I am doing wrong? Or is there a better way to do an in-place editor for the FMX TTreeViewItems component?
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TreeView, FMX.Layouts, FMX.Controls.Presentation,
FMX.MultiView, FMX.Edit, FMX.Objects, FMX.StdCtrls;
type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
type
TVLinkTreeViewItem = class(TTreeViewItem)
private
fData: string;
fEditor: TEdit;
fBackground: TRectangle;
procedure TreeViewItem1DblClick(Sender: TObject);
procedure EditorExit(Sender: TObject);
procedure EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
public
property Editor: TEdit read fEditor write fEditor;
property Data: string read fData write fData;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.ExpandAll;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TreeView1.CollapseAll;
end;
procedure TForm1.FormShow(Sender: TObject);
var
I, c, r, s: Integer;
vNode1,
vNode2,
vNode3,
vNode4: TVLinkTreeViewItem;
begin
TreeView1.BeginUpdate;
TreeView1.Clear;
for I := 0 to 4 do
begin
vNode1 := TVLinkTreeViewItem.Create(TreeView1);
vNode1.Text := 'Level 1 - '+ IntToStr(I);
TreeView1.AddObject(vNode1);
for c := 0 to 4 do
begin
vNode2 := TVLinkTreeViewItem.Create(vNode1);
vNode2.Text := 'Level 2 - '+ IntToStr(c);
vNode1.AddObject(vNode2);
for r := 0 to 4 do
begin
vNode3 := TVLinkTreeViewItem.Create(vNode2);
vNode3.Text := 'Level 3 - '+ IntToStr(r);
vNode2.AddObject(vNode3);
// for s := 0 to 4 do
// begin
// vNode4 := TVLinkTreeViewItem.Create(vNode3);
// vNode4.Text := 'Level 4 - '+ IntToStr(s);
// vNode3.AddObject(vNode4);
// end;
end;
end;
end;
//ExpandAll works when no parent is set for fBackGround and fEditor is not set in "TVLinkTreeViewItem.Create" below"
//If the Parents are set below, ExpandAll/EndUpdate causes "Augument out of range" exception.
TreeView1.ExpandAll;
treeView1.EndUpdate;
end;
{ TVLinkTreeViewItem }
constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
inherited;
fData := '';
fBackground := TRectangle.Create(AOwner);
//When ExpandAll is not called in FormShow,
// Calling "AddObject" or setting parent, as shown below, make all the code work,
// but will get intermident "Augument out of range" exceptions when resizing form,
// or when expanding or collapsing nodes using the buttons.
self.AddObject(fBackGround);
//fBackGround.Parent := self;
fBackGround.Visible := false;
fEditor := TEdit.Create(AOwner);
fBackGround.AddObject(fEditor);
//fEditor.Parent := fBackGround;
fEditor.Visible := false;
fEditor.Align := TAlignLayout.Client;
fEditor.OnKeyDown := EditorKeyUp;
self.OnDblClick := TreeViewItem1DblClick;
fEditor.OnExit := EditorExit;
end;
destructor TVLinkTreeViewItem.Destroy;
begin
inherited;
end;
procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
begin
fBackGround.Visible := true;
fBackGround.Width := self.Width - 20;
fBackGround.Height := self.Height;
fBackGround.Position.X := 20;
fEditor.Enabled := true;
fEditor.Visible := true;
fEditor.Opacity := 1;
fBackGround.BringToFront;
fEditor.BringToFront;
fEditor.Text := Text;
fEditor.SetFocus;
fEditor.SelectAll;
end;
procedure TVLinkTreeViewItem.EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
inherited;
if Key = vkReturn then
begin
Text := fEditor.Text;
fBackGround.Visible := false;
fEditor.Enabled := false;
end
else if Key in [vkEscape, vkCancel, vkTab, vkHardwareBack] then
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
end;
end;
procedure TVLinkTreeViewItem.EditorExit(Sender: TObject);
begin
fBackGround.Visible := false;
fEditor.Enabled := false;
fEditor.Visible := false;
end;
end.
Here's the fmx content:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnShow = FormShow
DesignerMasterStyle = 0
object TreeView1: TTreeView
Align = Left
Size.Width = 269.000000000000000000
Size.Height = 480.000000000000000000
Size.PlatformDefault = False
TabOrder = 0
Viewport.Width = 265.000000000000000000
Viewport.Height = 476.000000000000000000
end
object Button1: TButton
Position.X = 356.000000000000000000
Position.Y = 68.000000000000000000
TabOrder = 2
Text = 'Expand'
OnClick = Button1Click
end
object Button2: TButton
Position.X = 354.000000000000000000
Position.Y = 102.000000000000000000
TabOrder = 1
Text = 'Collapse'
OnClick = Button2Click
end
end
I have a form with KeyPreview=true and want to capture the arrow keys, unless we are in a control that should handle those.
The issue is: focus is always on one of those controls.
How can I adapt/design this to work?
.PAS file
unit uKeyDownTests;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TFrmKeyDownTests = class(TForm)
PnlBottom: TPanel;
PnlClient: TPanel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
public
end;
var
FrmKeyDownTests: TFrmKeyDownTests;
implementation
{$R *.dfm}
type
THackWinControl = class(TWinControl);
procedure TFrmKeyDownTests.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var lActiveControl: TControl;
begin
// Earlier code, but that did not work either:
// if Edit1.Focused or Edit2.Focused or Edit3.Focused then Exit;
lActiveControl := ActiveControl;
if Assigned(lActiveControl) then
begin
if lActiveControl = Edit1 then
begin
THackWinControl(Edit1).KeyDown(Key,Shift);
Exit;
end;
if lActiveControl = Edit2 then
begin
THackWinControl(Edit2).KeyDown(Key,Shift);
Exit;
end;
if lActiveControl = Edit3 then
begin
THackWinControl(Edit3).KeyDown(Key,Shift);
Exit;
end;
end;
if (Key = VK_RIGHT) then
begin
PnlBottom.Caption := PnlBottom.Caption + 'R';
Key := 0;
Exit;
end;
if (Key = VK_LEFT) then
begin
PnlBottom.Caption := PnlBottom.Caption + 'L';
Key := 0;
Exit;
end;
if (Key = VK_UP) then
begin
PnlBottom.Caption := PnlBottom.Caption + 'U';
Key := 0;
Exit;
end;
if (Key = VK_DOWN) then
begin
PnlBottom.Caption := PnlBottom.Caption + 'D';
Key := 0;
Exit;
end;
end;
end.
.DFM file
object FrmKeyDownTests: TFrmKeyDownTests
Left = 0
Top = 0
Caption = 'Keydown tests'
ClientHeight = 336
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
KeyPreview = True
OldCreateOrder = False
OnKeyDown = FormKeyDown
PixelsPerInch = 96
TextHeight = 13
object PnlBottom: TPanel
Left = 0
Top = 295
Width = 635
Height = 41
Align = alBottom
TabOrder = 0
end
object PnlClient: TPanel
Left = 0
Top = 0
Width = 635
Height = 295
Align = alClient
TabOrder = 1
object Edit1: TEdit
Left = 40
Top = 32
Width = 121
Height = 21
TabOrder = 0
Text = 'Edit1'
end
object Edit2: TEdit
Left = 40
Top = 72
Width = 121
Height = 21
TabOrder = 1
Text = 'Edit1'
end
object Edit3: TEdit
Left = 40
Top = 112
Width = 121
Height = 21
TabOrder = 2
Text = 'Edit1'
end
end
end
(Answering my own question for my specific situation, slightly different from the one in the 'Possible dupe', but based on the answers there)
In my case, the easiest solution was:
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY; which only calls inherited
KeyPreview=true for the form
A FormKeydown that handles what I want to do with arrow keys
Result:
The controls that have focus as well as the form handle the arrow keys
It does not matter if the controls have an OnKeyDown handler (the Edit2 control) or not (the others)
Modified code:
unit uKeyDownTests;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TFrmKeyDownTests = class(TForm)
PnlBottom: TPanel;
PnlClient: TPanel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY; public
end;
var
FrmKeyDownTests: TFrmKeyDownTests;
implementation
{$R *.dfm}
procedure TFrmKeyDownTests.DialogKey(var Msg: TWMKey);
begin
inherited;
end;
procedure TFrmKeyDownTests.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_RIGHT: PnlBottom.Caption := PnlBottom.Caption + 'R';
VK_LEFT : PnlBottom.Caption := PnlBottom.Caption + 'L';
VK_UP : PnlBottom.Caption := PnlBottom.Caption + 'U';
VK_DOWN : PnlBottom.Caption := PnlBottom.Caption + 'D';
end;
end;
{ TFrmKeyDownTests }
procedure TFrmKeyDownTests.Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
PnlBottom.Caption := PnlBottom.Caption + '-kd-';
end;
end.
I have questions about how to create dynamic controls, how destroy and how get value inside newly created control.
Create and count edits create in form worked correctly, but where I create edits in panels with buttons to destroy chosen panel (Panel [Edit, button]), it's create correctly, but count doesnt work.
And I don't know how to destroy chosen by me panel with edit without error (I didn't make it yet in code below).
I have this code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TfrmMain = class(TForm)
btnCreateNewObject: TButton;
btnCountValues: TButton;
btnCreateNewPanels: TButton;
btnAllEditsInPanels: TButton;
procedure btnCreateNewObjectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnCountValuesClick(Sender: TObject);
procedure btnCreateNewPanelsClick(Sender: TObject);
procedure btnAllEditsInPanelsClick(Sender: TObject);
private
dynEdit: TEdit;
dynPanel: TPanel;
yposition: integer;
ypositionpanel: integer;
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnCountValuesClick(Sender: TObject);
var
i: integer;
res: integer;
begin
res := 0;
for i := 0 to Self.ControlCount - 1 do
begin
if Controls[i] is TEdit then
begin
res := res + StrToInt((Controls[i] as TEdit).Text);
end;
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.btnCreateNewObjectClick(Sender: TObject);
begin
dynEdit := TEdit.Create(Self);
with dynEdit do
begin
Parent := frmMain;
Width := 80;
Height := 25;
Top := yposition;
Left := 3;
end;
yposition := yposition + 26
end;
procedure TfrmMain.btnCreateNewPanelsClick(Sender: TObject);
begin
dynPanel := TPanel.Create(Self);
with dynPanel do
begin
Parent := frmMain;
Width := 100;
Height := 40;
Top := ypositionpanel;
Left := 120;
dynEdit := TEdit.Create(Self);
with dynEdit do
begin
Parent := dynPanel;
Width := 80;
Height := 25;
Top := 3;
Left := 3;
end;
end;
ypositionpanel := ypositionpanel + 41;
end;
procedure TfrmMain.btnAllEditsInPanelsClick(Sender: TObject);
var
i, j: integer;
res: integer;
begin
res := 0;
for i := 0 to Self.ControlCount - 1 do
begin
for j := 0 to dynPanel.ControlCount - 1 do
begin
if dynPanel.Controls[j] is TEdit then
begin
res := res + StrToInt( (Controls[j] as TEdit).Text );
end;
end;
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
yposition := 1;
ypositionpanel := 1;
end;
end.
object frmMain: TfrmMain
Left = 0
Top = 0
Caption = 'frmMain'
ClientHeight = 500
ClientWidth = 888
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object btnCreateNewObject: TButton
Left = 775
Top = 475
Width = 113
Height = 25
Caption = 'Create new edit'
TabOrder = 0
OnClick = btnCreateNewObjectClick
end
object btnCountValues: TButton
Left = 775
Top = 444
Width = 113
Height = 25
Caption = 'Count all edits'
TabOrder = 1
OnClick = btnCountValuesClick
end
object btnCreateNewPanels: TButton
Left = 648
Top = 475
Width = 121
Height = 25
Caption = 'Create new panels'
TabOrder = 2
OnClick = btnCreateNewPanelsClick
end
object btnAllEditsInPanels: TButton
Left = 648
Top = 444
Width = 121
Height = 25
Caption = 'Count all edits in panels'
TabOrder = 3
OnClick = btnAllEditsInPanelsClick
end
end
You are iterating only through the Edit controls that are direct children of the Form itself, or of the last Panel created. You are not iterating through all of the Panels.
Use a TList or other suitable container to keep track of the Edits you create dynamically, then you can loop through that list/container when needed. And when you are ready to remove a Panel from the Form, simply Remove() its child TEdit from the list and then Free() the Panel, which will free the TEdit for you.
For example:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
System.Generics.Collections;
type
TfrmMain = class(TForm)
btnCreateNewObject: TButton;
btnCountValues: TButton;
btnCreateNewPanels: TButton;
btnAllEditsInPanels: TButton;
procedure btnCreateNewObjectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnCountValuesClick(Sender: TObject);
procedure btnCreateNewPanelsClick(Sender: TObject);
procedure btnAllEditsInPanelsClick(Sender: TObject);
procedure DestroyPanel(Sender: TObject);
private
{ Private declarations }
AllEdits: TList<TEdit>;
yposition: integer;
ypositionpanel: integer;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnCountValuesClick(Sender: TObject);
var
i, res: integer;
dynEdit: TEdit;
begin
res := 0;
for i := 0 to AllEdits.Count - 1 do
begin
dynEdit := AllEdits[i];
if dynEdit.Parent = Self then
res := res + StrToInt(dynEdit.Text);
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.btnCreateNewObjectClick(Sender: TObject);
var
dynEdit: TEdit;
begin
dynEdit := TEdit.Create(Self);
try
with dynEdit do
begin
Parent := Self;
Width := 80;
Height := 25;
Top := yposition;
Left := 3;
end;
AllEdits.Add(dynEdit);
except
dynEdit.Free;
raise;
end;
yposition := yposition + 26
end;
procedure TfrmMain.btnCreateNewPanelsClick(Sender: TObject);
var
dynPanel: TPanel;
dynEdit: TEdit;
dynButton: TButton;
begin
dynPanel := TPanel.Create(Self);
try
with dynPanel do
begin
Parent := Self;
Width := 200;
Height := 40;
Top := ypositionpanel;
Left := 120;
end;
dynEdit := TEdit.Create(dynPanel);
with dynEdit do
begin
Parent := dynPanel;
Width := 80;
Height := 25;
Top := 3;
Left := 3;
end;
dynButton := TButton.Create(dynPanel);
with dynButton do
begin
Parent := dynPanel;
Width := 100;
Height := 25;
Top := 3;
Left := 100;
Caption := 'Destroy this pnl';
onClick := DestroyPanel;
end;
AllEdits.Add(dynEdit);
except
dynPanel.Free;
raise;
end;
ypositionpanel := ypositionpanel + 41;
end;
procedure TfrmMain.DestroyPanel(Sender: TObject);
var
dynPanel: TPanel;
dynEdit: TEdit;
begin
dynPanel := TPanel(TButton(Sender).Owner);
dynEdit := TEdit(dynPanel.Controls[0]);
AllEdits.Remove(dynEdit);
dynPanel.Free;
end;
procedure TfrmMain.btnAllEditsInPanelsClick(Sender: TObject);
var
i, res: integer;
dynEdit: TEdit;
begin
res := 0;
for i := 0 to AllEdits.Count - 1 do
begin
dynEdit := AllEdits[i];
if dynEdit.Parent <> Self then
res := res + StrToInt(dynEdit.Text);
end;
ShowMessage(IntToStr(res));
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
yposition := 1;
ypositionpanel := 1;
AllEdits := TList<TEdit>.Create;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
AllEdits.Free;
end;
end.
Found a glitch with VCL styles: when you update the form caption, other controls previously redrawn within the same procedure don't get repainted, and you are forced to call Repaint, losing valuable processing time to redraw.
Example: (set project options/vcl style manually)
unit Unit11;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm11 = class(TForm)
Button1: TButton;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form11: TForm11;
implementation
{$R *.dfm}
procedure TForm11.Button1Click(Sender: TObject);
begin
Panel1.Caption := 'test';
caption := 'glitch';
end;
end.
object Form11: TForm11
Left = 0
Top = 0
Caption = 'Form11'
ClientHeight = 89
ClientWidth = 352
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 8
Top = 8
Width = 121
Height = 57
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 135
Top = 8
Width = 185
Height = 57
Caption = 'Panel1'
TabOrder = 1
end
end
program Project10;
uses
Vcl.Forms,
Unit11 in 'Unit11.pas' {Form11},
Vcl.Themes,
Vcl.Styles;
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
TStyleManager.TrySetStyle('Cobalt XEMedia');
Application.CreateForm(TForm11, Form11);
Application.Run;
end.
Set the caption calls in the sequence.
First form.caption, then child.caption.
Once you've called the wrong sequence, then stopped working the correct sequence. That's why I use here, the "set default" button.
This proceed, as long as there is no fix for it, I can live with that.
procedure TForm11.Button1Click(Sender: TObject);
begin // wrong order
Panel1.Caption := 'test';
caption := 'glitch';
end;
procedure TForm11.Button2Click(Sender: TObject);
begin // right order
caption := 'glitch';
Panel1.Caption := 'test';
end;
procedure TForm11.Button3Click(Sender: TObject);
var
i:integer;
begin // count no refresh
for i:= 1 to 300 do begin
caption := 'glitch '+intToStr(i);
Panel1.Caption := 'test '+intToStr(i);
end;
end;
procedure TForm11.Button4Click(Sender: TObject);
var
i:integer;
begin // count with refresh
for i:= 1 to 300 do begin
caption := 'glitch '+intToStr(i);
Panel1.Caption := 'test '+intToStr(i);
Panel1.Refresh;
end;
end;
procedure TForm11.Button5Click(Sender: TObject);
begin // set default
caption := 'Form11';
Panel1.Caption := 'Panel1';
Panel1.Refresh;
end;
end.
If you found another solution. Please tell me.