how to show Only relevant information in dbgrid delphi - delphi

information:
I have an order form.
With "keuze" and "aantal" it wright a new line. The Orderline gets an OrderID.
But the user may only see the orderline from his OrderID.
How can i make it work that it only shows, for example the OrderID "47" ?
procedure TfmOrder.btInvoerenClick(Sender: TObject);
begin
dm.atOrder.open;
dm.atOrder.Append;
dm.atOrder ['OrderStatus'] := ('Aangemeld');
dm.atOrder ['klantID'] := fminloggen.userid;
dm.atOrder ['OrderDatum'] := Kalender.date;
dm.atOrder ['Opmerkingen'] := leOpmerkingen.text;
dm.atOrder.post;
cbkeuze.Visible := true;
dbRegel.Visible := true;
leAantal.visible := true;
btOpslaan.Visible:= true;
end;
This is the code for making a new Order
procedure TfmOrder.btOpslaanClick(Sender: TObject);
var orderid:string;
begin
dm.atOrderregel.Open;
dm.atDier.open;
dm.atorderregel.Append;
dm.atOrderregel ['AantalDieren'] := leAantal.text;
dm.atOrderregel ['OrderID'] := dm.atOrder ['OrderID'];
dm.atOrderregel ['Diernaam'] := cbKeuze.Text;
dm.atOrderregel.Post;
leaantal.clear;
cbkeuze.ClearSelection;
end;
And this for a new orderline
thanks in advance
I know got a different error using this code:
begin
dm.atorder.Open;
dm.atorder.filter := 'KlantID = ' + (fminloggen.userid);
dm.atorder.filtered := true;
while not dm.atorder.Eof do
begin
cbOrder.Items.Add (dm.atorder['OrderID']);
dm.atOrder.Next;
end;
dm.atOrder.Close;
end;
It gives an error: The arguments are from the wrong type, or doesn't have right reach or are in conflict with each other.
here is userid declared.
var Gevonden: boolean;
userid : string;
begin
dm.atInlog.open;
Gevonden := false;
while (not Gevonden) and (not dm.atInlog.eof) do
begin
if dm.atInlog['email'] = leUser.Text
then
begin
Gevonden := true ;
inlognaam := dm.atInlog['email'];
userid := dm.atInlog['KlantID'];
end
else
dm.atInlog.Next
end;
this is obviously in another form

You can use the Filter property of the data set:
atOrderregel.Filter := 'OrderID = 47';
atOrderregel.Filtered := True;

You can add the grid's columns property statically in the object inspector, showing only the fields you need. If the columns list is empty (default) it is filled with all available fields.
Just add as many columns as you need and link each column to the corresponding field. You can reorder the columns and set the widths and titles individually. There are still some more properties available which are worth to explore.

