Grouping items inside TListView object? - delphi

I am trying to group the items inside TListView object, but I cant find the class responsible for grouping the objects, neither i were able to find such inside the documentation.
What is the class responsible about grouping items inside TListView object, and how to use it properly ?
The platform is Firemonkey ( Android/iOS) / Delphi XE6

The property I believe you're referring to is TListGroups, a collection that holds TListGroup items. There's a demo provided in the Delphi documentation.
Unfortunately, it's only available in the VCL and not FMX, as the underlying functionality is part of the Windows ListView control that TListView wraps.
The closest you can get in FMX is using TListBox and a TListBoxGroupHeader, which is covered in the Multi-Device Tutorial Using ListBox Components to Display a Table View (iOS and Android) in the docwiki:
procedure TForm1.FormCreate(Sender: TObject);
var
c: Char;
i: Integer;
Buffer: String;
ListBoxItem : TListBoxItem;
ListBoxGroupHeader : TListBoxGroupHeader;
begin
ListBox1.BeginUpdate;
for c := 'a' to 'z' do
begin
// Add header ('A' to 'Z') to the List
ListBoxGroupHeader := TListBoxGroupHeader.Create(ListBox1);
ListBoxGroupHeader.Text := UpperCase(c);
ListBox1.AddObject(ListBoxGroupHeader);
// Add items ('a', 'aa', 'aaa', 'b', 'bb', 'bbb', 'c', ...) to the list
for i := 1 to 3 do
begin
// StringOfChar returns a string with a specified number of repeating characters.
Buffer := StringOfChar(c, i);
// Simply add item
// ListBox1.Items.Add(Buffer);
// or, you can add items by creating an instance of TListBoxItem by yourself
ListBoxItem := TListBoxItem.Create(ListBox1);
ListBoxItem.Text := Buffer;
// (aNone=0, aMore=1, aDetail=2, aCheckmark=3)
ListBoxItem.ItemData.Accessory := TListBoxItemData.TAccessory(i);
ListBox1.AddObject(ListBoxItem);
end;
end;
ListBox1.EndUpdate;
end;
This produces (image from the indicated docwiki)

This is easily accomplished in FMX TListview control while using Livebindings by connecting your TListviewItem.header.break field to the fieled you want to group on (it will be a field in your db that is redundant over many records).

Related

How to iterate into components inside a TTabSheet

I'm trying to iterate between all components inside a TTabsheet. Problem is, inside this tab there are only a memo and a edit, but my code iterate between components in all form. What am I'm missing ?
var i : integer;
begin
with PageControl1.ActivePage do
for i := 0 to componentcount-1 do
begin
// componentcount should be 2, but actually is 95
components[i].doSomething;
end;
end;
I had something like this, where a button click caused code to traverse over all the controls that were on a tabsheet that was on a page control, using the components array. Later on I changed it to the following, that uses the controls array of the given tabsheet.
procedure TShowDocsByRank.CleanBtnClick(Sender: TObject);
var
i: integer;
begin
for i:= 0 to tabsheet1.controlcount - 1 do
if tabsheet1.controls[i] is TLabeledEdit
then TLabeledEdit (tabsheet1.controls[i]).text:= ''
else if tabsheet1.controls[i] is TComboBox
then TComboBox (tabsheet1.controls[i]).text:= '-';
end;
Perhaps 'with' might have something to do about it. You cant really tell what the return value for 'componentcount' will be (might even return number of components for the form itself?).

How to get data for FastReport (Delphi) from an TEdit?

I created a report using FastReport, but the only way I know to get data to that report is from a database, I want to get data from a TEdit and I don't want to store anything, just writing in TEdit + click on the button (fastreport.preview) + print and Done.
How can I do that ?
Please explain am new with Delphi and FastReport.
You can use the OnGetValue event of your TfrxReport component as follows:
procedure TForm1.frxReport1GetValue(const VarName: string; var Value: Variant);
begin
if(VarName = 'MyVariable') then
begin
Value := Edit1.Text;
end;
end;
Then you just need to add a memo item to the report and set its value to [MyVariable].
One possible approach is to access the TfrxReport and TfrxMemoView components at runtime. Note, that when you don't have a dataset, the Master Data band won't be printed, so you should use another band.
You may use the following code as a basic example. Just place one TfrxReportTitle band (named 'ReportTitle1') and one TfrxMemoView text object (named 'Memo1') on your TfrxReport component.
procedure TfrmMain.btnReportClick(Sender: TObject);
var
memo: TfrxMemoView;
band: TfrxReportTitle;
begin
// Get the band
band := (rptDemo.Report.FindObject('ReportTitle1') as TfrxReportTitle);
// Create a memo
memo := TfrxMemoView.Create(band);
memo.CreateUniqueName;
memo.ParentFont := True;
memo.Text := edtReport.Text;
memo.SetBounds(100, 1, 100, 16);
memo.HAlign := haLeft;
memo.AutoWidth := False;
// Use existing memo
memo := (rptDemo.Report.FindObject('Memo1') as TfrxMemoView);
memo.Text := edtReport.Text;
// Preview report
rptDemo.ShowReport(False);
end;
Notes: This is a working example, tested with FastReport 4.7.

