calculated field-running total - delphi

I'm used to calculate the balance of the calculated field., But when I connect the dbgrid. Calculated by moving the scroll is wrong.
Please get help
var
Form1: TForm1;
i : Integer;
procedure TForm1.FormShow(Sender: TObject);
begin
i := 0;
DataSource1.DataSet := ADOTable1;
DBGrid1.DataSource := DataSource1;
end;
procedure TForm1.ADOTable1CalcFields(DataSet: TDataSet);
begin
i := (ADOTable1Debtor.AsInteger - ADOTable1creditor.AsInteger) + i;
ADOTable1Total.AsInteger := i;
end;
Now run the application and move the scroll in dbgrid column numbers (total) will change.
I'd like to know how to stop the change.

The calculated fields is designed to show value calculations at the row level and is not designed to make aggregations (calculations based on a set of rows).
For example, the database layer will fire the OnCalc event in no particular order and every time is needed to obtain the value of the field (for display pruposes, for example), since that value is not stored and may (and usually do) depend on the values of other fields.
In a set of 10 rows, you can get it called for rows 1, 2, 3, 4, 5 and then in 1 again...
You can use it, for example, in a line_total column which is the product of quantity and unit_price, but you can't use it to show e.g. the sum(line_total) of all the lines, as I infer you're trying to do in the shown code.
How to perform aggregations then?
You may want to link your DataSet to a ClientDataSet, which have AggregateFields, in which you can perform calculations like SUM(Quantity * Price) on the entire row-set or sub-sets based on a Index.
To learn more about AggregateFields read ClientDataSet Aggregates and GroupState by Cary Jensen in EDN.

What you are trying can't work, since any scroll will change the result.
Take a AdoDataset instead with following Commandtext
select ID, creditor, Debtor,(Select sum (Debtor-Creditor) from Table1 t where t.ID<=Table1.ID) as Total
from Table1 order by ID

Related

How to compare 4 column values of TAdvStringGrid with one another and label them?

If i have colA, ColB, Colc, ColD and there are 1000 rows in each column in TAdvStringGrid . I would like check the number of double measurements of values in the 1000 rows in colA, ColB, Colc, ColD of TAdvStringGrid.
I am doing some thing like At first reading ColA, ColB, ColC, ColD values into multidimensional array and looping each element in multi dimensional array and comparing with each row element TAdvStringGrid and when found same using OnDrawcell function, I am labelling and displaying the row with a colour.
However it takes a lot of time. Is there a shorter way to do it. As the rows keep on increasing. Thanks for the answer in advance.
Every row is one measurement and one measurement consist of 4 values in ColA,B, C,D.
List : array of array of double;
SetLength (List,AdvStringGrid.RowCount,4);
for i := 0 to AdvStringGrid.RowCount -1 do begin
j:=0;
List[i,j] := strtofloat(AdvStringGrid.Cells[4,i+1]);
List[i,j+1] := strtofloat(AdvStringGrid.Cells[5,i+1]);
List[i,j+2] := strtofloat(AdvStringGrid.Cells[8,i+1]);
List[i,j+3] := strtofloat(AdvStringGrid.Cells[9,i+1]);
end;{for i}
How do i compare each element with neighbour and mark the duplicate??
Am I correct that every row is one meassurement? So one meassurement consist out of 4 values?
First thing is you shouldn't modify the visual StringGrid in in a loop. In the worst case the StringGrid invalidates and draws again after each action.
So it's good to read all data in an multidimention array.
To eliminate doubles i would sort everything and then compare neigbors. This is a pretty common pattern.
Define any order like ColA accending, then ColB accending, ColC accending and ColD accending and implenent a sort algorithm (like quicksort or mergesort).
After everything is sortet you can traverse the array from highes element to 0 and check if two neighbours are the same.
If you want to mark the double values instead of deleting them consider adding a 5th colum for a value when it is a duplicate.
After all the calcilation i would search for any Function like BeginUpdate() and Endupdate() to make sure that the StringGrid will only draw once.
Do all changes to StringGrid between the call of BeginUpdate() and Endupdate()
Update: your code could look something like this:
var
i:integer;
sortedList: array of array of double;
begin
setlength(List, 1000, 5); // use a fifth row for marking doublicates, set this value to 0
// Fill List like you mentioned here
sortedList = YourSortAlgorithm(List); // sort your List here
for i := high(sortedList) downto 0 do
begin
// compare if entries are duplicates
if sortedList[i,1] = sortedList[i-1,1] and sortedList[i,2] = sortedList[i-1,2] and sortedList[i,3] = sortedList[i-1,3] and sortedList[i,4] = sortedList[i-1,4] then
begin
sortedList[i-1,5] = 1; // 1 means duplicate, 0 means not duplicate
end;
end;
AdvStringGrid1.BeginUpdate;
// update your Stringgrid here
AdvStringGrid1.EndUpdate();
end;
Bye the way, instead of using a two dimentionaly array i would recoment to use a array of record.
Saying for example, that your ColA is a height, ColB is a length, ColC is a Temperature and ColD is a age you could define a record like this
type
TMeasurement = record
height: double;
length: double;
temperature: double;
age: double;
isBoolean: boolean;
end;
var
list: array of TMeasurement;
begin
//...
end;

