Run-time Equivalent to Assign Local Data... for TClientDataSet and TSQLQuery - delphi

The TSQLQuery class is unidirectional, so for it to be used as a source for a data-bound TDBGrid, a TClientDataSet needs to be linked between the TSQLQuery and the TDataSource that the TDBGrid is bound to.
I can connect the TSQLConnection and make the TSQLQuery active at design-time, with specified params, and then I can right-click on the CDS and choose the "Assign Local Data..." option to have it grab the data from the TSQLQuery, which then appears in the TDBGrid via the linked TDataSource.
The amount of time between choosing "Assign Local Data..." and the data actually appearing in the grid is very short, so I am looking for the way to replicate this at run-time.
Supposedly, I can set the Data property of the CDS to the Data of the source, but a TSQLQuery has no Data property. There was a post about using a provider, but
DataSetProvider1.DataSet := SQLQuery1;
ClientDataSet1.Data := DataSetProvider1.Data;
throws an access violation,
I did implement the data copy by looping through the TSQLQuery and appending the records to the TClientDataSet, but that was a lot slower than the "Assign Local Data...".
[Edit 1]
All the components I need are hooked up at design-time, and I can make the TSQLConnection active, then the TSQLQuery, then the TClientDataSet and the TDBGrid displays the data from the parameterised query defined in the TSQLQuery.
In the OnChange event of a TComboBox, I need to refresh the query using a different parameter and have the grid show the relevant results, so I Close the TSQLQuery, change the ParamByName value, Open the TSQLQuery and then call ClientDataSet1.Last to highlight the last row in the grid.
That gives me a "Cannot perform this operation on a closed dataset" error, so I use
ClientDataSet1.Active := true;
but that throws an "Access Violation".
All the examples I can find are all about dropping components onto a form, linking them together, and they work. Well, yes they do, but I need to change properties in code at run-time and still have it work, which it just refuses to do.
This is really beginning to frustrate me.
[Edit 2]
So I followed the example on the Embarcadero site for building a VCL Forms dbExpress Database Application, substituting my database connection details for the Interbase one the example uses.
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Building_a_VCL_Forms_dbExpress_Database_Application
In the designer, everything looked fine and the grid was showing the results from the query, but when I went to run it using F9, I was getting an "Access Violation" thrown from within TCustomClientDataSet.InternalCheck.

Turns out this is a known MIDAS.DLL versioning problem and can be resolved by including MIDASLib in the uses clause of the form. It is just unfortunate that the Datasnap.DBClient code is still throwing Access Violations instead of proper messages, especially since this problem was reported in 2013.

You can use this code. Just change TUniConnection and TUniQuery to what You are using:
Procedure CdsAssignTable(aTableName:String; aCds : TClientDataSet; aCon
:TUniConnection );
Var
aQUery : TUniQuery;
aProvider : TDataSetProvider;
begin
if aCon=Nil then raise Exception.Create('aCon=Nil');
if aCds=Nil then aCds:=TClientDataSet.Create(aCon.Owner);
aQUery:=TUniQuery.Create(Nil);
aQUery.SQL.Text:='select * from '+aTableName;
aQUery.Connection:=aCon;
aQUery.Open;
aProvider:=TDataSetProvider.Create(Nil);
aProvider.DataSet:=aQUery;
aCds.Data:=aProvider.Data;
FreeAndNil(aProvider);
FreeAndNil(aQUery);
End;

Related

How to use Microsoft Access In your login form for delphi

