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.
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
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 want to create a property editor because a lot of things does not supported by TValueListEditor. So I use a TStringGrid and other controls placed on it when the user enter the cells. When I place a TCheckBox for boolean values, the dynamically created TCheckBox is uncheckable. The onClick event handler does not fiered by the clicks (the grid fiered) and the caption of the TCheckBox lost its opacity. I set its parent and bring it to the front. By this time I used TEdit and TComboBox controls as well and they work fine. Somebody can help to use it in the expected way?
Here is an example to recreate the situation.
pas:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids,
StdCtrls;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
procedure onCheckBoxClicked( sender_ : TObject );
public
{ Public declarations }
fCheckBox : TCheckBox;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.onCheckBoxClicked( sender_ : TObject );
begin
if ( TCheckBox( sender_ ).checked ) then
TCheckBox( sender_ ).caption := 'true'
else
TCheckBox( sender_ ).caption := 'false';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fCheckBox := TCheckBox.create( NIL );
fCheckBox.Parent := stringGrid1;
fCheckBox.caption := 'Dynamic checkbox';
fCheckBox.left := 70;
fCheckBox.top := 30;
fCheckBox.onClick := onCheckBoxClicked;
fCheckBox.BringToFront;
stringgrid1.cells[1,1] := 'fgfgfgfgfgf';
stringgrid1.cells[1,2] := 'fgfgfgfgfgf';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
fCheckBox.Free;
end;
end.
The dfm:
object Form1: TForm1
Left = 358
Top = 183
Caption = 'Form1'
ClientHeight = 601
ClientWidth = 854
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object StringGrid1: TStringGrid
Left = 120
Top = 72
Width = 320
Height = 120
TabOrder = 0
end
object CheckBox1: TCheckBox
Left = 192
Top = 128
Width = 97
Height = 17
Caption = 'Static checkbox'
TabOrder = 1
end
end
This does not work with a checkbox because the string grid intercepts processing of the WM_COMMAND message. When you click the checkbox, a WM_COMMAND notification is sent to its parent - which is the string grid. The grid, in TCustomGrid.WMCommand of 'Vcl.Grids', checks if the notification is from its inplace editor and discards the message otherwise.
You can modify the processing of the message on the grid to change the behavior. One way is to derive a new control. E.g.
type
TStringGrid = class(vcl.grids.TStringGrid)
protected
procedure WMCommand(var Message: TWMCommand); message WM_COMMAND;
end;
TForm1 = class(TForm)
StringGrid1: TStringGrid;
....
...
procedure TStringGrid.WMCommand(var Message: TWMCommand);
var
Control: TWinControl;
begin
inherited;
Control := FindControl(Message.Ctl);
if Assigned(Control) and (Control <> InplaceEditor) then
Control.Perform(Message.Msg, MakeWParam(Message.ItemID, Message.NotifyCode),
Message.Ctl);
end;
Then the OnClick will fire. You don't need BringToFront, it works among sibling controls.
Regarding opacity, it's the checkbox's default appearance. You can verify this by placing a checkbox overlapping a label on the form itself.
I would like to show TCustomForm descendants as dialogs so that they are positioned poOwnerFormCenter. But the same code that is positioning the form correctly when FormStyle is fsNormal does not set the correct position when FormStyle is fsMDIChild.
When the secondary form has FormStyle = fsNormal Button1 opens the modal dialog like this:
But when the secondary form has FormStyle = fsMDIChild the positioning seems to be relative to the position of the MDI Child to the MDI Parent, not the absolute postion of the MDI Child:
I'm not sure if I make any mistakes, if this is maybe a bug or normal behavior.
Following code is used to show the dialog:
procedure TForm3.Button1Click(Sender: TObject);
var
AModalForm: TForm;
begin
AModalForm := TForm.Create(Self);
try
AModalForm.Position := poOwnerFormCenter;
AModalForm.ShowModal;
finally
AModalForm.Free;
end;
end;
Project to reproduce:
dpr
program Project2;
uses
Vcl.Forms,
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3};
{$R *.res}
var
AMainForm: TForm2;
A: TApplication;
begin
A := Application;
A.Initialize;
A.MainFormOnTaskbar := True;
A.CreateForm(TForm2, AMainForm);
A.Run;
end.
Unit2
pas
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Menus;
type
TForm2 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses
Unit3;
procedure TForm2.Button1Click(Sender: TObject);
var
AForm: TForm3;
begin
AForm := TForm3.Create(Self);
AForm.FormStyle := fsMDIChild;
end;
procedure TForm2.Button2Click(Sender: TObject);
var
AForm: TForm3;
begin
AForm := TForm3.Create(Self);
AForm.FormStyle := fsNormal;
end;
end.
dfm
object Form2: TForm2
Left = 0
Top = 0
Caption = 'Form2'
ClientHeight = 356
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
FormStyle = fsMDIForm
OldCreateOrder = False
Visible = True
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 97
Height = 356
Align = alLeft
TabOrder = 0
object Button1: TButton
Left = 8
Top = 39
Width = 75
Height = 25
Caption = 'fsMDIChild'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'fsNormal'
TabOrder = 1
OnClick = Button2Click
end
end
end
Unit3
pas
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var
AModalForm: TForm;
begin
AModalForm := TForm.Create(Self);
try
AModalForm.Position := poOwnerFormCenter;
AModalForm.ShowModal;
finally
AModalForm.Free;
end;
end;
end.
dfm
object Form3: TForm3
Left = 0
Top = 0
Caption = 'Form3'
ClientHeight = 336
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Visible = True
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
end
After trying several ideas found here on SO and also elsewhere, and all my attempts failing, I guess the safest (= working in all Delphi versions) solution is as follows.
Unit1 - Form1:TForm1 - fsMDIForm
Unit2 - Form2:TForm2 - fsMDIChild
Unit3 - AModalForm:TFom3 - ordinary form, shown modally, centered on the Form2
The essential part is simply a fallback to manually calculating and setting Left and Top properties of AModalForm so it becomes centered. It requies also setting Position property to poDesigned
// Showing the modal form centered by a fsMDIChild form
procedure TForm2.Button1Click(Sender: TObject);
var
AModalForm: TForm3;
begin
AModalForm := TForm3.Create(self);
try
AModalForm.Left := Self.ClientOrigin.X + (Self.ClientWidth-AModalForm.Width) div 2;
AModalForm.Top := Self.ClientOrigin.Y + (Self.ClientHeight-AModalForm.Height) div 2;
AModalForm.Position := poDesigned;
AModalForm.ShowModal;
// use modalresult as needed
finally
AModalForm.Free;
end;
end;
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?