Change integer field in db table to display as string on cxgrid

I have a delphi form with a cxgrid on it, the grid is connected to a query/datasource.
If a field in the database table is an Integer, is there anyway of me displaying the integer as a string on the cxgrid column?
e.g. 1 = January
2 = February
3 = March
and so on.
Thanks,
Yes, there is such possibility.
Put TcxEditRepository component next go your grid. Double click on it, you should see the empty window with "Add..." button. Click it and from the list of available components select ImageComboBox.
Now, you need to edit Items property of this combo.
After filling up all rows go to your view (TcxGridDBTableView or TcxGridTableView) and pick the column which contains integer values. This column has a property called RepositoryItem. If you did everything correctly you should be able to select the repository item which you've created earlier (the ImageComboBox). After selecting it, your column should immediately display month names instead of numbers.
Other approach is to override cxGrid column OnGetDisplayText event. You could do something like that:
procedure TSomeForm.GetDisplayText(Sender: TcxCustomGridTableItem;
ARecord: TcxCustomGridRecord; var AText: string);
var
nVal : Integer;
begin
nVal := ARecord.Values[Sender.Index];
case nVal of
1: AText := "January";
//and so on
end;
end;

Expand only selected groups i DevExpress grid

I have e DevExpress grid where I would like to expand some groups that contain a certain value.
I have a grouping on year and week and I would like to have only the current year and the current week expanded as default.
I have been searching for some hints, but havn't found any yet.
The question is a "bit" old but since you are still active I'll answer it.
For the simplicity I've used only two columns: "Year" and "Population", so the grouping is only by "Year".
Here is the screenshot of an example application:
You see an initial data.
I am grouping rows using a "Year" column.
I am expanding group with "Year" = 1, I've assumed that current year = 1.
procedure TfrmMain.btnExpandClick(Sender: TObject);
const
CurrentYear = 1;
var
i: Integer;
begin
for i := 0 to tbvMain.ViewData.RowCount - 1 do
begin
// Check if a row is a grouping row.
if not tbvMain.ViewData.Rows[i].IsData then
begin
// Check if a grouping value is the one that you want expanded.
if tbvMain.ViewData.Rows[i].Values[clmYear.Index] = CurrentYear then
tbvMain.ViewData.Rows[i].Expand(False);
end;
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
var
i: Integer;
begin
// Prepare some random data.
Randomize;
tbvMain.DataController.RecordCount := 10;
for i := 0 to tbvMain.DataController.RecordCount - 1 do
begin
tbvMain.DataController.Values[i, clmYear.Index] := Random(3) + 1;
tbvMain.DataController.Values[i, clmPopulation.Index] := Random(100);
end;
end;
The key here is to check if the given row is data or not. If it is not data then it is a grouping row, for this you need to use tbvMain.ViewData.Rows[i].IsData
You can find full source code here. Please note that I've used Delphi 2009 and Dev Express build 56.
Have you try with something like this?
TableView1.ViewData.Records[0].Expand(true)
If the current year is the first, otherwise replace 0 with the correct record number

