ChrisChuah 0 Posted June 25, 2022 (edited) Hi I am using TSQLQuery, TClientDataSet and TDataSetProvider with DBGrid I will click on the record row in the middle of the grid and the grid shows that it is highlighted HOwever, when i close and open the ClientDataSet to refresh the data, i would use the bookmark to go back to the last bookmark before the Client dataset is Close l_bookmark := TntDBGrid1.Datasource.DataSet.GetBookmark; TntDBGrid1.DataSource.DataSet.Close; TntDBGrid1.DataSource.Dataset.Open; TntDBGrid1.DataSource.DataSet.GotoBookmark(l_bookmark); TntDBGrid1.DataSource.Dataset.FreeBookmark(l_bookmark); After open, the indicator is shown correctly on the dbgrid but the highlighted row is on the first row. see pic please advise regards chris Edited June 25, 2022 by ChrisChuah added pic Share this post Link to post
Lajos Juhász 293 Posted June 25, 2022 When you refresh the data the bookmark will no longer be valid. It's a pure luck if you don't see an error message. You shold remember the key of the active record before the refresh and call locate for it. Share this post Link to post
Attila Kovacs 629 Posted June 25, 2022 12 minutes ago, Lajos Juhász said: When you refresh the data the bookmark will no longer be valid. It's a pure luck if you don't see an error message. You shold remember the key of the active record before the refresh and call locate for it. Huh? Since when is that? Share this post Link to post
Attila Kovacs 629 Posted June 25, 2022 (edited) @ChrisChuah Interesting, I suspect some extra unusual code what we can't see. Furthermore always use BookmarkValid() and do not use FreeBookmark. By the way, you have 2 indicators on the first pic, I never used Indicators but I don't think it's normal, do you have multiselect or similar turned on? Edited June 25, 2022 by Attila Kovacs Share this post Link to post
Lajos Juhász 293 Posted June 25, 2022 1 hour ago, Attila Kovacs said: Huh? Since when is that? Since Delphi 1. If you close and reopen the query the bookmark is invalid. Share this post Link to post
Attila Kovacs 629 Posted June 25, 2022 (edited) 22 minutes ago, Lajos Juhász said: Since Delphi 1. If you close and reopen the query the bookmark is invalid. I don't believe you. There was no bookmark in Delphi 1. Just tested with Devart's UniQuery, I can close the dataset and open it again, the bookmark is still valid. Maybe you are referring to some old BDE component or some other 3rd party which is for some reason saves an instance pointer/memory address too, instead of just key-field values. But I have never seen that before. Edited June 25, 2022 by Attila Kovacs Share this post Link to post
Lajos Juhász 293 Posted June 25, 2022 I've tested with fireDAC, the bookmark is never valid after reopen (however will find the record if you don't scroll the scrollbar). After every scroll I get: with a call stack: :763ec3a2 KERNELBASE.RaiseException + 0x62 FireDAC.Stan.Error.FDException(nil,???,26209084,???) FireDAC.Stan.Error.FDException($37E6CA0,???,200,???) FireDAC.Comp.DataSet.ErrorNoBmk FireDAC.Comp.DataSet.TFDDataSet.InternalGotoBookmark(???) FireDAC.Comp.DataSet.TFDDataSet.InternalGotoBookmark((144, 138, 137, 3, 81, 0, 0, 0, 5, 0, 0, 0)) Data.DB.TDataSet.GotoBookmark((144, 138, 137, 3, 81, 0, 0, 0, 5, 0, 0, 0)) Unit1.TForm1.Button1Click($37E6930) I know that this was the case with BDE, DBX and now FireDAC. For the reference my code is: procedure TForm1.Button1Click(Sender: TObject); var lbm: TBookmark; begin lbm:=fdquery1.Bookmark; fdquery1.Close; fdquery1.Open; if not FDQuery1.BookmarkValid(lbm) then ShowMessage('Not Valid!'); fdquery1.Bookmark:=lbm end; Share this post Link to post
Attila Kovacs 629 Posted June 25, 2022 This is ridiculous. I can even free the dataset (TUniQuery), recreate it and use the bookmark again on it. Are you sure you have key-fields in your table? Maybe keyfields property should be set for firedac? procedure Run; var a: TQAdresse; bm: TBookmark; begin a := TQAdresse.Create; try a.Open; a.ID.Locate(105); WriteLn(a.ID.AsString); bm := a.Bookmark; a.Free; a := TQAdresse.Create; a.Open; if a.BookmarkValid(bm) then a.Bookmark := bm; WriteLn(a.ID.AsString); a.Next; WriteLn(a.ID.AsString); finally a.Free; end; end; Prints: 105 105 106 Done. Share this post Link to post
Lajos Juhász 293 Posted June 25, 2022 Tested with a persistent field and set the PK. It doesn't make a difference I still get: Project Project1.exe raised exception class EFDException with message '[FireDAC][Comp][DS]-200. Bookmark is not found for dataset [FDQuery1]'. Maybe UniDAC has a different implementation for bookmarks. Share this post Link to post
Hans J. Ellingsgaard 21 Posted June 25, 2022 I think that Lajos is correct about that bookmarks are lost when closing the query in FireDAC, but if I remember correctly, it did work in Client Dataset. Share this post Link to post
Brian Evans 105 Posted June 26, 2022 (edited) 22 hours ago, Attila Kovacs said: I don't believe you. There was no bookmark in Delphi 1. Just tested with Devart's UniQuery, I can close the dataset and open it again, the bookmark is still valid. Maybe you are referring to some old BDE component or some other 3rd party which is for some reason saves an instance pointer/memory address too, instead of just key-field values. But I have never seen that before. There certainly was GetBookmark/GotoBookMark()/FreeBookMark() in Delphi 1. Attached is an image of the Delphi 1 help topic for GetBookmark, notice what the "Note:" at the bottom says: Edited June 26, 2022 by Brian Evans Share this post Link to post
Attila Kovacs 629 Posted June 26, 2022 @Brian Evans cool, thx. It's time to forget this 30yo limitation and fixing FD. Share this post Link to post
ChrisChuah 0 Posted June 28, 2022 Thank you. Seems like i did not see that bookmarks are invalidated when dataset is closed and opened again. Then how can i refresh the dataset if i do not close and open it? Share this post Link to post
Stano 143 Posted June 28, 2022 I would proceed differently. I would save the PK and then use it to set up the record. Share this post Link to post
Lajos Juhász 293 Posted June 28, 2022 2 hours ago, Stano said: I would proceed differently. I would save the PK and then use it to set up the record. This was my first suggestion as that is the way that always works. Share this post Link to post
ChrisChuah 0 Posted June 30, 2022 (edited) I just found out that i can use the dataset to refresh rather than Open and close. The dbgrid will refresh with updated content. Now i have another problem is when i open the dataset, the dbgrid is filled to the bottom and it will somehow stop there.. i need to scroll down to request it to draw again. anyone have this problem? is there a way to scroll all the way down the grid automatically once the drawing is completed? please see this video on the problem.. anyone can advise? source procedure TfrmMain.dbgABNDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var l_bitmap : TBitmap; l_rect : TRect; l_bmpWidth, l_width : integer; l_str : string; begin if ( (dbgABN.DataSource.Dataset as TClientDataset).RecNo mod 2 = 0) then begin if dbgABN.Canvas.Brush.Color = dbgABN.Color then begin dbgABN.Canvas.Brush.Color := clSkyBlue; end; end; l_rect := Rect; l_str := Column.Field.DisplayText; if Column.FieldName = 'ROW_ID' then begin l_bitmap := TBitmap.create; try ImageList2.GetBitmap(0, l_bitmap); l_bmpWidth := (Rect.bottom - Rect.Top); l_rect.Right := Rect.Left + l_bmpWidth; dbgABN.Canvas.StretchDraw(l_rect, l_bitmap); finally l_bitmap.Free; end; // cc : reset the output rectangle l_rect := Rect; l_rect.Left := l_rect.Left + l_bmpWidth; end; l_width := 5 + dbgABN.Canvas.TextExtent(l_str).cx; if (l_width > Column.Width) then Column.Width := l_width; dbgABN.DefaultDrawColumnCell(l_rect, DataCol, (Column as TTntColumn), State); end; Screen_Recording_2022-06-30_at_9_50.33_AM.mp4 Edited June 30, 2022 by ChrisChuah Share this post Link to post
Attila Kovacs 629 Posted June 30, 2022 The thumbnail suggests that you have owner-drawing on, the bug must be in your code. Share this post Link to post
ChrisChuah 0 Posted July 1, 2022 (edited) Hi I am not sure if this function affected the drawing procedure TForm1.ClientDataSet1AfterOpen(DataSet: TDataSet); var l_index : integer; l_fieldName : string; begin for l_index := 0 to ClientDataSet1.FieldCount - 1 do begin l_fieldName := ClientDataSet1.Fields[l_index].FieldName; ClientDataSet1.Fields[l_index].DisplayWidth :=10; if (l_fieldName = 'SDT') or (l_fieldName = 'EST_D') or (l_fieldName = 'LAST_D') or (l_fieldName = 'ACT_D') or (l_fieldName = 'CREATED_AT') or (l_fieldName = 'UPDATED_AT') then ClientDataSet1.Fields[l_index].OnGetText := getDateTimeStr else if (l_fieldName = 'STIME') then ClientDataSet1.Fields[l_index].OnGetText := getTimeStr else if (l_fieldName = 'SDATE') then ClientDataSet1.Fields[l_index].OnGetText := getDateStr else if (l_fieldName = 'ORDEST_N_C') or (l_fieldName = 'FREE_REM') then ClientDataSet1.Fields[l_index].OnGetText := getForeignStr; end; Dataset.Last; end; procedure TForm1.TntDBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var l_bitmap : TBitmap; l_rect : TRect; l_bmpWidth, l_width : integer; l_str : string; begin if (ClientDataSet1.RecNo mod 2 = 0) then begin if TntDBGrid1.Canvas.Brush.Color = TntDBGrid1.Color then begin TntDBGrid1.Canvas.Brush.Color := clSkyBlue; end; end; l_rect := Rect; l_str := Column.Field.DisplayText; if Column.FieldName = 'ROW_ID' then begin l_bitmap := TBitmap.create; try ImageList3.GetBitmap(1, l_bitmap); l_bmpWidth := (Rect.bottom - Rect.Top); l_rect.Right := Rect.Left + l_bmpWidth; TntDBGrid1.Canvas.StretchDraw(l_rect, l_bitmap); finally l_bitmap.Free; end; // cc : reset the output rectangle l_rect := Rect; l_rect.Left := l_rect.Left + l_bmpWidth; l_width := 5 + TntDBGrid1.Canvas.TextExtent(l_str).cx; <== if (l_width > Column.Width) then <== Column.Width := l_width; <== end; TntDBGrid1.DefaultDrawColumnCell(l_rect, DataCol, (Column as TTntColumn), State); end; As i store all the dates as Unix Epoch format. hence i need to translate all the values to date time format when drawn on the grid. However, is there a way to know when the drawing on the grid is completed? Or is there a better way to make the grid draw out faster? Seems like if i dont make it auto size for every column, the drawing is faster. The longest column is the Foreign Characters and the resizing took quite long there Is there a way to auto size and auto fit the column which is not done during the drawing part? please advise regards chris Edited July 1, 2022 by ChrisChuah added the drawing code Share this post Link to post