TDBGrid not saving TColumn.Width property after Visible := false - delphi

When saving a form with a TDBGrid having a column with Visible = false, the column's Width property is not saved, and the original value of the Width property is lost.
The context is a TColumn in a standard VCL TDBGrid control on a form (Delphi 10.1 Berlin). If the Visible property of the column is set to false in the object inspector, the Width property immediately goes to -1. Setting Visible back to true, restores the previous value of Width.
However, if the form is saved while the column's Visible property is false, and the form is closed and reopened, then the same column's Visible property is set to true again, the Width always comes back as 163839. This of course hides any other columns to the right, and messes up scrolling, which rightly confuses non-technical users.
I suspect it is a VCL bug, since the property getter for the TColumn.Width property (in unit Vcl.DBGrids) is as follows:
unit Vcl.DBGrids;
...
function TColumn.GetWidth: Integer;
begin
if not Showing then // depends on Visible property
Result := -1 // obviously streamed to .DFM
else if cvWidth in FAssignedValues then
Result := FWidth
else
Result := DefaultWidth;
end;
Is this a VCL bug, or am I using it wrong?
PS: does this have anything to do with TColumn.AssignedValues? Or is the built-in Delphi DBGrid just flawed.
Edit: My app allows the user to resize, reorder and show/hide columns in a main results grid at run-time, but also to restore back to defaults. I'm using the grid at design-time as the "master" when restoring defaults, i.e. default column widths and order, and also which columns are visible by default. But losing the column Width for columns hidden by default is messing with that.

Related

How to change DBgrid row value?

I would like to change DBgrid row value with outside data, how to achieve that?
For example i would like to add Tedit value into dbgrid selected row column(for example 5) via button click.
Also, i would like to add value from 1 dbgrid to another dbgrid.(Add to an existing number, not replace).
Suppose you have a dataset connected to db-aware controls like TDBEdit and TDBGrid via a TDataSource. Delphi's db-aware controls are basically the default ones which come with Delphi and are displayed on the Data controls tab of its Component Palette plus any 3rd-party ones that you install. The reason they are called db-aware is because they are written so that the values they display are automatically derived from the related fields of the dataset.
Also suppose that the dataset is called Table1 and has a CustomerName field that you want to change. The simplest code which will achieve that is something like:
Table1.Edit; // put table one into dsEdit state so that field values can be changes
Table1.FieldByName('CustomerName').AsString := 'Jones';
Table1.Post; // save the change(s) to Table1
More optimal code might be
Table1.Edit;
try
Table1.DisableControls; // this prevents the db-aware controls updating on-screen while the changes are made
Table1.FieldByName('CustomerName').AsString := 'Jones';
Table1.FieldByName('CustomeCountry').AsString := 'DE#;
finally
Table1.Post;
Table1.EnableControls; // Eable screen updating od the db-aware controls again
end;
That would update the values displayed in DBEdit controls linked to the CustomerName and CustomerCountry fields and in the CustomerName and CustomerCountry cells of the current (i.e. highlighted) row in the DBGrid, because the current row in the DBGrid always tracks the current row in the connected dataset.
If you really do want to update a given column from a TEdit's text you could do that like this:
Table1.Edit;
DBGrid1.Columns[5].Field.AsString := Edit1.Text;
Table1.Post;

How do you right justify DBGrid titles when using a FDMemtable in Delphi?

