Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2015-11

Increasingly frustrated with DbGrids

10 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Luca Olivetti

Hello,

I constantly stumble with the behaviour of the TDBGrid. I understand that it's a complex component, but every time I find an issue an Luiz fixes it, two more pop up (or an older one resurfaces). The latest episode of the saga is the futility of using a custom editor (with OnSelectEditor), since the grids then sets the value of the field using this snippet of code

   if (FEditornil) and FEditor.Visible then begin
     Msg.LclMsg.msg:=GM_GETVALUE;
     Msg.grid:=Self;
     Msg.Col:=FCol;
     Msg.Row:=FRow;
     Msg.Value:=GetCells(FCol, FRow);
     FEditor.Dispatch(Msg);
     SetEditText(Msg.Col, Msg.Row, Msg.Value);
   end;

and this only works with the editors defined in Grids.pas, the only ones managing the GM_GETVALUE message.
For other kinds of editors the result depends on how you move in the grid: it could either maintain the value the editor set or reset it to an empty string.

I think I will file a bug once I can create a simple project, but in the meantime if somebody has an idea on how to solve it, it would be appreciated.

Oh, and a custom editor has no way of adapting if the dbgrid is scrolled, a column is resized, etc. Again, this only work for the editors defined in Grids.pas.

Bye

Luca Olivetti Wetron Automation Technology http://www.wetron.es/ Tel. +34 93 5883004 (Ext.3010) Fax +34 93 5883007

Reply by email

Tony Whyman
to Luca Olivetti

Luca,

Intercept classes are a very useful technique for overcoming this problem. I recently wanted to have a specialised TComboBox as a TStringGrid Editor and defined an intercept class for this as:

TComboBox = class(stdctrls.TComboBox)
   private
     FGrid: TCustomGrid;
     FCol,FRow: Integer;
   protected
     procedure WndProc(var TheMessage : TLMessage); override;
     procedure CloseUp; override;
     procedure KeyDown(var Key : Word; Shift : TShiftState); override;
     procedure Loaded; override;
     procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
     procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
     procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
     procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
     procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
   public
     procedure EditingDone; override;
     property BorderStyle;
     property OnEditingDone;
   end;

The result was a specialist editor for the stringgrid (and which handles TGridMessage) but which could be placed on the form and edited as a TComboBox. The TStringFrid "OnSelectEditor " event is then used to return the combobox as an editor.

Intercept classes can be used to customise just about any component. Just ensure that your intercept class is defined after the parent class.

Tony Whyman
MWA

Luca Olivetti wrote 34 lines
Hello, I constantly stumble with the behaviour of the TDBGrid. I understand that it's a complex component, but every time I find an issue an Luiz fixes it, two more pop up (or an older one resurfaces). The latest episode of the saga is the futility of using a custom editor (with OnSelectEditor), since the grids then sets the value of the field using this snippet of code if (FEditornil) and FEditor.Visible then begin Msg.LclMsg.msg:=GM_GETVALUE; Msg.grid:=Self; Msg.Col:=FCol; Msg.Row:=FRow; Msg.Value:=GetCells(FCol, FRow); FEditor.Dispatch(Msg); SetEditText(Msg.Col, Msg.Row, Msg.Value); end; and this only works with the editors defined in Grids.pas, the only ones managing the GM_GETVALUE message. For other kinds of editors the result depends on how you move in the grid: it could either maintain the value the editor set or reset it to an empty string. I think I will file a bug once I can create a simple project, but in the meantime if somebody has an idea on how to solve it, it would be appreciated. Oh, and a custom editor has no way of adapting if the dbgrid is scrolled, a column is resized, etc. Again, this only work for the editors defined in Grids.pas. Bye

Reply by email

Mattias Gaertner
to Tony Whyman
Show quoted text 6 lines
[...] Intercept classes are a very useful technique for overcoming this problem. I recently wanted to have a specialised TComboBox as a TStringGrid Editor and defined an intercept class for this as: TComboBox = class(stdctrls.TComboBox)

Reintroducing 'TComboBox' can confuse users of your code and has no benefit for grids. This should work as well:

TMyComboBox = class(TComboBox)

Mattias

Reply by email

Tony Whyman
to Mattias Gaertner

Ah, but then you can't edit it in the IDE.

OK, intercept classes are a quick and dirty way of avoiding defining a new component - but as long as you are willing to "hold your nose", they do a useful job.

Mattias Gaertner wrote
Reintroducing 'TComboBox' can confuse users of your code and has no benefit for grids. This should work as well: TMyComboBox = class(TComboBox)

Reply by email

Luca Olivetti
to Luca Olivetti: I constantly stumble with the behaviour of the TDBGrid. I understand that it's a complex…

El 02/11/15 a les 11:12, Luca Olivetti ha escrit:

Show quoted text 28 lines
Hello, I constantly stumble with the behaviour of the TDBGrid. I understand that it's a complex component, but every time I find an issue an Luiz fixes it, two more pop up (or an older one resurfaces). The latest episode of the saga is the futility of using a custom editor (with OnSelectEditor), since the grids then sets the value of the field using this snippet of code if (FEditornil) and FEditor.Visible then begin Msg.LclMsg.msg:=GM_GETVALUE; Msg.grid:=Self; Msg.Col:=FCol; Msg.Row:=FRow; Msg.Value:=GetCells(FCol, FRow); FEditor.Dispatch(Msg); SetEditText(Msg.Col, Msg.Row, Msg.Value); end; and this only works with the editors defined in Grids.pas, the only ones managing the GM_GETVALUE message. For other kinds of editors the result depends on how you move in the grid: it could either maintain the value the editor set or reset it to an empty string. I think I will file a bug once I can create a simple project, but in the meantime if somebody has an idea on how to solve it, it would be appreciated.

