Gustav Schubert 28 Posted Thursday at 09:26 PM FMX: What is the recommended way to load example data into TStringGrid? I have this, but it no longer works: unit FrmMain; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, FMX.Types, FMX.Controls, FMX.Forms, Data.Bind.EngExt, FMX.Bind.DBEngExt, FMX.Bind.Editors, Data.Bind.Components, Data.Bind.DBScope, Data.Bind.DBLinks, Fmx.Bind.DBLinks, FMX.Layouts, FMX.Grid, Data.DB, Datasnap.DBClient, FMX.Edit, System.Bindings.Outputs, FMX.StdCtrls, System.Rtti, FMX.Grid.Style, FMX.Controls.Presentation, FMX.ScrollBox; type TFormMain = class(TForm) StringGrid: TStringGrid; TestBtn: TButton; procedure FormCreate(Sender: TObject); procedure TestBtnClick(Sender: TObject); private ClientDataSet: TClientDataSet; DataSource: TDataSource; BindScope: TBindScopeDB; BindingsList: TBindingsList; gl: TBindDBGridLink; Counter: Integer; end; var FormMain: TFormMain; implementation {$R *.fmx} procedure TFormMain.FormCreate(Sender: TObject); var dn: string; fn: string; pn: string; ML: TStringList; begin TestBtn.Enabled := False; ReportMemoryLeaksOnShutdown := True; dn := 'C:\Users\Public\Documents\Embarcadero\Studio\37.0\Samples\Data\'; fn := 'country.xml'; pn := dn + fn; if FileExists(pn) then begin ClientDataSet := TClientDataSet.Create(self); BindingsList := TBindingsList.Create(self); ML := TStringList.Create; ML.LoadFromFile(pn); ClientDataSet.Active := False; ClientDataSet.XMLData := ML.Text; ClientDataSet.Active := True; ML.Free; DataSource := TDataSource.Create(self); DataSource.AutoEdit := False; DataSource.DataSet := ClientDataSet; BindScope := TBindScopeDB.Create(self); BindScope.DataSet := ClientDataSet; BindScope.DataSource := DataSource; TestBtn.Enabled := True; end; end; procedure TFormMain.TestBtnClick(Sender: TObject); begin if (gl <> nil) then gl.Active := False; gl.Free; gl := TBindDBGridLink.Create(BindingsList); gl.AutoBufferCount := False; gl.Category := 'DB Links'; gl.DataSource := BindScope; gl.GridControl := StringGrid; // <-- Exception in D13 // in D12.3: ok // in D13: Crash because Position = nil in TControl.GetBoundRect { function TControl.GetBoundsRect: TRectF; begin // if Position = nil then // result := TRectF.Create(0, 0, 0, 0) // else Result := TRectF.Create(Position.X, Position.Y, Position.X + Width, Position.Y + Height); end; } gl.Active := True; Inc(Counter); Caption := IntToStr(Counter); end; end. Share this post Link to post
Dave Nottage 634 Posted Thursday at 09:28 PM 2 minutes ago, Gustav Schubert said: <-- Exception in D13 What is the exception? Please run it in the debugger and show the callstack at that point. Share this post Link to post
Gustav Schubert 28 Posted Thursday at 09:44 PM 12 minutes ago, Dave Nottage said: What is the exception? FMX.Controls.TControl.GetBoundsRect FMX.ScrollBox.TScrollContent.IsVisibleChild($CB85650) FMX.Controls.TControl.RecalcUpdateRect FMX.Controls.TControl.InternalSizeChanged FMX.Controls.TControl.HandleSizeChanged FMX.Controls.TControl.SizeChanged(???) FMX.Controls.TControl.RecalcSize FMX.ScrollBox.Style.TStyledCustomScrollBox.RealignContent FMX.Grid.Style.TStyledGrid.MMInvalidateContentSize(???) FMX.Presentation.Messages.TMessageSender.SendMessage(???) FMX.Grid.TGridModel.InvalidateContentSize FMX.Grid.TGridModel.RemoveColumn(???) FMX.Grid.TCustomGrid.ContentBeforeRemoveObject(???) FMX.ScrollBox.TScrollContent.DoRemoveObject($CB85650) FMX.Types.TFmxObject.RemoveObject(???) FMX.Types.TFmxObject.Destroy FMX.Controls.TControl.Destroy FMX.Grid.TColumn.Destroy System.TObject.Free Fmx.Bind.DBLinks.TBindDBStringGridColumnCreator.ClearColumns Data.Bind.DBLinks.TBaseBindDBGridLink.ClearColumns(TBindDBStringGridColumnCreator($CB95270) as IBindDBGridLinkControlManager) Fmx.Bind.DBLinks.TCustomBindDBGridLink.ClearColumns(TBindDBStringGridColumnCreator($CB95270) as IBindDBGridLinkControlManager) Fmx.Bind.DBLinks.TCustomBindDBGridLink.ClearGeneratedExpressions(???) Fmx.Bind.DBLinks.TCustomBindDBGridLink.UpdateColumns Data.Bind.DBLinks.TInternalBindGridLink.ApplyComponents Data.Bind.Components.TActivatedContainedBindComponent.UpdateControlChanged Data.Bind.Components.TCommonBindComponent.SetControlComponent($62A29B0) Fmx.Bind.DBLinks.TBaseBindDBGridControlLink.SetControl(???) FrmMain.TFormMain.TestBtnClick($4EC7280) FMX.Controls.TControl.Click FMX.StdCtrls.TCustomButton.Click FMX.Controls.TControl.MouseClick(???,???,45,14) FMX.Forms.TCommonCustomForm.MouseUp(mbLeft,[ssLeft],141,38,???) FMX.Platform.Win.WndProc(460274,514,0,2490509) FMX.Platform.Win.TPlatformWin.HandleMessage FMX.Forms.TApplication.HandleMessage FMX.Platform.Win.TPlatformWin.Run FMX.Forms.TApplication.Run SG02.SG02 I get access violation, Position is nil in TControl.GetBoundsRect. Share this post Link to post
david berneda 66 Posted Friday at 01:14 PM I had a similar bug but I couldn't reproduce it anymore. Looks like RemoveColumn triggers some calls that act too late, when Position etc are already destroyed (use-after-nil). I did submit a QC issue but never was activated as I couldn't add reproduceable steps. Share this post Link to post
david berneda 66 Posted Friday at 01:18 PM As an alternative, this is our free api that can load data into grids (Vcl and Fmx), just doing this: uses BI.Xml; BIGrid1.Data := TBIXml.FromFile('country.xml') https://github.com/Steema/TeeBI Share this post Link to post
Gustav Schubert 28 Posted Friday at 05:11 PM I now have a workaround: Need to unload the Presentation before attempting the critical assignment. procedure TFormMain.TestBtnClick(Sender: TObject); begin StringGrid.UnloadPresentation; // to avoid problems later if (gl <> nil) then gl.Active := False; gl.Free; gl := TBindDBGridLink.Create(BindingsList); gl.AutoBufferCount := False; gl.Category := 'DB Links'; gl.DataSource := BindScope; gl.GridControl := StringGrid; // <-- critical line gl.Active := True; StringGrid.LoadPresentation; // now it is safe to have a presentation Inc(Counter); Caption := IntToStr(Counter); end; Depending on who the Owner is of these databinding components, Form or Application, it may be necessary to do the same (unloading the presentation of the string grid) when the application closes down. I needed to do this in FormClose, FormDestroy would be tool late. Share this post Link to post
david berneda 66 Posted Friday at 07:20 PM Thanks for the unload tip ! I'll check with our grids if the bug happen. 1 Share this post Link to post
Gustav Schubert 28 Posted 16 hours ago This topic is about the access violation in TControl.GetBoundsRect when using a TStringGrid with data binding, Win32 platform. It is a "regression" in Delphi 13. By now, there are two known manifestations of the problem: when assigning to the GridControl property of TBindDBGridLink, and when closing down the application. New: I have investigated the second manifestation, when closing down. To do so I am passing nil as Owner when creating components, so that I have control over the order of destruction in FormDestroy. Order of destruction matters, StringGrid is key here. When the StringGrid is destroyed first, no problem. When StringGrid is not destroyed first, then I need the workaround, and unload the presentation, to avoid the problem. Updated test program: unit FrmMain; interface uses System.SysUtils, System.Classes, Data.Bind.Components, Data.Bind.DBScope, Data.DB, Datasnap.DBClient, FmX.Bind.DBEngExt, Fmx.Bind.DBLinks, FmX.Bind.Editors, FMX.Forms, FMX.Controls, FMX.Grid, FMX.Grid.Style, FMX.Edit, FMX.StdCtrls, FMX.ScrollBox, FMX.Controls.Presentation; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private TestBtn: TButton; StringGrid: TStringGrid; ClientDataSet: TClientDataSet; DataSource: TDataSource; BindScope: TBindScopeDB; BindingsList: TBindingsList; gl: TBindDBGridLink; procedure TestBtnClick(Sender: TObject); end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); var dn: string; fn: string; pn: string; ML: TStringList; begin ReportMemoryLeaksOnShutdown := True; TestBtn := TButton.Create(nil); TestBtn.Parent := Self; TestBtn.Position.X := 10; TestBtn.Position.Y := 10; TestBtn.Name := 'TestBtn'; TestBtn.OnClick := TestBtnClick; TestBtn.Enabled := False; StringGrid := TStringGrid.Create(nil); StringGrid.Parent := Self; StringGrid.Position.X := 10; StringGrid.Position.Y := 60; StringGrid.Width := 600; StringGrid.Height := 200; StringGrid.Name := 'StringGrid'; ClientDataSet := TClientDataSet.Create(nil); BindingsList := TBindingsList.Create(nil); dn := 'C:\Users\Public\Documents\Embarcadero\Studio\37.0\Samples\Data\'; fn := 'country.xml'; pn := dn + fn; if FileExists(pn) then begin ML := TStringList.Create; ML.LoadFromFile(pn); ClientDataSet.Active := False; ClientDataSet.XMLData := ML.Text; ClientDataSet.Active := True; ML.Free; DataSource := TDataSource.Create(nil); DataSource.AutoEdit := False; DataSource.DataSet := ClientDataSet; BindScope := TBindScopeDB.Create(nil); BindScope.DataSet := ClientDataSet; BindScope.DataSource := DataSource; TestBtn.Enabled := True; end; end; procedure TForm1.FormDestroy(Sender: TObject); begin StringGrid.UnloadPresentation; gl.Free; BindScope.Free; BindingsList.Free; DataSource.Free; ClientDataSet.Free; StringGrid.Free; TestBtn.Free; end; procedure TForm1.TestBtnClick(Sender: TObject); begin TestBtn.Enabled := False; StringGrid.UnloadPresentation; gl := TBindDBGridLink.Create(nil); gl.AutoBufferCount := False; gl.Category := 'DB Links'; gl.DataSource := BindScope; gl.GridControl := StringGrid; gl.Active := True; StringGrid.LoadPresentation; end; end. 1 Share this post Link to post
Dave Nottage 634 Posted 15 hours ago 1 hour ago, Gustav Schubert said: Updated test program: Can you upload your test program here? Alternatively, create an entry in QP and attach there? Share this post Link to post
Gustav Schubert 28 Posted 12 hours ago 3 hours ago, Dave Nottage said: Can you upload your test program here? Sure. SG02.zip Share this post Link to post