How to clone a dataset structure (TFDQuery) in Delphi 10?

Can anyone help me to clone a TFDQuery in run-time? I'm coding in Delphi Tokyo , I have a Datamodule with a TFDQuery in which I defined all fields properties using Fields Editor at design time, in this way my DBGrid1 that points to this a Datamodule of this dataset, has all columns properly formated (dislay names, width, format, order). During run-time I need to create new instances of TFDQuery, TDatamodule and link these new objects with the Dbgrid1. I need this new TFDQuery be identiical to the existing one defined at design-time in order to keep DBgrid1 with same display names, display width and display formats as the design-time!
I tried the following approaches to copy dataset field definitions :
**1st Approach : Method Assign for TFDQuery (didn't work) **
type
TFormDados = class(TForm)
Edit1: TEdit;
Button1: TButton;
DBGrid1: TDBGrid;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
vconnection : TFDConnection;
vdataset : TFDQuery;
vdatasource : Tdatasource;
public
{ Public declarations }
end;
var
FormDados: TFormDados;
implementation
{$R *.dfm}
Uses
unitdata;
procedure TFormDados.Button1Click(Sender: TObject);
var
i : integer;
begin
vconnection := TFDConnection.Create(nil);
vconnection.Assign(Dtmodule.FDConGrafico);
vdataset := TFDQuery.Create(nil);
vdataset.Connection := vconnection;
vdataset.Assign(Dtmodule.FDQueryDados); // Runtime Error : Cannot assign a TFDQuery to a TFDQuery
2nd Approach : Assign FieldDefs from the existing Dataset to the new one - didn't work !
...
vdataset.FieldDefs.Assign(Dtmodule.FDQueryDados.FieldDefs);
vdataset.sql := Dtmodule.FDQueryDados.sql;
vdataset.params := Dtmodule.FDQueryDados.Params;
vdataset.FieldDefs.Update;
vdataset.CreateDataSet;
vdatasource := Tdatasource.create(nil);
vdatasource.DataSet := vdataset;
dbgrid1.DataSource := vdatasource;
vdataset.close;
vdataset.Params[0].Asinteger := strtoint(edit1.Text);
vdataset.Params[1].Asinteger := strtoint(edit2.Text);
vdataset.Open;
Althought Assign method had run, vdataset didn't receive the fields definitions of the existing FDQquery . After open the vdataset , DBGrid1 did not show the columns sequence, labels and formats fro the source dataset , WHY ?
3rd Approach - Copy fields definition, one by one - didn't work
for i:=0 to Dtmodule.FDQueryDados.Fields.Count -1 do
begin
with vdataset.FieldDefs.AddFieldDef do
begin
Name := Dtmodule.FDQueryDados.FieldDefs[i].Name;
Datatype := Dtmodule.FDQueryDados.FieldDefs[i].DataType;
Displayname := Dtmodule.FDQueryDados.FieldDefs[i].Displayname;
Fieldno := Dtmodule.FDQueryDados.FieldDefs[i].FieldNo;
end;
end;
vdataset.FieldDefs.Update;
vdataset.CreateDataSet;
vdatasource := Tdatasource.create(nil);
vdatasource.DataSet := vdataset;
dbgrid1.DataSource := vdatasource;
...
This code lead to the same result as the approach 2nd, i.e., it run but after opened vdataset , DBGrid1 did not show the columns sequence, labels and formats fro the source dataset.
I appreciate your help to fix the above code OR to implement the right method to copy dataset fields definitions from one existing dataset to a new one.
Thank you all in advance !
When you use the Fields editor for queries you are creating Fields not FieldDefs. From what I can tell the FieldDefs are kept in sync with the FieldsCollection when the component is created (or maybe opened not 100% sure). The Display* properties are not available on the FieldDef object - they only exist on the Field object. When you go to copy the structure you need to iterate the fields. The the method we use is below.
Note that the loop and the items created are "Fields", but we use a temporary FieldDef object to make the code simpler. The TFieldDef.CreatField serves as a class factory method to get the correct type of field i.e. TIntegerField vs TStringField. Also if you are using calculated fields you will need to hookup the OnCalcField event. This method does not do that.
procedure CopyFieldStructure(Source: TDataSet; Target: TDataset);
{^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}
var
Field: TField;
NewField: TField;
FieldDef: TFieldDef;
begin
Target.Fields.Clear;
Target.FieldDefs.Clear;
// Cannot perform the next operation on an opened dataset
if Target.State <> dsInactive then
Target.Close;
for Field in Source.Fields do
begin
// We are going to setup the first part in a FieldDef
// that will set us use the CreateField Call in order to
// get the correct subclass of TField created.
FieldDef := Target.FieldDefs.AddFieldDef;
FieldDef.DataType := Field.DataType;
FieldDef.Size := Field.Size;
FieldDef.Name := Field.FieldName;
NewField := FieldDef.CreateField(Target);
NewField.Visible := Field.Visible;
NewField.DisplayLabel := Field.DisplayLabel;
NewField.DisplayWidth := Field.DisplayWidth;
NewField.EditMask := Field.EditMask;
NewField.Calculated := Field.Calculated;
end;
end;
Here is a similar StackOverflow question. I think this is where I originally took my code from: Is there some better way to copy all DataSet Fields and their properties to another DataSet?
And here is one other blog post that uses a similar approach: How to: Clone TField and TDataset fields structure
Also don't get fooled by the TDataSet.CopyField method. The help makes it seem like it could copy the field structure. When really it copies the current field "values" for any matching field names.

Printing listview items using fastreport in Delphi

I am looking for a simple example or a reference to print items from a listview using fastreport. For example: I have a listview that has three columns: id, name and marks. The listview has n number items each containing id, name and marks of individual students. I need to print the whole listview using fastreport in a tabular format. I googled but couldn't find an exact sample for my scope. Similarly, I couldn't find any documentation regarding this in fastreport demo. I am using Delphi XE8 with default installed fastreport version.
For demonstration purposes.
Place on the form the following components:
TButton;
TListView;
frxReport;
frxUserDataSet;
Double click on frxReport. In the Fastreport designer do
insert ReportTitle, MasterData and PageFooter bands.
Press Report => Data menu items. Check frxUserDataSet check box and press OK button.
Assign MasterData band with frxUserDataSet as double click on MasterData band and select frxUserDataSet, then press 'OK' button.
In MasterData band insert Text object (Memo).
In Memo write [element].
Designer should look like this :
Now we can write some code :
procedure TForm8.FormCreate(Sender: TObject);
var
I: Integer;
Li : TlistItem;
begin
// Just to fill some data in ListView
for I := 1 to 10 do
begin
Li := ListView1.Items.Add;
LI.Caption := 'Col ' + IntToStr(i);
end;
end;
In frxReport1 OnGetValue event write for example :
procedure TForm8.frxReport1GetValue(const VarName: string; var Value: Variant);
begin
if CompareText(VarName, 'element') = 0 then
Value := ListView1.Items[frxUserDataSet1.RecNo].Caption;
end;
And now print data
procedure TForm8.Button1Click(Sender: TObject);
begin
frxUserDataSet1.RangeEnd := reCount;
frxUserDataSet1.RangeEndCount := ListView1.Items.Count;
frxReport1.ShowReport();
end;
The result after Button1 is pressed:
Note : In this answer is used part of FastReport PrintStringList demo.

How to add duplicate data from a button in delphi?

I have two forms with TListview for each form (to show the data I just added).
Its okay when I try to add data from form1 and in Listview(form1), its shown. But i also want, if i add data from form1, Listview(form1) and Listview(Form2) automatically added.
Here is my code:
procedure TForm1.btnAddClick(Sender: TObject);
var
data1,data2 : TListItem;
m1,m2,m3 : String;
dat : TForm2;
begin
m1 := EMom1.Text;
m2 := EMom2.Text;
m3 := EMom3.Text;
//listview form1
data1 := ListView1.Items.Add;
data1.Caption := m1;
data1.SubItems.Add(m2);
data1.SubItems.Add(m3);
//listview form2
data2 := dat.ListView1.Items.Add; {error on this code}
data2.Caption := m1;
data2.SubItems.Add(m2);
data2.SubItems.Add(m3);
data2 := dat.ListView1.Items.Add;
The variable dat has not been initialized. I would expect the compiler to warn you of this. I hope you enabled hints and warnings.
You need to supply a valid reference to a TForm2 instance. I don't know enough about your program to know where you'll get that reference. Presumably you know the answer.
You should also extract the list view item adding code into a method:
procedure AddListItem(lv: TListView; s1, s2, s3: string);
You can then call this function twice passing the two different list views.
Finally, were you able to switch to using virtual list views you would not need two copies of the data.

Resources