

Gustav Schubert
Members-
Content Count
123 -
Joined
-
Last visited
-
Days Won
1
Gustav Schubert last won the day on December 11 2019
Gustav Schubert had the most liked content!
Community Reputation
30 ExcellentTechnical Information
-
Delphi-Version
Delphi 12 Athens
Recent Profile Visitors
The recent visitors block is disabled and is not being shown to other users.
-
The September Patch Readme mentions a fix for FMX. Please note: This change in method TScrollContent.IsVisibleChild, also fixes one the two issues in my test code above. The workaround in TestBtnClick is no longer needed, but the one in FormDestroy is still necessary.
-
Sure. SG02.zip
-
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.
-
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.
-
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.
-
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.
-
I tried it out and have this, more aligned with my thinking: procedure TFormMain.AlignSegment; var p: TPoint3D; // TPoint3D is our 3D vector dx, dy, dz: single; l: single; ay, az: single; begin p := M2.Position.Point - M1.Position.Point; dx := p.X; dy := p.Y; dz := p.Z; p.Z := 0; l := p.Length; // length of projection az := RadToDeg(arctan2(dy, dx)); // azimut ay := RadToDeg(arctan2(dz, l)); // declination Segment.ResetRotationAngle; Segment.RotationAngle.Z := az; Segment.RotationAngle.Y := -ay; Segment.Position.Point := M1.Position.Point.MidPoint(M2.Position.Point); end;
-
12.3 April Patch 1.0 Personality Error / Incomplete
Gustav Schubert replied to dwb's topic in General Help
Thanks for you excellent precision, it was helpful! The documents folder is apparently the folder where everyone will dump their stuff. My attempt to make sure that nothing in there will be automatically saved to the cloud failed at least once. It is a no go area for my documents. I almost never look there. So, when I try to find the Catalog Repository folder, I go straight to "Dieser PC" in Explorer and try my luck in "Öffentlich" first. I guess the new version of PAServer fixes the need to copy newly installed provisioning files from the new location to the location where PAServer is looking for them. But how can one know? -
12.3 April Patch 1.0 Personality Error / Incomplete
Gustav Schubert replied to dwb's topic in General Help
I have just installed the May patch via GetIt, using the link in the IDE. I see two Catalog Repository folders, one in the public documents library, and one in the user account document library, which has the new files. -
Delphi 12 Athens Refactoring Broken
Gustav Schubert replied to Navid Madani's topic in Delphi IDE and APIs
I can only comment on the rename refactoring. My impression is that it works about the same as before. Therefore: not using it is not an option. But, still my point here, just for anyone else who is reading, my own user story: I use Ctrl-Shift-E a lot. And that was my bigggest problem with Delphi 12, because the IDE would disappear within two seconds from pressing Ctrl-Shift-E. It took me days to find out that installing the modelling option was the solution. I can now use rename refactoring as before, with about the same expectation. But back to the topic now, I think the question should have been: Has the (rename) refactoring functionality in Delphi 12/Athens deteriorated, in comparison to 10.1 Berlin? ( I don't know, will read about it here... ) -
Delphi 12 Athens Refactoring Broken
Gustav Schubert replied to Navid Madani's topic in Delphi IDE and APIs
To get rename refactoring back, install the modelling option, as Uwe pointed out in RSP-42936. It worked for me! -
You could vote for feature request RSP-18851. It suggests a new event for TCommonCustomForm, which is called when resize has ended.
-
We only need to deal with the flattened points, distance to line segment computation is not needed. I know this only because I have just tested out the FlattenToPolygon approach starting from the PathDemo.zip test program from above. To find the distance between two points is easy when working with TPointF. It is more difficult to update the properties of a supposedly easy to use TLine test component. One would have expected a TLine component to have TPointF properties for start and end points, but nope. The snippets below may be of help, when building the interactive test program. procedure TFormMain.InitProps; begin { Since we are using design time test components in the test program ... } { the TPath under test } Path.HitTest := False; { a TCircle } Circle.Width := 50; Circle.Height := 50; Circle.HitTest := False; Circle.Opacity := 0.5; Circle.Stroke.Color := claBlue; { a TLine } Line.LineType := TLineType.Top; Line.RotationCenter.Point := TPointF.Zero; Line.Height := 0; Line.HitTest := False; Line.Stroke.Color := claRed; end; procedure TFormMain.PanelMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin if FDragging then begin FDragging := False; ControlPoint1.Visible := True; ControlPoint2.Visible := True; end else if ssCtrl in Shift then begin ShowClosestPoint(TPointF.Create(X, Y)); // <-- new: Ctrl-Click to test! end end; procedure TFormMain.ShowClosestPoint(P: TPointF); var T: TPointF; PT: TPointF; begin { T is closest point on Path, measured from test point P } T := FindClosestPoint(Path, P); { center the translucent circle at the closest point } Circle.Position.Point := T - TPointF.Create(Circle.Width, Circle.Height) / 2; { update the test line properties, so that Line is drawn from P to T } PT := T - P; Line.Position.Point := P; Line.Width := PT.Length; Line.RotationAngle := 180 + RadToDeg(TPointF.Zero.Angle(PT)); end; function TFormMain.FindClosestPoint(APath: TPath; APoint: TPointF): TPointF; var Poly: TPolygon; l: Integer; i: Integer; d: single; iMin: Integer; dMin: single; begin APath.Data.FlattenToPolygon(Poly, 2); l := Length(Poly); if l < 1 then Exit; dMin := APoint.Distance(Poly[0]); iMin := 0; for i := 1 to l - 1 do begin d := APoint.Distance(Poly[i]); if d < dMin then begin dMin := d; iMin := i; end; end; result := Poly[iMin]; end;
-
Function with 2 return values ?
Gustav Schubert replied to Henry Olive's topic in RTL and Delphi Object Pascal
But it started with two good answers at the top, which I liked, to make it clear. 🙂 -
Function with 2 return values ?
Gustav Schubert replied to Henry Olive's topic in RTL and Delphi Object Pascal
You can return a TPointF: procedure Test; var A, B: Currency; // <-- ? P: TPointF; function GetResult(const s: string): TPointF; begin result.X := 99.98; result.Y := -1.01; end; begin { get going fast } P := GetResult('ABC'); { in order to be able to play with some data types } A := P.X; B := P.Y; end;