Im assuming your grid is bound to a datasource component. This datasource is then linked with a TDataset descendant. There are a couple of ways you could acheive the desired filtering of the dataset to display only orderid 47.
Firstly, you could set the Datasets SQL property to contain a (server side) SQL query such as:
SELECT * from table WHERE OrderID = #OrderID
You would also need to create a parameter in the dataset to pass the (changing) value for the required OrderID. So add a new Parameter to the dataset (#OrderID), and then at runtime you can set this parameter value in code, something like:
DataSet.Parameters['#OrderID'].Value := ParameterValue;
Alternatively, you could also FILTER the dataset (client side) to just show the correct data:
Set your SQL property of the dataset to retrive the entire table, something like:
SELECT * FROM table
And then at runtime you could set the Filter property of the dataset to only get OrderID 47:
Dataset.Filter := 'OrderID = '+InttoStr(ParameterValue);
Depending on your needs one method may suit better (performance/memory) wise.
As Najem has commented, there is also a third method - using a Master-Detail dataset relationship. This method works using two datasets, one is the master of the other. When the master table record is changed, the detail dataset is then filtered using the value defined in the Key or MasterFields property of the M-D relatioship.

If you are connected to some datasource you could always create a SQL Query. Something like:
SELECT * FROM YourDBTable WHERE OrderID=47

Related

Delphi 10.4 - Sort Memtable by clicking Stringgrid Header

I feel like an idiot because my question seams so simple but I don't get it done :D
My Settings is that:
One Dataset (Memtable), One Stringgrid. The Grid is bind via live Bindungs.
I would like to sort my Columns by clicking on the GridHeader. In the OnHeaderClick Event I get an tColumn Object. I only can read the Column.Header String, but I changed the Text from the Header to a more speakable Text. When I put Column.header into Memtable.Indexfieldsname Memtable says that field does not exist, what is right, but I don't know how to get the right Fieldname from the column.
What you want is quite straightforward to do. In the example below, which uses the demo data from
the Biolife demo, I've linked the StringgRid to the FDMemTable entirely by binding objects
created in code so that there is no doubt about any of the binding steps or binding properties,
nor the method used to establish the bindings.
procedure TForm2.FormCreate(Sender: TObject);
var
BindSourceDB1 : TBindSourceDB;
LinkGridToDataSourceBindSourceDB1 : TLinkGridToDataSource;
begin
// Note : You need to load FDMemTable1 at design time from the sample Biolife.Fds datafile
// The following code creates a TBindSourceDB which Live-Binds FDMemTable1
// to StringGrid1
//
// As a result, the column header texts will be the fieldnames of FDMemTable1's fields
// However, the code that determines the column on which to sort the StringGrid does not depend
// on this
BindSourceDB1 := TBindSourceDB.Create(Self);
BindSourceDB1.DataSet := FDMemTable1;
LinkGridToDataSourceBindSourceDB1 := TLinkGridToDataSource.Create(Self);
LinkGridToDataSourceBindSourceDB1.DataSource := BindSourceDB1;
LinkGridToDataSourceBindSourceDB1.GridControl := StringGrid1;
end;
procedure TForm2.StringGrid1HeaderClick(Column: TColumn);
// Sorts the STringGrid on the column whose header has been clicked
var
ColIndex,
FieldIndex : Integer;
AFieldName : String;
begin
ColIndex := Column.Index;
FieldIndex := ColIndex;
AFieldName := FDMemTable1.Fields[FieldIndex].FieldName;
Caption := AFieldName;
// Should check here that the the field is a sortable one and not a blob like a graphic field
FDMemTable1.IndexFieldNames := AFieldName;
end;
Note that this answer assumes that there is a one-for-one correspondence between grid columns and fields of the bound dataset, which will usually be the case for bindings created using the default methods in the IDE. However, Live Binding is sophisticated enough to support situations where this correspondence does not exist, and in those circumstances it should not be assumed that the method in this answer will work.

How to force a Client DataSet to recalculate calculated and internal calculated fields?

I have a ClientDatSet with a few fkInternalCalc fields. The CDS is not linked to any provider; instead it's filled on the fly. How can I force CDS to recalculate all the "calculable" fields? I can not call Refresh() because there is no provider to refresh data from. The only way I have come with so far has been to navigate through all records, which is not the best way.
PS: I have read this question and this post, but I'm hoping for a more elegant way.
I achieve that with a helper (stripped here to the necessary), which allows to call the protected methods without any hack. Make sure to check for DataSet.State = dsInternalCalc inside OnCalcFields for fkInternalCalc fields.
type
TClientDataSetHelper = class helper for TClientDataSet
public
function AssureEditing: Boolean;
procedure InternalCalc;
end;
function TClientDataSetHelper.AssureEditing: Boolean;
begin
result := not (State in [dsEdit, dsInsert]);
if result then
Edit;
end;
procedure TClientDataSetHelper.InternalCalc;
var
needsPost: Boolean;
saveState: TDataSetState;
begin
needsPost := AssureEditing;
saveState := setTempState(dsInternalCalc);
try
RefreshInternalCalcFields(ActiveBuffer);
finally
RestoreState(saveState);
end;
if needsPost then
Post;
end;
This can easily be expanded for normal calculated fields using CalculateFields. Although this shouldn't be necessary as calculated fields are recalculated whenever any other data field changes.
This is a bit of a hack, but it works!
DBGrid.Height := 30;
DBGrid.Height := 200; // Refresh all Rows after first
CalculatedProc(DataSet); // Refresh first calculated fields. (Write name of your calculate procedure)

Add a blank row, then populate using a dialog in Delphi/DataSnap/dbExpress

I'm planning to create a dialog that would create a blank record in a Firebird database, then populate the fields with values taken from text edit boxes in that dialog.
I am using the following:
dbExpress
DataSnap
Delphi XE2
Maybe it should go like this:
DataSource.DataSet.Append;
(call the dialog with the db text boxes pointing to DataSource)
if ModalResult = mrOK then
DataSource.DataSet.Post;
Any ideas? I'd like to know which components to use (ClientDataSet, or SQLQuery or SQLDataSet). I've no idea on how to go about this.
EDIT: Code formatting
In other words, you want to create a dialog with non-data aware controls. In order to do this, you need three TSQLQueries: one to retrieve data when entering the dialog (in case of editing the data), one to insert and one to update.
Here is some (edited) code from such a dialog which I wrote the other day. The parameter 'n' is the id of the tuple to be edited; its value will be -1 if I am inserting a new record.
Function TEditCashbox.Execute (n: longint): boolean;
var
q: TSqlQuery;
begin
if n = -1 then
begin
edDate.Text:= datetostr (date);
edAmount.text:= '0';
end
else with qGetCashbox do
begin
params[0].asinteger:= n;
open;
edDate.text:= fieldbyname ('curdate').asstring;
edAmount.text:= fieldbyname ('amount').asstring;
edDetails.text:= fieldbyname ('details').asstring;
close
end;
if showmodal = mrOK then
begin
if n = -1 then
begin
q:= qInsertCashbox;
q.ParamByName ('p0').asinteger:= dm.GenerateID ('cashbox')
end
else
begin
q:= qUpdateCashbox;
q.ParamByName ('p0').asinteger:= n
end;
with q do
begin
parambyname ('p1').asdate:= strtodate (edDate.text);
parambyname ('p2').asinteger:= strtoint (edAmount.Text);
parambyname ('p3').asstring:= edDetails.text;
execsql
end;
end
end.
qGetCashbox is a query defined as
select curdate, amount, details from cashbox where id = :p1
qInsertCashbox is
insert into cashbox (id, curdate, amount, details)
values (:p0, :p1, :p2, :p3)
qUpdateCashbox is
update cashbox set
curdate = :p1,
amount = :p2,
details = :p3
where id = :p0
Of course, you could also use data aware components, which require the 'trinity' - TSQLDataSet, TDataSetProvider and TClientDataSet. Using data aware components is easier, but sometimes there are cases in which this approach is not suitable. If you do use data aware components, then my template code is as follows
sdsEditDeposit.params[0].AsInteger:= n; // this is the TSQLDataSet
with qEditDeposit do // this is the clientdataset
begin
open;
if n = -1 then // new tuple
begin
insert;
fieldbyname ('amount').asinteger:= 0;
fieldbyname ('curdate').asdatetime:= date;
end;
edit;
if showmodal = mrOK then
begin
if n = -1 then
begin
n:= dm.GenerateID;
fieldbyname ('id').asinteger:= n;
end;
result:= n;
post;
applyupdates (0)
end
else
begin
cancel;
result:= 0
end;
end;
You might want to allow for a cancel as well...
DataSource.DataSet.Append;
(call the dialog with the db text boxes pointing to DataSource)
if ModalResult = mrOK then
DataSource.DataSet.Post
else
Datasource.Dataset.cancel;
I use TADOQuery components with MS-SQL and it works reliably.
You need a TSQLConnection configured for your database and (to make it easy) a TSQLTable. Link the TSQLTable to the TSQLConnection and select the desired table in the TableName property. Next drop a TDataSetProvider onto the form and connect it with the TSQLTable. Now take a TClientDataSet, set its Provider to the TDataSetProvider and connect the DataSource to the TClientDataSet.
Remember: to actually write the data into the database you have to call TClientDataSet.ApplyUpdates.
More info here

How do I sort a CSV file in a TTable?

I have a TTable, and I am loading CSV files to this TTable. Three fields are there: Id, Hits & Path.
I made some lookup fields to this TTable with another query.
I want to sort the table. I am getting the message "Capability not supported." when I try to call AddIndex('ndxHits','HITS',[]);
Here is my code:
with DM.TblCVResults do
begin
try
Active := False;
TableName := 'C:\CSV\123.txt';
Active := True;
AddIndex('ndxHits','HITS',[]);
AddIndex('ndxCandidate','LkCandidate',[]);
AddIndex('ndxLastCV','LkLastCV',[]);
AddIndex('ndxPostCode','LkPostCode',[]);
IndexDefs.Update;
Active := True;
DM.TblCVResults.IndexName := 'ndxHits';
except
on E: Exception do
MsgError(E.Message);
end;
end
Your previous question mentioned you were using ttASCII as the TableType. ttASCII tables, AFAIK, don't support indexes.
Your best bet is to load the ttASCII TTable content into a TClientDataset` (CDS), which does support indexes. I haven't tested with a ttASCII table as the source, but it should be as simple as:
Add a TDatasetProvider component to your application. Set it's DataSet property to your TTable.
Add a TClientDataSet component to your application. Set it's ProviderName to the DataSetProvider you added above. (I've named it CDS in the steps below.)
Open both the Table and the ClientDataSet (CDS), in that order.
Table1.Active := True;
CDS.Active := True;
Turn off updating of the TTable if you don't need it. (It's much faster.)
CDS.LogChanges := False;
Run the following code to create the indexes:
// Repeat for each additional index
with CDS.IndexDefs.AddIndexDef do
begin
Name := 'ndxHits';
Fields := 'Hits';
Options := [];
end;
Set the ClientDataSet's IndexName property to the index you want active:
CDS.IndexName := 'ndxHits';
Use the ClientDataSet like you would any other dataset. Search it using Locate or FindKey, add to it using Insert or Append, filter it, and so forth.

Adding a calculated field to a Query at run time

I'm getting data using a query in Delphi, and would like to add a calculated field to the query before it runs. The calculated field is using values in code as well as the query so I can't just calculate it in SQL.
I know I can attach an OnCalcFields Event to actually make the calculation, but the problem is after adding the calculated field there are no other fields in the query...
I did some digging and found that all of the field defs are created but the actual fields are only created
if DefaultFields then
CreateFields
Default Fields is specified
procedure TDataSet.DoInternalOpen;
begin
FDefaultFields := FieldCount = 0;
...
end;
Which would indicate that if you add fields you only get the fields you added.
I would like all the fields in the query AS WELL AS the ones I Add.
Is this possible or do I have to add all the fields I'm using as well?
Nothing prevents you from creating all the fields first in your code,
then add your calculated fields.
You can either use a "hacked type" to use the protected CreateFields:
type
THackQuery = class(TADOQuery)
end;
[...]
MyQuery.FieldDefs.Update;
THackQuery(MyQuery).CreateFields;
or borrowing some code from CreateFields:
MyQuery.FieldDefs.Update;
// create all defaults fields
for I := 0 to MyQuery.FieldDefList.Count - 1 do
with MyQuery.FieldDefList[I] do
if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and
not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then
CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]);
then create your calculated fields:
MyQueryMyField := TStringField.Create(MyQuery);
with MyQueryMyField do
begin
Name := 'MyQueryMyField';
FieldKind := fkCalculated;
FieldName := 'MyField';
Size := 10;
DataSet := MyQuery;
end;
Delphi now has the option to combine automatic generated fields and calculated fields : Data.DB.TFieldOptions.AutoCreateMode an enumeration of type TFieldsAutoCreationMode. This way you can add your calculated fields at runtime. Francois wrote in his answer how to add a field at runtime.
Different modes of TFieldsAutoCreationMode :
acExclusive
When there are no persistent fields at all, then automatic fields are created. This is the default mode.
acCombineComputed
The automatic fields are created when the dataset has no persistent fields or there are only calculated persistent fields. This is a convenient way to create the persistent calculated fields at design time and let the dataset create automatic data fields.
acCombineAlways
Automatic fields for the database fields will be created when there are no persistent fields.
You need to add all fields in addition to your calculated field.
Once you add a field, you have to add all of the fields that you want in the data set.
Delphi calls this persistent fields versus dynamic fields. All fields are either persistent or dynamic. Unfortunately, you can't have a mixture of both.
Another thing to note, from the documentation is
Persistent fields component lists are
stored in your application, and do not
change even if the structure of a
database underlying a dataset is
changed.
So, be careful, if you later add additional fields to a table, you will need to add the new fields to the component. Same thing with deleting fields.
If you really don't want persistent fields, there is another solution. On any grid or control that should show the calculated field, you can custom draw it. For example, many grid controls have a OnCustomDraw event. You can do your calculation there.
If you have know your to be calculated fields names at runtime, you can use something like that.
var
initing:boolean;
procedure TSampleForm.dsSampleAfterOpen(
DataSet: TDataSet);
var
i:integer;
dmp:tfield;
begin
if not initing then
try
initing:=true;
dataset.active:=false;
dataset.FieldDefs.Update;
for i:=0 to dataset.FieldDefs.Count-1 do
begin
dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName;
dmp.DataSet:=dataset;
if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then
begin
dmp.Calculated:=true;
dmp.DisplayWidth:=255;
dmp.size:=255;
end;
end;
dataset.active:=true;
finally
initing:=false;
end;
end;
procedure TSampleForm.dsSampleAfterClose(
DataSet: TDataSet);
var
i:integer;
dmp:TField;
begin
if not initing then
begin
for i:=DataSet.FieldCount-1 downto 0 do
begin
dmp:=pointer(DataSet.Fields.Fields[i]);
DataSet.Fields.Fields[i].DataSet:=nil;
freeandnil(dmp);
end;
DataSet.FieldDefs.Clear;
end;
end;
procedure TSampleForm.dsSampleCalcFields(
DataSet: TDataSet);
var
tmpdurum,tmpOldDurum:integer;
begin
if not initing then
begin
tmpDurum := dataset.FieldByName( 'state' ).AsInteger;
tmpOldDurum:= dataset.FieldByName( 'oldstate' ).AsInteger;
dataset.FieldByName( 'txtState' ).AsString := State2Text(tmpDurum);
dataset.FieldByName( 'txtOldState' ).AsString := State2Text(tmpOldDurum);
end;
end;
procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
if dsSample.Active then
dsSample.Close;
dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
dsSample.Open;
end;

Resources