I can't seem to get the fixed row Titles in a DBGrid to align right justified when using a FDMemtable. Whenever I set the Field alignment to taRightJustify it right justifies the data cells perfectly. However, the DBGrid titles are always left justified.
What's even more frustrating is I can set the corresponding DBGrid column title alignment to taRightJustify and it appears perfectly fine in the IDE. But when I run the program the column title shows as left justified.
Has anyone found a way to make DBGrid column titles stay right justified when using a FDMemtable?
BTW, this also happens with taCenter. The data cells align centered but the titles stay left justified.
PEBKAC
The issue was of my own making. I did not invoke the DBGrid Columns Editor and add all the fields. Instead, I was using the "Structure" pane and getting to the DBGrid columns that way. Although the Structure pane allowed me to modify the column titles this was only temporary and did not persist when the program was run.
I can't reproduce the issue using a TFDMemTable either.
I dropped a TFDMemTable, TDataSource, and TDBGrid on a new VCL application's main form, connected them as usual (the grid's datasource set to DataSource1, the datasource's DataSet set to FDMemTable1), and then added the below code to the form's OnCreate event:
procedure TForm3.FormCreate(Sender: TObject);
begin
FDMemTable1.FieldDefs.Add('ID', ftInteger, 0, True);
FDMemTable1.FieldDefs.Add('LastName', ftString, 20);
FDMemTable1.FieldDefs.Add('FirstName', ftString, 20);
FDMemTable1.FieldDefs.Add('Salary', ftCurrency);
FDMemTable1.CreateDataSet;
FDMemTable1.Active := True;
FDMemTable1.AppendRecord([1, 'Smith', 'John', 30000]);
FDMemTable1.AppendRecord([2, 'Jones', 'Jane', 40000]);
FDMemTable1.AppendRecord([3, 'Doe', 'David', 2500]);
DBGrid1.Columns[3].Alignment := TAlignment.taRightJustify;
DBGrid1.Columns[3].Title.Alignment := TAlignment.taRightJustify;
end;
It also works correctly if I set everything up at designtime. Repeat the same setup steps I used above, but instead of using the code, use the following steps:
Select FDMemTable1 in the Object Inspector. At the bottom of the OI, click the LoadFromFile link, and navigate to the BDS Samples data folder (by default, in C:\Users\Public\Public Documents\Embarcadero\Studio\17.0\Samples\Data) and select animals.fds. (No specific reason for choosing that one, except it has a numeric field we can use for testing.)
Right-click on the DBGrid, and choose Columns Editor, or click the ellipsis button on the DBGrid.Columns property in the Object Inspector. Right-click in the Columns Editor and choose Add all fields.
Select either the Size or Weight column, expand it's Title property, and set Alignment to taRightJustify.
Run the application. The column you modified in step #3 above has a right-aligned title. (Here I used the Size column.)
The code below works for me in Seattle. I'm using a TClientDataSet rather than a TFDMemTable, but I can't see that that would make any difference.
If you have persistent columns defined on your DBGrid, you can also set a column's title alignment via the Object Inspector - use it to select the column, then expand its Title node and you can set the title alignment there.
procedure TForm1.CDS1AfterOpen(DataSet: TDataSet);
var
i : Integer;
begin
for i := 0 to DBGrid1.Columns.Count - 1 do
DBGrid1.Columns[i].Title.Alignment := taRightJustify;
end;
Btw, if you think you're setting the alignment in the OI but it's getting ignored, see if you can find out why, as follows:
Make sure your form is saved, then right-click on it and select View as text. Then, in the IDE editor window, you can see whether the Alignment property is saved as you've specified in the OI. Use the editor context menu to return to viewing the form as a form.
Add an override of the form's Loaded method as I've described in a comment. With a breakpoint on the inherited in Loadeds body, you can inspect the Alignment value before and after inherited is called.
Why am I suggesting looking into Loaded? Well, it is called after a form is streamed in from the DFM and is the routine where the run-time system finishes setting up the form. Sometimes, admittedly very rarely, another component (usually a 3rd-party one) misbehaves and causes strange behaviour of the properties of other compononents.

How to cope with the bug with style lookup in controls nested in FMX Frames

