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.
Related
I've encountered a very strange DBComboBox problem in a master/detail app using Access via ADO. If you have a DBComboBox (.Style=csDropDown) containing a list of items and you enter some text that doesn't exist in the list, the value in the table's DBComboBox field won't appear when navigating back to that record. I've used the DBNavigator.OnClick code below to attempt to resolve this problem but it only works if the first record in the table contains a value not in the list. When you change the value of the DBComboBox in the first record to one that is in the list, no nonconforming items will appear in the DBComboBox text. Has anyone found a solution to this?
procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);
var
SavePlace : TBookmark;
begin
if (DBComboBox1.Text='') then begin
SavePlace := TADODataSet(DBNavigator1.DataSource.DataSet).GetBookmark;
TADODataSet(DBNavigator1.DataSource.DataSet).Requery;
TADODataSet(DBNavigator1.DataSource.DataSet).GotoBookMark(SavePlace);
TADODataSet(DBNavigator1.DataSource.DataSet).FreeBookMark(SavePlace);
end;
end;
Unfortunately I don't have XE installed, but I have made a sample project which
reproduces your problem in D7 and Seattle. The code is shown below and I think
you will find that if you follow the exact steps below, it shows that there is something
rather strange going on. Update See the bottom of the answer for a possible work-around, which I think is preferable to the code you quote in your q.
As you'll see, except for Form1 itself, all the components are created at runtime
entirely in code. This is to remove any doubt whether the behaviour is caused
by some obscure property setting (it isn't) and in case you wish to submit it
to EMBA as a bug report. For a similar reason I've used a TClientDataSet so that
the app does not depend on any external data.
Steps (please follow steps 4-7 exactly the first time you try them)
Restart the IDE and create a new project and edit the .Pas file for the main form as shown below. The reason for restarting the IDE is that I discovered that if it has been running for a long time (two days in my case) the details of the misbehaviour
of the app change slightly).
Compile and run.
The app will start with the first from in the DBGrid selected.
Type anything (an 'X' will do) into the DBComboBox, then click the Save toolbutton
on the DBNavigator.
Click the Next (>) toolbutton on the DBNavigator once only. The DBComboBox now displays
'Two'.
Click the Prior (<) toolbutton on the DBNavigator once only. The DBComboBox is now empty.
Click the Prior (<) toolbutton on the DBNavigator once only. The DBComboBox now displays
what you typed in step 4.
Close the app. Most likely the IDE debugger will catch a fault and open the CPU window.
This fault occurs on the line
DestroyWindow(FHandle);
in TApplication.Destroy. I am no Windows internals expert but I think it's likely that this is because of some corruption being caused by whatever causes the blank result in step 6. The fact that
step 7 causes the DBComboBox to correctly display what you typed makes me suspect that cause is actually
in the way the DBComboBox interacts with its FieldDataLink which connects it to the dataset.
Btw, the fact that the fault does not occur if you call DBComboBox1.Free in TForm1's FormDestroy
seems to me to confirm that the fault is related to whatever is causing your problem.
All this, and the fact that it has apparently passed unnoticed in the 25 years of Delphi, seems very strange
to me. This demo app can show up another quirk that's been lurking in the DBGrid for a similar length ot time. To see
it:
Comment out all the references to the DBComboBox and reinstate dgMultiSelect amongst the grid options in the
line that sets them. Compile and run the app.
Click in the cell in the Name column for the first row, type something and save it.
Click the Next toolbutton once. The first row does not de-select itself as it should.
AFAICT (by displaying the DBGrid's count of Bookmarks on the form's caption) this is not
because it has saved a bookmark on the first row.
While I've been writing this, a possible work-around has occurred to me, which I'll updated
this to include if I can get it to work.
Code
type
TForm1 = class(TForm)
procedure FormCreate(Sender : TObject);
private
procedure SetUpDataSet;
procedure SetUpGUI;
protected
public
ClientDataSet1 : TClientDataSet;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
DBNavigator1: TDBNavigator;
DBComboBox1: TDBComboBox;
end;
[...]
procedure TForm1.SetUpGUI;
begin
ClientDataset1 := TClientDataSet.Create(Self);
DataSource1 := TDataSource.Create(Self);
DataSource1.DataSet := ClientDataSet1;
DBGrid1 := TDBGrid.Create(Self);
DBGrid1.Top := 8;
DBGrid1.Left := 8;
DBGrid1.Width := 425;
DBGrid1.Options := [dgEditing, dgTitles, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit{, dgMultiSelect}];
DBGrid1.DataSource := DataSource1;
DBGrid1.Parent := Self;
DBNavigator1 := TDBNavigator.Create(Self);
DBNavigator1.DataSource := DataSource1;
DBNavigator1.Top := 144;
DBNavigator1.Left := 16;
DBNavigator1.Parent := Self;
DBComboBox1 := TDBComboBox.Create(Self);
DBComboBox1.DataField := 'Name';
DBComboBox1.DataSource := DataSource1;
DBComboBox1.Top := 240;
DBComboBox1.Left := 16;
DBComboBox1.Parent := Self;
end;
procedure TForm1.SetUpDataSet;
var
Field : TField;
begin
// Create 2 fields in the CDS
Field := TIntegerField.Create(Self);
Field.FieldName := 'ID';
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
Field := TStringField.Create(Self);
Field.FieldName := 'Name';
Field.Size := 40;
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetUpGUI;
SetUpDataSet;
// Set up DBComboBox
DBComboBox1.Style := csDropDown;
DBComboBox1.Items.Add('One');
DBComboBox1.Items.Add('Two');
DBComboBox1.Items.Add('Three');
// Next, set up the CDS
ClientDataSet1.CreateDataSet;
ClientDataSet1.InsertRecord([1, '']);
ClientDataSet1.InsertRecord([2, 'Two']);
ClientDataSet1.InsertRecord([3, '']);
ClientDataSet1.First;
end;
Possible work-around Add the following method to Form1:
procedure TForm1.ClientDataSet1AfterScroll(DataSet: TDataSet);
var
S : String;
begin
S := DataSet.FieldByName('Name').AsString;
if S <> DbComboBox1.Text then
DbComboBox1.Text := S;
Caption := IntToStr(DBGrid1.SelectedRows.Count);
end;
Then, in the SetUpGUI method, add the following immediately after the line where ClientDataSet1 is created:
ClientDataset1.AfterScroll := ClientDataSet1AfterScroll;
I have not tested this thoroughly, but it seems to work in the test conditions of the steps I've described above.
if Length(idStrArray)>0 then
begin
with DataModule4.ADQueryTemp do
begin
Close;
SQL.Clear;
SQL.Add('SELECT id, pato, ftest, res FROM tbl ');
SQL.Add('WHERE id IN ('+idStrArray+')');
Open;
(rprMasterDataFish as Tfrxmasterdata).DataSet := frxDst_Multi;
(rprMasterDataFish as Tfrxmasterdata).DataSetName := 'Multi';
end;
end;
Hello,
I have TfrxDBDataset component. I can add fields from table like above. But i also want to add fields and values manually at runtime.
I have text file like this :
id note
1 sample
2 sample
I want to read this text file and insert note to frxDst_Multi. Is this possible ?
I dont want to create a new column as note in tbl. Because, i have too many mysql server.
Thanks in advice,
You can't add fields to a dataset while it is open, so you have to do it before
it is opened, either in code or using the TDataSet fields editor. If you are
doing it in code, you can add the field in the dataset's BeforeOpen
event.
The next problem is that is you don't want to field to be bound to the table the
dataset accesses, you need to add it as a calculated field and set its value
in the dataset's`OnCalcFields' event - see example below.
Ideally, the added field would be a TMemoField, but unfortunately a TMemoField
can't be a calculated field (FieldKind = ftMemo). So probably the best thing you can do is to make it a String field, but then you will need to
give it a fixed maximum size and truncate the field's value at that size when you calculate its value.
Btw, I don't know whether your TfrxDBDataset supports the fkInternalCalc fieldkind, but if it does, then you could try adding the note field as a TMemoField instead of a TStringField one.
The one thing I haven't been able to do is to load the field's value from
an external file because you haven't said in your q how to determine the
name of the file which is to be read.
Obviously, the IsNull check in the frxDst_MultiCalcFields event is to avoid the overhead of reloading the file if its contents have already been read.
const
NoteFieldSize = 4096;
procedure TForm1.AddNoteField;
var
NoteField : TField;
begin
if frxDst_Multi.FindField('Note') = Nil then begin
NoteField := TStringField.Create(frxDst_Multi);
NoteField.FieldName := 'Note';
NoteField.Size := NoteFieldSize;
NoteField.FieldKind := fkCalculated;
NoteField.DataSet := frxDst_Multi;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
frxDst_Multi.Open;
end;
procedure TForm1.frxDst_MultiCalcFields(DataSet: TDataSet);
var
S : String;
begin
if DataSet.FieldByName('Note').IsNull then begin
S := 'a note'; // replace by code to set the field value
DataSet.FieldByName('Note').AsString := Copy(S, 1, NoteFieldSize);
end;
end;
procedure TForm1.frxDst_MultiBeforeOpen(DataSet: TDataSet);
begin
AddNoteField;
end;
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.
I am currently coding a school project. I apologize in advance for a possibly noob question.
I need to make a system for a fictional NGO, and I need to have all users register and login whenever they use the program.
I use editboxes and maskedits to receive the login data. The login screen has two editboxes, one for the username and one for the password. The registration screen is the same form, resized and with two additional, dynamic editboxes: One for confirming the password and another for the user's e-mail address.
Now, I made the static editboxes to contain a default value: If the user enters the editbox, then the default value disappears. If the user then exits the editbox without entering any values, the default value reappears. I haven't managed to get the dynamic editboxes to do the same.
How does one get OnActivate event handlers such as OnEnter/OnExit to fire when an already created dynamic component is activated?
If you are using a relatively modern Delphi version that supports XP+ Visual Styles, then TEdit has a TextHint property that does exactly what you are looking for, without the need to use any events at all.
procedure TMyForm.FormCreate(Sender: TObject);
var
Edit: TEdit;
begin
Edit := TEdit.Create(Self);
Edit.Parent := ...;
...
Edit.TextHint := 'default text here';
end;
Otherwise, if you really want to use the events, then you can do this instead:
procedure TMyForm.FormCreate(Sender: TObject);
var
Edit: TEdit;
begin
Edit := TEdit.Create(Self);
Edit.Parent := ...;
...
Edit.Text := 'default text here';
Edit.OnEnter := EditEnter;
Edit.OnExit := EditExit;
end;
procedure TMyForm.EditEnter(Sender: TObject);
begin
if TEdit(Sender).Text = 'default text here' then
TEdit(Sender).Text := '';
end;
procedure TMyForm.EditExit(Sender: TObject);
begin
if TEdit(Sender).Text = '' then
TEdit(Sender).Text := 'default text here';
end;
1st off I am Still a little green to Delphi so this might be a "mundane detail" that's being over looked. [sorry in advance]
I have a need to create a TSQLDataset or TClientDataSet from an Oracle 11g cursor contained in a package. I am using Delphi XE2 and DBExpress to connect to the DB and DataSnap to send the data back to the client.
I'm having problems executing the stored procedure from the Delphi code.
Package Head:
create or replace
PACKAGE KP_DATASNAPTEST AS
procedure GetFaxData(abbr varchar2, Res out SYS_REFCURSOR);
END KP_DATASNAPTEST;
Package Body:
create or replace
PACKAGE body KP_DATASNAPTEST AS
procedure GetFaxData(abbr varchar2, Res out SYS_REFCURSOR)is
Begin
open Res for
SELECT Name,
Address1,
City,
fax_nbr
FROM name
JOIN phone on name.Abrv = phone.abrv
WHERE phone.fax_nbr is not null and name.abrv = abbr;
end;
END KP_DATASNAPTEST;
I have no problem executing this procedure in SQL Developer the problem resides in this code on the DataSnap server:
function TKPSnapMethods.getCDS_Data2(): OleVariant;
var
cds: TClientDataSet;
dsp: TDataSetProvider;
strProc: TSQLStoredProc;
begin
strProc := TSQLStoredProc.Create(self);
try
strProc.MaxBlobSize := -1;
strProc.SQLConnection:= SQLCon;//TSQLConnection
dsp := TDataSetProvider.Create(self);
try
dsp.ResolveToDataSet := True;
dsp.Exported := False;
dsp.DataSet := strProc;
cds := TClientDataSet.Create(self);
try
cds.DisableStringTrim := True;
cds.ReadOnly := True;
cds.SetProvider(dsp);
strProc.Close;
strProc.StoredProcName:= 'KP_DATASNAPTEST.GetFaxData';
strProc.ParamCheck:= true;
strProc.ParamByName('abbr').AsString:= 'ZZZTOP';
strProc.Open; //<--Error: Parameter 'Abbr' not found.
cds.Open;
Result := cds.Data;
finally
FreeAndNil(cds);
end;
finally
FreeAndNil(dsp);
end;
finally
FreeAndNil(strProc);
self.SQLCon.Close;
end;
end;
I have also tried assigning the param value through the ClientDataSet without any luck.
I would not be apposed to returning a TDataSet from the function if its easier or produces results. The data is used to populate custom object attributes.
As paulsm4 mentioned in this answer, Delphi doesn't care about getting stored procedure parameter descriptors, and so that you have to it by yourself. To get params of the Oracle stored procedure from a package, you can try to use the GetProcedureParams method to fill the list with parameter descriptors and with the LoadParamListItems procedure fill with that list Params collection. In code it might look like follows.
Please note, that following code was written just in browser according to documentation, so it's untested. And yes, about freeing ProcParams variable, this is done by the FreeProcParams procedure:
var
ProcParams: TList;
StoredProc: TSQLStoredProc;
...
begin
...
StoredProc.PackageName := 'KP_DATASNAPTEST';
StoredProc.StoredProcName := 'GetFaxData';
ProcParams := TList.Create;
try
GetProcedureParams('GetFaxData', 'KP_DATASNAPTEST', ProcParams);
LoadParamListItems(StoredProc.Params, ProcParams);
StoredProc.ParamByName('abbr').AsString := 'ZZZTOP';
StoredProc.Open;
finally
FreeProcParams(ProcParams);
end;
...
end;
I don't think Delphi will automagically recognize Oracle parameter names and fill them in for you. I think you need to add the parameters. For example:
with strProc.Params.Add do
begin
Name := 'abbr';
ParamType := ptInput;
Value := ZZZTOP';
...
end;