Jump to content
ChrisChuah

DBGrid selected row does not highlight the selected row when ClientDataSet is Closed and Open again

Recommended Posts

Posted (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

1389271130_Record100selected.thumb.png.408121fb4857ce6087f7d9f241aa7059.png386449203_RefreshRecord100notselected.thumb.png.78cf37b4a3cf962806c55618cb2ef4ee.png

Edited by ChrisChuah
added pic

Share this post


Link to post

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
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
Posted (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 by Attila Kovacs

Share this post


Link to post
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
Posted (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 by Attila Kovacs

Share this post


Link to post

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:

image.png.397aa1521d3db52735082b1f1a266ee0.png 

 

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

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

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

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
Posted (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:

 

CaptureDelphi1GetBookmarkHelp.PNG

Edited by Brian Evans

Share this post


Link to post

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

I would proceed differently. I would save the PK and then use it to set up the record.

Share this post


Link to post
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
Posted (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;

 

Edited by ChrisChuah

Share this post


Link to post
Posted (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 by ChrisChuah
added the drawing code

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×