Getting coordinates of an ExcelRange object

In Excel's COM API:
Given an ExcelRange object, how would I determine which rows and columns are contained within it?
I do not want the contents of the range, just the "coordinates" of the range, preferably as integers.
I did notice that ExcelRange has both a Row and a Column property, however these only indicate the row and column of the upper left corner of the range.
Note: I am using Delphi, however this question could be relevant to any language using Excel though COM, so answering using Delphi is not necessary.
Assuming a simple rectangular range then you use the Rows and Columns properties of the ExcelRange object. The top-left of the selection is determined by Range.Row and Range.Column. The number of selected rows and columns is given by Range.Rows.Count and Range.Columns.Count.
In complete generality an Excel range can be made up of multiple non-contiguous areas. In this case you use the Areas property of ExcelRange to iterate through the simple rectangular ranges that make up the complex range.
To illustrate consider the following code:
procedure DescribeExcelRange(const Range: ExcelRange);
var
AreaIndex: Integer;
Area: ExcelRange;
begin
for AreaIndex := 1 to Range.Areas.Count do
begin
Area := Range.Areas[i];
Writeln(Format(
'Area %d: R%dC%d:R%dC%d',
[AreaIndex, Area.Row, Area.Column,
Area.Row+Area.Rows.Count-1, Area.Column+Area.Columns.Count-1]
));
end;
end;
I have not actually tested this code so I hope I have remembered correctly that indexing is 1-based.

Zeoslib - loop over calculated fields

I have a Delphi 7 project using Zeoslib 6.6.6 and Sqlite3.
On the form I have a Zquery selecting everything out of a sample database table along with a bunch of calcuated fields (TFloatField; TCurrencyField). The OnCalcFields event of the query runs fine and all field values are set.
However, when i try to loop over the dataset, I consistently get a 'List index out of bounds (62893)' exception, though i am well within the field count limit (the first calculated field of about 14).
Snippet:
gd is a TStringGrid, ZQuery4 is a TZQuery
while not ZQuery4.Eof do
begin
row := row + 1;
gd.Cells[0, row] := IntToStr(gd.Row);
gd.Cells[1, row] := ZQuery4pid.Value; //Known column
gd.Cells[2, row] := FormatFloat('0.00', ZQuery4area.Value); //known column
for i := 3 to ZQuery4.FieldCount - 1 do
begin
field := Zquery4.Fields[i]; //crashes here when accessing the first calculated field.
if field.IsNull
then gd.Cells[i, row] := ''
else gd.Cells[i, row] := field.AsString;
end;
end;
The bizarre thing is that if i connect a DBGrid to the query it works fine. Any ideas?
What happens when gd.cells[x,row] exceeds the number of rows you have set in the string grid? Probably that's your error. If you set your stringgrid to have 62000 rows, great, that's your error. Otherwise, I suspect that you're hitting a limit.
I always used to have my string grid row count grow like this, with logic after row=row+1:
if gd.RowCount<=row then gd.RowCount := row+1;
If however you really are getting this error at 60k+ stringgrid rows, it is possible that instead what is happening is you are hitting a string grid row length limit.
Since you haven't obviously posted ALL your code, it's hard to know what sets the row count in the string grid where, and how it grows.
In the case that you've hit a limit beyond which a StringGrid no longer operates, I suggest you drop StringGrid and use ExgridView or some other virtual gridview that can handle a very very large amount of data.
I seem to have stumbled across at least a workaround:
changing
gd.Cells[i, row] := field.AsString;
to
gd.Cells[i, row] := field.DisplayText;
seems to have solved the problem.

Resources