I have met a bug in applying of StyleLookup in TTabControl nested in FMX Frame. The appearance of the control does not accept the style if the frame with the control is nested in a HorzScrollBox and at form creation the position of the frame is outside the visible area of the form. In other words if the control is visible when the form is shown the control is painted with necessary style appearance otherwise - not.
To reproduce the bug:
Create a new grid metropolis application.
Create a Frame and put a TTabControl on it. Set Align of the TabControl to alClient. Add, say, 4 TTabItems.
Open detail form, and set its width to a value > your Screen.Width (for me 2500 was enough).
Copy-paste any of the Columns (Layouts), say, 2 times and use the most right layout to nest a frame in it. You will see normal presentation of tabitems at designtime. Set style settings. You will get the following picture:
Done. Run the project. You will get the bug.
At runtime the appearance of TabItem is like this:
If you set the StyleLookup manually at runtime the appearance might either not change either be set to every other item (for example 1st, 3rd and not set to 2nd and 4th though you assign the same StyleLookup to all TabItems). The other interesting finding. If you have other forms containing TabControls with TabItems (nested without frames and closer to left border of the form) for example Form2, you get the following. If you show the faulty Form1 at runtime first you will see the bug, but if you close this form and show Form2 you see proper TabControl. Closing Form2 and showing Form1 (faulty) afterwards will give you a proper appearance of TabControl in a faulty form.
It has something in common with the bug reported by me earlier: Incorrect selection of items in an FMX TListbox (Grid Metropolis UI). It is still not fully solved.
There was also a question yesterday but it is about an exact problem with VCL Frames and the solution is not suitable for FMX.
Appended.
The way is to partially set the style is to set OnPainting event handler for TTabControl in the parent form unit (it does not work being set in frame unit) and write something like this:
procedure TPatientsScrollF.HMDiagnosisFr1TabControl2Painting(Sender: TObject;
Canvas: TCanvas; const ARect: TRectF);
var
i: byte;
begin
(Sender as TTabControl).StyleLookup := 'tabcontrolstyle';
for i := 0 to (Sender as TTabControl).TabCount-1 do
(Sender as TTabControl).Tabs[i].StyleLookup := 'tabitemstyle';
end;
But than you still get a problem - the Tabs are not drawn properly - see the lower edge of inactive Tabs.
Or even like this:
Appended 2
I have just met the bug appearing even at design-time after placing a frame in a form and assigning style to TabItems. The TabControl looks like in fig. 3.

First use of TListView and TTreeView does not show data

I have only one form in my application, where I insert a frame accordingly the user action.
This form is inserted in a TScrollBox, since it sometimes has a width bigger than the screen/window.
I have one or more TListViews on the frame, and many TEdits.
When running the application and opening any frame, one or more TListView are not filled in. By debug I see the data is pulled from the SQL. TEdits are filled in. Moving the SQL cursors gets all TEdits updated accordingly.
The closing this frame and opening again (or any other) then it start showing the data on the TListView.
I have not found any correlation that make sense. Apparently there is some kind of initialization missing. Not all TListView are empty, in some frames I have 4 or 5 of them and it show 2 with data, the others are empty.
EDIT: I have changed the title of this question, since I have noted that the problem seems not be related to the LiveBindings primarly, but seems to be related to some kind of initialization of the TListView on other lists.
I made a new test and see that TTreeView is also having the same problem, showing totally out of order the data, only at first time. If I previouslly opens something else it works fine, if I close and open the frame with the TTreeview the second time is ok.
In this image I show the TreeView messed:
It should not have all that spaces and some of the nodes are overlapped.
My application has only one form, and everything are frames that are created as parent of a TScrollBox.
With Delphi XE5 the issues of TScrollBox were many, I had to upgrade to XE6.
Many things start working with XE6, however there is still a little problem with TScrollBox:
There is a need to call ApplyStyleLookup, after the frame is inserted and parented to the scroll, to make the initialization. That makes the TListView and TTreeView works fine since the very first time.
constructor TFrameBase.Create(AOwner: TComponent);
begin
inherited;
Align := TAlignLayout.Left;
Parent := AOwner as TFmxObject;
if (AOwner is TScrollBox) then
(AOwner as TScrollBox).ApplyStyleLookup;
end;
This is my frame constructor. This fixed all the problems.

Column.Index is not reflecting immediately in TDBGrid

I have a TDBGrid and have a Client dataset associated through a TDataSource.
When i try to re-arrange the columns of the grid programtically this works fine when there are one or more columns in the grid.
Lets say, i made all the columns visible to false. So, the grid does not have any columns yet.
Now, when i set the Index of column at 11 to 0.
Columns[iColIndx].Index := iNewColIndex;
// Assume iColIndx = 11 and iNewColIndex = 0
Columns[iNewColIndex].Visible := True;
//Making the column visible
The above assignment of column index is not getting reflected in the Grid. If i try setting it for twice or thrice, then it is getting reflected. Am i missing anything?
I solved it. I have assigned OnDrawDataCell of the grid for drawing the gradients, but I was not calling the DefaultDrawDataCell from there. Moreover, the DefaultDrawing property was turned off.
After setting DefaultDrawing and calling DefaultDrawDataCell from within the DrawDataCell event handler, it is working fine now.

Resources