This is driving me nuts ....
I fetch the names of my databases to populate the cxComboBox1:
procedure TForm1.FormShow(Sender: TObject);
var
I: Integer;
DBList: TStringDynArray;
begin
DBList := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)), '*.abs', TSearchOption.soAllDirectories);
for I := 0 to Length(DBList) - 1 do
begin
cxCombobox1.Properties.Items.Add(DBList[I]);
end;
end;
This works OK.The list of my databases show in the cxCombobox1.
In the second cxCombobox I populate the table names that belong to the database
displayed in cxCombobox1.
procedure TForm1.cxComboBox1PropertiesChange(Sender: TObject);
var
TABLES: TStringList;
i: integer;
begin
if ABSTable1.Active = True then
ABSTable1.Close;
cxComboBox2.properties.Items.Clear;
TABLES := TStringList.Create;
ABSDatabase1.DatabaseFileName:=cxCombobox1.Text;
try
ABSDatabase1.Open;
ABSDatabase1.GetTablesList(TABLES);
for i:= 0 to TABLES.Count-1 do
cxComboBox2.properties.Items.Add(TABLES[i]);
finally
TABLES.free;
end;
end;
This basically works ok. Selecting the database in the cxComboBox1 populates the cxComboBox2
with pertinent tables.
So the general idea is to open the table when selected in the cxComboBox2.
And I did :
procedure TForm1.ABSTable1BeforeOpen(DataSet: TDataSet);
begin
ABSTable1.DatabaseName:= ABSDatabase1.DatabaseName;
ABSTable1.TableName := cxComboBox2.Text;
end;
And on combobox2 change event I did:
procedure TForm1.cxComboBox2PropertiesChange(Sender: TObject);
begin
cxGrid1DBTableView1.ClearItems;
ABSTable1.Open;
cxGrid1DBTableView1.DataController.CreateAllItems;
end;
This works ok. But only when I open the tables from the selected database (combobox1)
that are displayed in the combobox2.
Say I have the table opened and then go select another database in the combobox1
I get the error "Missing ABSTable1.Tablename" !
What am I missing here? Where is my table name getting lost ?
If I replace the combobox2 on change event with a button :
procedure TForm1.cxButton2Click(Sender: TObject);
begin
if ABSTable1.Active = True then
ABSTable1.Close;
cxGrid1DBTableView1.ClearItems;
ABSTable1.Open;
cxGrid1DBTableView1.DataController.CreateAllItems;
end;
then everything works....
This happens because you call Items.Clear() in cxComboBox1PropertiesChange which raises a change event which in return results in a call to cxComboBox2PropertiesChange.
You should use Items.BeginUpdate() before any update and Items.EndUpdate() when you finished the update to avoid raising events on each updated step / item.
Related
I have ported an application from ADO to FireDAC applying several RegExp replaces on the source code to convert the ADOQuery, ADOTables, ADOCommands, ADOStoredProcs, etc. ... to the corresponding FireDAC components.
It has worked fine, but now when running that application plenty of forms raise errors because of the type of the persistent fields being different than the type expected (the one defined from ADO when the persistent field was created).
I'm trying to make a list of those errors, creating an instance of all my forms and opening their datasets with persistent fields, and logging the errors. I can get the list of forms from the project source code, but when I try to use FindClass to create each form I get an error telling that the class has not been found.
Is there any other way to create a Form/DataModule from its class name ?.
This is my current code:
class procedure TfrmCheckFormularis.CheckDatasets(ProjecteFile: string);
var frmCheckFormularis: TfrmCheckFormularis;
Projecte: string;
rm: TMatch;
cc: TComponentClass;
c: TComponent;
i: integer;
Dataset: TFDQuery;
begin
Projecte := TFile.ReadAllText(ProjecteFile);
frmCheckFormularis := TfrmCheckFormularis.Create(Application);
try
with frmCheckFormularis do begin
Show;
qryForms.CreateDataSet;
qryErrors.CreateDataSet;
// I get a list of all the forms and datamodules on my project
for rm in TRegEx.Matches(Projecte, '^(?:.* in '')(?<File>.*)(?:'' {)(?<Class>.*)(?:},)', [roMultiline]) do begin
qryForms.AppendRecord([rm.Groups['File'].Value, rm.Groups['Class'].Value]);
end;
// Check every form and datamodule
qryForms.First;
while not qryForms.Eof do begin
cc := TComponentClass(FindClass(qryFormsClass.Value));
c := cc.Create(frmCheckFormularis);
try
for i := 0 to c.ComponentCount - 1 do begin
if c.Components[i] is TFDQuery then begin
Dataset := c.Components[i] as TFDQuery;
// When the Dataset has persistent fields, I open it to check if the persistent fields are correct
if Dataset.FieldDefs.Count > 1 then begin
try
Dataset.Open;
except
on E: Exception do qryErrors.AppendRecord([c.Name, Dataset.Name, E.Message]);
end;
end;
end;
end;
finally
c.Free;
end;
qryForms.Next;
end;
end;
finally
frmCheckFormularis.Free;
end;
end;
Thank you.
Using the "new" RTTI in Delphi is quite easy. The following code will (hopefully*) create one instance of each form in your application:
procedure TForm1.Button1Click(Sender: TObject);
var
Context: TRttiContext;
&Type: TRttiType;
InstanceType: TRttiInstanceType;
begin
Context := TRttiContext.Create;
for &Type in Context.GetTypes do
begin
if (&Type.TypeKind = tkClass) and &Type.IsInstance then
begin
InstanceType := TRttiInstanceType(&Type);
if InstanceType.MetaclassType.InheritsFrom(TForm) and (InstanceType.MetaclassType <> TForm) then
TFormClass(InstanceType.MetaclassType).Create(Application){.Show}; // optionally show it
end;
end;
end;
* Technically, it will create one instance of each proper descendant class of TForm.
Hey guy's so am making this program in delphi that loads a list of usernames i want to add them all to the Memo without using selectall i want to use the for loop to learn how it works as you can see i tried by failed it selects both but only adds the last one which is weird xD Any help would be great thanks guys
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Lines.Clear;
Listbox1.Items.LoadFromFile('names.txt');
end;
procedure TForm1.Button2Click(Sender: TObject);
var
I: Integer;
begin
for I:=Listbox1.Items.Count-1 downto 0 do
begin
ListBox1.ItemIndex:=I;
Memo1.Lines.Add(ListBox1.Items.Strings[1]);
end
end;
end;
end.
Well, you add the item with index 1 every time. You presumably mean:
for I:=Listbox1.Items.Count-1 downto 0 do
Memo1.Lines.Add(ListBox1.Items[I]);
This adds in reverse order. If you want the items in the same order it is simply:
Memo1.Lines.Assign(ListBox1.Items);
I have a grid that I use to display few query results (cxGrid1.ActiveLevel.GridView := cxGrid1DBTableView1; etc ...). On form close, I close the queries as well.
However, the former columns that were last displayed remain visible (when I return to that form again) How can I eliminate these traces of columns as well? I would like empty grid when I return to the form.
Edit : This is the query that I run :
procedure TForm2.cxRadioGroup1Click(Sender: TObject);
begin
case cxRadioGroup1.ItemIndex of
0: begin
with Form1.UniQuery3 do begin
Close;
sql.Clear;
sql.Add('select * from program_log');
Open;
cxGrid1.ActiveLevel.GridView := cxGrid1DBTableView1;
end;
end;
1: begin
with Form1.UniQuery4 do begin
Close;
sql.Clear;
sql.Add('select * from guests_log');
Open;
cxGrid1.ActiveLevel.GridView := cxGrid1DBTableView2;
end;
end;
end;
end;
Calling
cxGrid1DBTableView1.ClearItems;
Removes all traces of columns which are then unavailable if I want to run the query again.
The easiest way to accomplish this is to create another cxgridlevel > cxGrid1dbtableview. Then just call set this level on form show:
procedure TForm2.FormShow(Sender: TObject);
begin
cxGrid1.ActiveLevel.GridView := cxGrid1DBTableView3;
end;
Since it is assigned to nothing so will the grid display empty.
Calling cxGrid1DBTableView1.ClearItems will remove all columns. Just call it in your FormClose event handler. To recreate columns call cxGrid1DBTableView1.DataController.CreateAllItems function.
With the code below I am trying to reload a DirectionsResult back into a TGMDirections.
procedure Form2.Button2Click(Sender: TObject);
var
DR: TDirectionsResult;
i: Integer;
begin
DR:= TDirectionsResult.Create(Form1.FDirection, 0);
DR.XMLData.BeginUpdate;
for i:= 0 to Memo1.Lines.Count - 1 do
begin
DR.XMLData.Append(Memo1.Lines[i]);
end;
DR.XMLData.EndUpdate;
ShowMessage(Form1.FDirection.DirectionsResult[0].Routes[0].Leg[0].EndAddress);
end;
All seems well until the ShowMessage where I get a List out of bounds message.
I take it that the DR has not been created or the Memo has not loaded into the DirectionsResult.
Further adaption has confirmed the DirectionsResult[0] does not exist.
Help with the correction would be greatly appreciated.
You can't add a TDirectionsResult to DirectionsResult array programatically, you need to invoke Execute method from TGMDirections object.
However you can do something like this
procedure TForm1.Button1Click(Sender: TObject);
var
DR: TDirectionsResult;
begin
DR:= TDirectionsResult.Create(GMDirection1, 1);
DR.XMLData.Text := Memo1.Lines.Text;
ShowMessage(DR.Routes[0].Leg[0].EndAddress);
end;
That is, you can work without problems with your object and you can access to all properties and methods.
Note the assignation between XMLData and Memo.Lines, don't assign line to line because the control of XML is made on OnChange event of XMLData.
Regards.
I have a form with 2 grids showing records selected using master-detail option on Devart UniQuery. This working very nice showing the wanted records in detail that relates to master.
I have the option to select records (companies) using a filter. This is done by 30 buttons with a letter on each and then when pressing one I set the filter with this code
procedure TfrmJsCompanies.ButtonClick(Sender: TObject);
var
ButtonValue: char;
FilterString: string;
begin
ButtonValue := (Sender as TcxButton).Caption[1];
FilterString := ButtonValue + '%';
with grdCompaniesView1.DataController.Filter.Root do
begin
Clear;
BoolOperatorKind := fboOr;
AddItem(colCompany_Name, foLike, FilterString, FilterString);
end;
grdCompaniesView1.DataController.Filter.Active := True;
grdCompaniesView1.FilterRow.Visible := False;
ActiveControl := grdCompanies;
end;
If I do this i get the result I expect unless I first press a button that gives me master records that has detail records and there after press a button that gives me no master records - in this case the detail records from the previous selection are still shown in my detail grid
What can I do to get rid of this?
This behavior is caused by the fact that filtering is executed on the cxGrid level, but not on the DataSet level and, as a result, the DataSet is not filtered.
One way to deal with that could be:
procedure TForm1.DetailViewFilterRecord(ADataController: TcxCustomDataController; ARecordIndex: Integer;
var Accept: Boolean);
begin
Accept := MasterView.DataController.FilteredRecordCount >0;
end;
procedure TForm1.MasterViewDataControllerFilterChanged(Sender: TObject);
begin
DetailView.DataController.Refresh
end;