OK, so I am a self taught basic coder by watching videos on how to do basic coding with Delphi and i have successfully created an application where i use a text file for a login form with usernames and passwords being checked (with a lot of help from videos). How can i use Microsoft Access for the login for the username and a password? I struggle to find a video on how it works. I found a video telling me how to connect ado tables and connections, but i am struggling with the login. Any help would be appreciated.
If you can follow the video about Ado tables, all you are missing is how to configure the TAdoConnection you use to connect to the database. Assuming you've got the project set up:
In the IDE, click your TAdoConnection - usually it would be called AdoConnection1 by default.
In the Object Inspector, click AdoConnection1's ConnectionString property.
In the Data Link properties pop-up, select the MS Office 12.0 Access Database Engine OLE DBProvider (the version number may be different on your system), then click `Next'.
On the next tab, in item 1, enter your database's exact name and extension, including the full path to it.
In item 2, enter the User name
Then click Test connection. Assuming you get Test connection succeeded, you're done.
Try that and see how you get on.
Btw, very occasionally you may come across Delphi Ado projects that don't have a TAdoConnection; in that case, you configure the connection of the TAdoDataSet component (e.g. a TAdoTable) by the above method.
If you would prefer to start with a blank form and set it up from scratch yourself, do the following before the above steps:
Place the followinng components on the form: a TAdoConnection; a TAdoTable; a TDataSource (on the Data Access tab of the Component Palette); a TDBGrid and a TDBNavigator (both on the Data controls tab. Then wire them up as follows:
In the Object Inspector set the DataSource properties of DBNavigator1 and DBGrid1 to DataSource1
Set DataSource1's DataSet property to AdoTable1
Set AdoTable1's Connection property to AdoConnection1
Then, configure AdoConnection1 as detailed above.
Finally
Set AdoTable1's TableName property to the name of a table in your database, thn set its Active property to True.
Compile and run.
Once you've got it working, set AdoTable1's Active property to False and instead set it to open in Form1's FormCreate event. You can also set AdoConnection1's LoginPrompt to False if you prefer.

How to get each row from a TDBGrid in c++

I am using embarcadero c++ builder and i want to copy the result of a query contained in a DBGrid, is there a way to get each row from the DBGrid?
It depends on what you mean by "copy the result," as well as what type of database you are using. A TDBGrid is typically used to display and edit data visually. It's not the most efficient way to get information from a database and put it into a data structure.
So, if what you want is to (non-visually) do some action for each row of a SQL result set, you should probably use a TSQLQuery. This object has useful methods and properties (i.e., First(), Next(), and Eof) that allow you to iterate through the result set.
You don't get the result from the TDBGrid directly; you get it from the TDataSet attached to the TDataSource that is connected to the TDbGrid. IOW, you read it directly from the TQuery/TADOQuery/TSQLQuery that you use to execute the query. As you've not mentioned which DBMS and data components you're using, it's impossible to be more specific.
You can find the component that is executing the query at design-time (in the IDE) by clicking on the grid and inspecting it's DataSource property in the Object Inspector's Properties page, which will give you the name of the TDataSource component. Clicking on that datasource and inspecting it's DataSet property will give you the name of the query component which is running the SQL statement. You can read from that component to retrieve the data that is returned by executing the statement.

Set up SQL Database table in Delphi with TDBGrid (Unidirectional error)

How can i set the whole table of a database to show in my delphi form? Using TDBGrid i presume; but when I configure the data source (connected to a query) I receive an error message about it being Unidirectional. I've heard about a Clientdataset but that didnt seem to work. Could i have some clear instructions on how to do this please? Thank you in advance, Toby.
You say you are using TSQLQuery. This is one of the dbExpress components which are designed to be Unidirectional only (except the TSimpleDataSet). You either have to connect the TSQLQuery to a TDataSetProvider and TClientDataSet or change your query component to one that will buffer the data locally.
To use TDataSetProvider and TClientDataSet:
Set the DataSet property of TDataSetProvider to the SQLQuery.
Set the ProviderName property of the TClientDataSet to the DataSetProvider.
When the ClientDataset is opened, it will contain the data from your SQLQuery.
Set the DataSet Property of your TDataSource to the ClientDataset so the data can be displayed in your DBGrid.
Since you appear to be new to using databases with Delphi, I would recommend you use a different query component, because using the TDataSetProvider and TClientDataSet can be complicated. I suggest
TSimpleDatSet in dbExpress,
TADOQUery or TADODataset in dbGo,
TQuery in BDE (not recommended),
TFDQuery in FireDAC, or
other third party query components.

Fast Reports Non Database

I have been using Report Builder for some years, but I am getting tired of the cha-ching, cha-ching. It is a great reporting tool for "non database" reports.
I have started playing around with Fast Reports and I am utterly flustered with it. It seems a great reporting tool for databases but big question mark concerning complex "non database" reports. Their demos and help are horrible.
Wished I could show a report I am talking about. The report is a serial communications report that has operating system information which of course is single in nature. It has 4 distinct table which has installed serial ports and USB Serial Device tables. It also has a summary memo.
Has anyone successfully designed a report of the above configuration in Fast Reports? And yes, I have posted the same query with Fast Reports. Just want other opinions.
Thanks in advance.
I've extended the option provided by #jrodenhi's answer, which seems to be the proper way to do what you want (thus I've made this answer a community wiki). So here are the two options you can use (and I think there will be more of them):
1. Define custom variables
The answer by #jrodenhi shows how to add report variables in code. I'll try to show here, how to define them in report designer.
Open report designer
Go to menu Report / Variables...
In the Edit Variables window create a new category by clicking the Category button. Then you can rename the category the same way as you do e.g. for files in Windows Explorer:
Then you can declare a custom variable by clicking Variable button. You can give to the variable some meaningful name the same way as you do e.g. for files in Windows Explorer:
After this save your changes by clicking OK button:
Then you'll get back to the report designer, where you can find your just declared variables in the Data Tree pane tab Variables from where you can drag and drop the variables to the report:
After you refine all component positions and properties you can close the report designer and go back to the Delphi IDE. There you can write a handler for the OnGetValue event of your report and if its VarName parameter equals to your variable, change its Value parameter to the value you want to give to the associated report component:
procedure TForm1.frxReport1GetValue(const VarName: string; var Value: Variant);
begin
if VarName = 'MyVariable' then
Value := 'This is a new value!';
end;
2. Modify report components directly from Delphi code
There is an option of direct access to report components from your Delphi code. For instance, to find a certain report component by name you can use the FindObject method of the TfrxReport object. Then you can follow the usual pattern of checking if the returned reference is of type of the control you want to access and if so, you can access the instance by typecasting, as usually.
For example to find a TfrxMemoView object of name Memo1 on the frxReport1 report and modify its text you may write:
var
Component: TfrxComponent;
begin
Component := frxReport1.FindObject('Memo1');
if Component is TfrxMemoView then
TfrxMemoView(Component).Memo.Text := 'New text';
end;
Most of the reports I write are for payroll. In addition to the table based data that makes up the bulk of the report, there is usually a significant amount of singular data, such as user and employer information that is frequently most easily entered as a series of variables taken from a filter dialog that runs before the report. Once the filter has run I iterate through all of its variables and add them to the report variables. Maybe you could pick up your singular system variables and do the same.
This is my routine that runs just before I prepare a report. It not only adds variables, it also adds functions defined in Delphi that can be used in the report:
procedure TFastReportDriver.SetVariables(sVariables: String; var frxReport: TfrxReport);
var i,
iDetail: Integer;
sl: TStringList;
sVariable,
sValue: String;
begin
sl := TStringList.Create;
sl.CommaText := sVariables;
frxReport.Variables.Clear;
frxReport.Variables[' Filter'] := Null;
for i := 0 to sl.Count - 1 do begin
sVariable := sl.Names[i];
sValue := Qtd(sl.ValueFromIndex[i]);
frxReport.Variables.AddVariable('Filter', sVariable, sValue);
end;
frxReport.AddFunction('procedure CreateCheck(hPaystub, iCheckNo: Integer)');
frxReport.AddFunction('function NumberToWords(cAmount: Currency): String');
frxReport.OnUserFunction := repChecksUserFunction;
sl.Free;
end;
Then, in your code, you call frxReport.DesignReport and at run time you can drag and drop your variables onto your report:
And, if you have defined any functions like above, they show up under the functions tab.
I've had success with this method:
Use an in-memory table, such as TkbmMemTable, TdxMemTable or any in-memory table.
Link the in-memory table to the TfrDataSet.
Populate the in-memory table using usual TDataset methods and run the report.
You might want to take a look at using NexusDB as a way to hold data for the report. NexusDB is a database which besides being a full client server database can also run as a totaly in-memory database with full SQL support and no external dlls or anything required as it is compiled into your app. They have a free embedded version that will satisfy your needs. I use it for many things, it honestly a wonderful tool to have in your toolbox.

Delphi Infopower Grid with dbNavigator

I have a infoPower Grid that is bound to the TQuery Component with following sql
SELECT membership_number, message_id, msgText,target, date_time_creation,
date_time_display
FROM MessageMembership
WHERE membership_number = :membershipnumber
ORDER BY date_time_display desc
qalso I have bind it to the dbnavigator as well as DataSource which is binded with The InfoPOwer Grid. Now insert and delete and update buttons are enable but insert button dont let me type the new values also update button dnt let me type the updated value. But delete is working as expected. What can be possible issue or step I am missing?
The code for executing TQuery is :
MessageMembershipSelectQuery.ParamByName('membershipnumber').AsString :=
custQuery.FieldByName('cust_code').AsString;
MessageMembershipSelectQuery.Open;
Please help me.
TQuery is a BDE dataset. It won´t let you edit it´s contents unless the RequestLive property is set to True and you have a TUpdateSQL component bound to it, by the UpdateObject property. Those two properties work together to enable the batch mode of BDE, which is named cached updates.
However, I strongly advise you to not invest in BDE, not even to learn how to program in Delphi. Much better is to start studing the way of TClientDataset. It´s a much more functional dataset that will give you more control over all parts of your application, from selecting to updating data.
The nice thing about TClientDataset is that you will isolate the SQL dependecy of your application away from the main domain logic, which will be written all around TClientDataset. That logic will be data-dependent, not SQL-dependent.
Think about it: now you have an application that works with MySQL. Then, suddenly, you have to port it to work with Oracle. If you isolate your SQL dependency away from the main code, this refactoring will be much faster and safer.
Remember: BDE is dead.

Resources