I have a TDbGrid, and I can easily tell how many columns are in it at runtime with the FieldCount property, but there doesn't seem to be a corresponding RowCount property to display how many records are being displayed. How can I find this out?
Both RowCount and VisibleRowCount are protected properties in TCustomGrid that are not exposed in TDBGrid. But you can get round that doing the following:
type
TDummyGrid = class(TDBGrid);
RowCount := TDummyGrid(MyDBGrid).RowCount;
VisibleRowCount := TDummyGrid(MyDBGrid).VisibleRowCount;
Be warned that this includes the header.
You could try:
DBGrid1.DataSource.DataSet.RecordCount
Maybe there are better solutions. But this worked for me.
I would use
TDbGrid.ApproxCount
Related
I have found many solutions online but they aren't working becaue the StringGrid1.ColumnCount peoperty is read only. I am using Delphi 10 Seattle.
I have a StringGrid1 and I need to add columns at runtime. To be specific I have to add columns according to the size of a TList. In particular:
var a: TList<double>;
begin
//fill the TList...
for i := 0 to a.Count - 1 do
begin
StringGrid1.AddColumn(); //how can I do this?
end;
end;
I find this very easy on Lazarus (but it has FPC of course) but on Delphi I really don't know what to do. I am working on Firemonkey.
Use the grid's AddObject() or InsertObject() method to add an object instance of the desired TColumn-derived class, like TStringColumn. The column object will get added to the grid's Columns array. The ColumnCount property simply returns the number of columns in the array, that is why it is read-only.
I'm using a TcxGrid with Grouping. I want to find out how many grouped rows there are but I can't seem to find the right property. There is a <mytableview>.GroupedItemCount but that just refers to how many columns the grid is getting grouped by.
Basically I just want to know if all the groups are collapsed. I could keep a count of expanded groups by watching the GroupRowExpanded and GroupRowCollapsed events but it feels like there should be a better way.
My current plan is to compare the group count with <mytableview>.ViewData.RowCount. If they are different then I must have an expanded group.
I'm guessing the answer is simple.. but the TcxGrid has so many options that I'm not having much luck finding the right one.
I think you are looking for:
level0GroupCount := gridview.DataController.Groups.ChildCount[-1];
This is the number of data groups at level 0.
To check if every groups are full collapsed:
function AreGridGroupsCollapsed(_gridView : TcxGridDBTableView): Boolean;
var
level0GroupCount : Integer;
begin
level0GroupCount := _gridView .DataController.Groups.ChildCount[-1];
Result := groupCount = _gridView.ViewData.RowCount;
end;
I am dynamically adding fields to a TDataSet using the following code:
while not ibSQL.Eof do
fieldname := Trim(ibSql.FieldByName('columnnameofchange').AsString);
TDataSet.FieldDefs.Add(fieldname , ftString, 255);
end
Problem is that I might get duplicate names so what is the easiest way to screen for duplicates and not add the duplicates that are already added.
I hope not to traverse through the TDataSet.FieldDefList for each column added as this would be tedious for every single column addition. And there can be many additions.
Please supply another solution if possible. If not then I am stuck using the FieldDefList iteration.
I will also add that screening out duplicates on the SQL query is an option but not a desired option.
Thanks
TFieldDefs has a method IndexOf that returns -1 when a field with the given name does not exist.
If I understand you correctly, the easiest way would probably be to put all of the existing field names in a TStringList. You could then check for the existence before adding a new field, and if you add it you simply add the name to the list:
var
FldList: TStringList;
i: Integer;
begin
FldList := TStringList.Create;
try
for i := 0 to DataSet.FieldCount - 1 do
FldList.Add(DataSet.Fields[i].FieldName);
while not ibSQL.Eof do
begin
fieldname := Trim(ibSql.FieldByName('columnnameofchange').AsString);
if FldList.IndexOf(fieldName) = -1 then
begin
FldList.Add(fieldName);
DataSet.FieldDefs.Add(fieldname , ftString, 255);
end;
ibSQL.Next;
end;
finally
FldList.Free;
end;
end;
I'm posting this anyway as I finished writing it, but clearly screening on the query was my preference for this problem.
I'm having a bit of trouble understanding what you're aiming for so forgive me if I'm not answering your question. Also, it has been years since I used Delphi regularly so this is definitely not a specific answer.
If you're using the TADOQuery (or whatever TDataSet you're using) in the way I expect my workaround was to do something like:
//SQL
SELECT
a.field1,
a.... ,
a.fieldN,
b.field1 as "AlternateName"
FROM
Table a INNER JOIN Table b
WHERE ...
As which point it automatically used AlternateName instead of field1 (thus the collision where you're forced to work by index or rename the columns.
Obviously if you're opening a table for writing this isn't a great solution. In my experience with Delphi most of the hardship could be stripped out with simple SQL tricks so that you did not need to waste time playing with the fields.
Essentially this is just doing what you're doing at the source instead of the destination and it is a heck of a lot easier to update.
What I'd do is keep a TStringList with Sorted := true and Duplicates := dupError set. For each field, do myStringList.Add(UpperCase(FieldName)); inside a try block, and if it throws an exception, you know it's a duplicate.
TStringList is really an incredibly versatile class. It's always a bit surprising all the uses you can find for it...
i have tried looking online but had no luck,
How i could delete all records in an adotable in button click, which match a varying criteria. For example i want to be able to delete all records in an adotable where Labour_ID (this is a field name within the adotable) is equal to DBedit.Text.
sorry this is a bit vague, but suggestions would be appreciated. thanks
You can delete the rows with a simple loop:
while ADOTable1.Locate('Labour_ID', Edit1.Text, []) do
ADOTable1.Delete;
Better yet is to use a TADOQuery instead, and do it with SQL:
ADOQuery1.SQL.Text := 'DELETE FROM YourTable WHERE Labour_ID = :Labour_ID';
ADOQuery1.Params.ParamByName('Labour_ID').AsString := Edit1.Text;
ADOQuery1.ExecSQL;
See the Delphi documentation on TDataSet.Locate for info on the last LocateOptions parameter. (The link is to XE2's docs, but it hasn't changed much (if at all) for ADO since D7).
I am using JvMemoryData to populate a JvDBUltimGrid. I'm primarily using this JvMemoryData as a data structure, because I am not aware of anything else that meets my needs.
I'm not working with a lot of data, but I do need a way to enumerate the records I am adding to JvMemoryData. Has anyone done this before? Would it be possible to somehow "query" this data using TSQLQuery?
Or, is there a better way to do this? I'm a bit naive when it comes to data structures, so maybe someone can point me in the right direction. What I really need is like a Dictionary/Hash, that allows for 1 key, and many values. Like so:
KEY1: val1;val2;val3;val4;val5;etc...
KEY2: val1;val2;val3;val4;val5;etc...
I considered using THashedStringList in the IniFiles unit, but it still suffers from the same problem in that it allows only 1 key to be associated with a value.
One way would be to create a TStringList, and have each item's object point to another TList (or TStringList) which would contain all of your values. If the topmost string list is sorted, then retrieval is just a binary search away.
To add items to your topmost list, use something like the following (SList = TStringList):
Id := SList.AddObject( Key1, tStringList.Create );
InnerList := tStringList(SList.Objects[id]);
// for each child in list
InnerList.add( value );
When its time to dispose the list, make sure you free each of the inner lists also.
for i := 0 to SList.count-1 do
begin
if Assigned(SList.Objects[i]) then
SList.Objects[i].free;
SList.Objects[i] := nil;
end;
FreeAndNil(SList);
I'm not a Delphi programmer but couldn't you just use a list or array as the value for each hash entry? In Java terminology:
Map<String,List>
You already seem to be using Jedi. Jedi contains classes that allow you to map anything with anything.
Take a look at this related question.
I have been using an array of any arbitrarily complex user defined record types as a cache in conjunction with a TStringList or THashedStringList. I access each record using a key. First I check the string list for a match. If no match, then I get the record from the database and put it in the array. I put its array index into the string list. Using the records I am working with, this is what my code looks like:
function TEmployeeCache.Read(sCode: String): TEmployeeData;
var iRecNo: Integer;
oEmployee: TEmployee;
begin
iRecNo := CInt(CodeList.Values[sCode]);
if iRecNo = 0 then begin
iRecNo := FNextRec;
inc(FNextRec);
if FNextRec > High(Cache) then SetLength(Cache, FNextRec * 2);
oEmployee := TEmployee.Create;
oEmployee.Read(sCode);
Cache[iRecNo] := oEmployee.Data;
oEmployee.Free;
KeyList.Add(Format('%s=%s', [CStr(Cache[iRecNo].hKey), IntToStr(iRecNo)]));
CodeList.Add(Format('%s=%s', [sCode, IntToStr(iRecNo)]));
end;
Result := Cache[iRecNo];
end;
I have been getting seemingly instant access this way.
Jack