Bug created:

http://bugs.freepascal.org/view.php?id=28944

Bye

Luca Olivetti Wetron Automation Technology http://www.wetron.es/ Tel. +34 93 5883004 (Ext.3010) Fax +34 93 5883007

Reply by email

Luca Olivetti
to Tony Whyman: Intercept classes are a very useful technique for overcoming this problem. I recently…

El 02/11/15 a les 11:24, Tony Whyman ha escrit:

Show quoted text 5 lines
Luca, Intercept classes are a very useful technique for overcoming this problem. I recently wanted to have a specialised TComboBox as a TStringGrid Editor and defined an intercept class for this as:

Yes, I used similar hacks in the past (just last week I asked here how to remove the border in a TPageControl), and I'll probably follow your advice here, still, I think that if the TDbGrid allows for a custom editor, it should work without this kind of hack.

Bye

Luca Olivetti Wetron Automation Technology http://www.wetron.es/ Tel. +34 93 5883004 (Ext.3010) Fax +34 93 5883007

Reply by email

Luca Olivetti
to Mattias Gaertner

El 02/11/15 a les 12:36, Mattias Gaertner ha escrit:

Show quoted text 7 lines
On Mon, 2 Nov 2015 10:52:25 +0000 Tony Whyman wrote: > Ah, but then you can't edit it in the IDE. The IDE does not know about your new class. You need to unregister the old and register the new.

it doesn't matter: the ide uses the old class, but the unit (and the grid) will see the new one.
It's a hack, really, which will probably bite me in the rear somewhere in the future, but I have to ship the programme now :-(

Bye

Luca Olivetti Wetron Automation Technology http://www.wetron.es/ Tel. +34 93 5883004 (Ext.3010) Fax +34 93 5883007

Reply by email

Mattias Gaertner
to Luca Olivetti
Show quoted text 8 lines
[...] > The IDE does not know about your new class. > You need to unregister the old and register the new. it doesn't matter: the ide uses the old class, but the unit (and the grid) will see the new one. It's a hack, really, which will probably bite me in the rear somewhere in the future, but I have to ship the programme now :-(

Your 'hack' is totally fine, if you know about it.

I merely wanted to point out that 'TComboBox =
class(stdctrls.TComboBox)' is not a 'hack' and has no special effect.

Mattias

Reply by email

Luca Olivetti
to Luca Olivetti: El 02/11/15 a les 11:12, Luca Olivetti ha escrit: Bug created:…

El 02/11/15 a les 11:56, Luca Olivetti ha escrit:

Show quoted text 34 lines
El 02/11/15 a les 11:12, Luca Olivetti ha escrit: > Hello, > > I constantly stumble with the behaviour of the TDBGrid. > I understand that it's a complex component, but every time I find an > issue an Luiz fixes it, two more pop up (or an older one resurfaces). > The latest episode of the saga is the futility of using a custom editor > (with OnSelectEditor), since the grids then sets the value of the field > using this snippet of code > > if (FEditornil) and FEditor.Visible then begin > Msg.LclMsg.msg:=GM_GETVALUE; > Msg.grid:=Self; > Msg.Col:=FCol; > Msg.Row:=FRow; > Msg.Value:=GetCells(FCol, FRow); > FEditor.Dispatch(Msg); > SetEditText(Msg.Col, Msg.Row, Msg.Value); > end; > > and this only works with the editors defined in Grids.pas, the only ones > managing the GM_GETVALUE message. > For other kinds of editors the result depends on how you move in the > grid: it could either maintain the value the editor set or reset it to > an empty string. > > I think I will file a bug once I can create a simple project, but in the > meantime if somebody has an idea on how to solve it, it would be > appreciated. Bug created: http://bugs.freepascal.org/view.php?id=28944

As I said in the bug report I just discovered
http://www.freepascal.org/~michael/articles/grids/grids.pdf (published in 2008!) describing how to correctly implement a custom editor (so the method from the wiki I originally used is wrong, I also added a note to the wiki).

*However* the component described by Michaël only works with a StringGrid, not with a DBGrid: when you change cell the grids sends the GM_SETVALUE message *before* the GM_GETVALUE one, so the edited value is lost and a semi-random one is used instead.

To make it work one has to override the Change method (as it is done in grids.pas):

procedure TStringCellEditor.Change;
begin
   {$IfDef DbgGrid} DebugLn('TStringCellEditor.Change INIT
text=',Text);{$ENDIF}
   inherited Change;
   if (FGridnil) and Visible then begin
     FGrid.EditorTextChanged(FCol, FRow, Text);
   end;
   {$IfDef DbgGrid} DebugLn('TStringCellEditor.Change END');{$ENDIF}
end;

*but* the EditorTextChanged method is protected, so an additional cracker class (ugly) has to be used, e.g.

TMyGrid = class(TCustomGrid)
   procedure EditorTextChanged(const aCol, aRow: Integer; const aText:
string); override;
end;
procedure TMyGrid.EditorTextChanged(const aCol, aRow: Integer;
   const aText: string);
begin
   inherited EditorTextChanged(aCol, aRow, aText);
end;
procedure TMyEditor.Change;
begin
  if (FGridnil) and Visible then begin
     TMyGrid(FGrid).EditorTextChanged(FCol, FRow, Text);
end;

Maybe EditorTextChanged (and others methods to be used in custom editors) should be public?
Comments?
Is there something else that I'm missing?

Bye

Luca Olivetti Wetron Automation Technology http://www.wetron.es/ Tel. +34 93 5883004 (Ext.3010) Fax +34 93 5883007

Reply by email