I've seen this question asked as part of another question before, so know it can't just be me...if I open a new FireMonkey2 HD app, add a TButton and TStringGrid, then add this to the button on click event, NOTHING happens in the grid when I click the button!
procedure TForm33.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i:= 0 to 6 do
begin
StringGrid1.Cells[0,i] := 'Row:' + IntToStr(i);
end;
stringgrid1.UpdateColumns;
stringgrid1.SetFocus;
end;
Any ideas ?
PS I've also tried using TStringGrid.OnGetValue and it still won't show anything in the StringGrid.
Having looked further into the TStringGrid source code, C is nil so the Cells are never being set.
procedure TStringGrid.SetValue(Col, Row: Integer; const Value: TValue);
var
C: TColumn;
begin
C := Columns[Col];
if Assigned(C) then
begin
C.UpdateRowCount(RowCount);
C.SetCells(Row, Value);
end;
I appears there are no Columns in the "virgin" StringGrid, so how do you add them ? There is a r+w RowCount property but ColCount is readonly...
You must add at least a column to the TStringGrid in order to add data to the cells, you can do this in runtime
StringGrid1.AddObject(TStringColumn.Create(Self));
or in design time using the Items Designer
Related
I'm trying to iterate between all components inside a TTabsheet. Problem is, inside this tab there are only a memo and a edit, but my code iterate between components in all form. What am I'm missing ?
var i : integer;
begin
with PageControl1.ActivePage do
for i := 0 to componentcount-1 do
begin
// componentcount should be 2, but actually is 95
components[i].doSomething;
end;
end;
I had something like this, where a button click caused code to traverse over all the controls that were on a tabsheet that was on a page control, using the components array. Later on I changed it to the following, that uses the controls array of the given tabsheet.
procedure TShowDocsByRank.CleanBtnClick(Sender: TObject);
var
i: integer;
begin
for i:= 0 to tabsheet1.controlcount - 1 do
if tabsheet1.controls[i] is TLabeledEdit
then TLabeledEdit (tabsheet1.controls[i]).text:= ''
else if tabsheet1.controls[i] is TComboBox
then TComboBox (tabsheet1.controls[i]).text:= '-';
end;
Perhaps 'with' might have something to do about it. You cant really tell what the return value for 'componentcount' will be (might even return number of components for the form itself?).
I made a very simple testproject in Delphi 10.2 using FMX.
The setup is simple :
TGrid that is binded to a TClientDataSet (done in the designer).
button that allows the user to open an XML file
This all works fine and the TGrid is populated with all records from the XML File. The XML file is created by another TClientDataSet from an older project.
Now for the problem.
When I move a column to another position all the data is messed up. I do this by just holding down the mouse on a column and than drag it a few columns to the right.
At first it looks fine, but when you start scrolling vertical, it seems that the data is not in the correct columns anymore.
I have the feeling that it only corrects the data in the visual part of the grid, and as soon as you start scrolling the data is not in the correct columns anymore.
Is this a known bug or is there something wrong with my project.
As I said before, there is absolutely no code in this project all is done in the designer. (except for the clientdataset1.LoadFromFile offcourse)
You can try populate your data manually (Grid: TGrid; CDS: TClientDataSet):
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
Col: TColumn;
begin
CDS.Active := True;
for I := 0 to CDS.FieldDefs.Count - 1 do begin
Col := TColumn.Create(Grid);
Grid.AddObject(Col);
Col.Header := CDS.FieldDefs[I].Name;
Col.Tag := I;
end;
Grid.RowCount := CDS.RecordCount;
end;
procedure TForm1.GridGetValue(Sender: TObject; const ACol, ARow: Integer; var Value: TValue);
begin
CDS.First;
CDS.MoveBy(ARow);
Value := CDS.Fields[ACol].Text;
end;
And after this you can use my solution for columns: stackoverflow.com/q/43418528/2292722
This fixed it for me.
I just move the fields that where moved in the grid also in the ClientDataSet and thus far it seems to work.
procedure TForm1.Grid1ColumnMoved(Column: TColumn; FromIndex, ToIndex: Integer);
var
FieldFrom : string;
FieldTo : string;
begin
FieldFrom := Grid1.ColumnByIndex(FromIndex).Header;
FieldTo := Grid1.ColumnByIndex(ToIndex).Header;
ClientDataSet1.FieldByName(FieldFrom).Index := FromIndex;
ClientDataSet1.FieldByName(FieldTo).Index := ToIndex;
end;
But I just wish there was a better way of knowing from the TColumn which fieldname is involved. Seems like the most significant information is missing from this class.
I would like to retrieve a value from a TDBGrid the selected row, how can I do?
procedure TForm7.Button2Click(Sender: TObject);
var
i, j: Integer;
s: string;
begin
if DBGrid1.SelectedRows.Count>0 then
with DBGrid1.DataSource.DataSet do
for i:=0 to DBGrid1.SelectedRows.Count-1 do
begin
GotoBookmark(DBGrid1.SelectedRows.Items[i]);
for j := 0 to FieldCount-1 do
begin
if (j>0) then s:=s+', ';
s := s + FindField(Fields.Fields[j].FieldName).AsString;
end;
Listbox1.Items.Add(s);
s:= '';
end;
end;
The code below fixes a few problems with yours.
The main problems were the fact that you weren't correctly initialising s and your way of getting the fields for the selected row(s) was flawed.
The calls to DataSet.Disable/EnableControls and ListBox1.Items.BeginUpdate/EndUpdate are to speed the process up.
Also, avoid the with construct like the plague. As you can see, my use of a local DataSet variable instead involves minimal extra typing and avoids all sorts of accidental problems that can arise when you use with.
procedure TForm1.GetSelected;
var
i,
J : Integer;
s : String;
DataSet : TDataSet;
begin
if DBGrid1.SelectedRows.Count>0 then begin
DataSet := DBGrid1.DataSource.DataSet;
// with DBGrid1.DataSource.DataSet do
try
ListBox1.Items.BeginUpdate;
DataSet.DisableControls;
for i:=0 to DBGrid1.SelectedRows.Count-1 do
begin
DataSet.GotoBookmark(Pointer(DBGrid1.SelectedRows.Items[i]));
s := '';
for j := 0 to DataSet.FieldCount - 1 do
begin
if (j>0) then s:=s+', ';
s := s + DataSet.Fields[j].AsString;
//s := s + FindField(Fields.Fields[j].FieldName).AsString;
end;
Listbox1.Items.Add(s);
//s:= '';
end;
finally
DataSet.EnableControls;
ListBox1.Items.EndUpdate;
end;
end;
end;
**Update: **
You can set the current grid row as selected like this
DBGrid1.SelectedRows.CurrentRowSelected := True;
Update #2
The grid's selected rows are stored in a TBookmarkList named SelectedRow. To clear the current selections, all you need do is to call its Clear method, like so:
procedure TForm1.btnClearSelectedClick(Sender: TObject);
begin
DBGrid1.SelectedRows.Clear;
end;
Equally, if you want to clear your ListBox, you just call its Clear method, as in:
procedure TForm1.btnClearListBoxClick(Sender: TObject);
begin
ListBox1.Clear;
end;
If you are having trouble getting my code to work, try this:
In the Object Inspector, set the DBGrid Options property dgMultiSelect to True.
Place a button on your form and in its OnClick handler call GetSelected.
Compile and run. Click a row in the grid and then the button. Nothing happens. The reason is that clicking the button moves focus away from the DBGrid, so that as far as it is concerned, none of its rows is selected. Then try step 3.
Run the app again. This time press and hold the Ctrl key while you click the grid then the button. This time the selected row appears in the ListBox. With the Ctrl button still down, click another row in the grid, then the button. This time, both rows are added to the ListBox.
only a single line call is needed to retrieve a value when you know the column number. The system will automatically return values from the current row, or last line clicked or double-clicked in a grid list.
var colidx : integer; ss : string;
..
ss := DBGrid1.Fields[colidx].AsString;
this is my method to retrieve the key
procedure TfrmArticle.Button2Click(Sender: TObject);
begin
op:='M';
with frmajtArticlee do
begin
Edit1.text:=dm.ADOQueryArticle.fieldbyname('ID_prod').Value;
Edit2.text:=dm.ADOQueryArticle.fieldbyname('ref_art').Value;
Edit3.text:=dm.ADOQueryArticle.fieldbyname('code_barre').Value;
Edit4.text:=dm.ADOQueryArticle.fieldbyname('marque').asstring;
Edit5.text:=dm.ADOQueryArticle.fieldbyname('qts_art').Value;
Edit6.text:=dm.ADOQueryArticle.fieldbyname('designation').asstring;
Edit7.text:=dm.ADOQueryArticle.fieldbyname('prix_achats_ht').Value;
Edit8.text:=dm.ADOQueryArticle.fieldbyname('prix_vente_faciliter').Value;
Edit9.text:=dm.ADOQueryArticle.fieldbyname('prix_vente_cache').Value;
val_mod:=dm.ADOQueryArticle.fieldbyname('ID_prod').asstring;
val_mod:string "declare it in public "
IF YOU'RE NEED TO FIND HOW TO GET DATA OF A SELECTED ROW IN THE DBGRID, I HAD THE HINT. I USE DBGRID.SelectedField TO GET THE SELECTED FIELD AND CONVERT THE FIELD TO BE SELECTED TO BOOKMARK BY DB_GRID.SelectedField.DataSet.Bookmark
AND USE DATASET.GOTOBOOKMART TO GO TO THE SELECTED RECORD FIELD IN DBGRID DB_DATA.DataSet.GotoBookmark (DB_GRID.SelectedField.DataSet.Bookmark);
I clone a panel and its contents(A image and a checkbox) 20 times.
Sample of the panel being cloned:
This is the procedure used to clone a whole panel:
procedure TForm1.ClonePanel(pObjectName: Tpanel);
var apanel : Tpanel;
Ctrl, Ctrl_: TComponent;
i: integer;
begin
//handle the Control itself first
TComponent(apanel) := CloneComponent(pObjectName);
with apanel do
begin
Left := 24;
Top :=64;
end;
//now handle the childcontrols
for i:= 0 to pObjectName.ControlCount-1 do
begin
Ctrl := TComponent(pObjectName.Controls[i]);
Ctrl_ := CloneComponent(Ctrl);
TControl(Ctrl_).Parent := apanel;
TControl(Ctrl_).Left := TControl(Ctrl).Left;
TControl(Ctrl_).top := TControl(Ctrl).top;
end;
end;
The following is the the code that physically does the cloning(called above):
function TForm1.CloneComponent(AAncestor: TComponent): TComponent;
var
XMemoryStream: TMemoryStream;
XTempName: string;
begin
Result:=nil;
if not Assigned(AAncestor) then
exit;
XMemoryStream:=TMemoryStream.Create;
try
XTempName:= AAncestor.Name;
AAncestor.Name:='clone_' + XTempName + inttostr(panels);
inc(panels);
XMemoryStream.WriteComponent(AAncestor);
AAncestor.Name:=XTempName;
XMemoryStream.Position:=0;
Result:=TComponentClass(AAncestor.ClassType).Create(AAncestor.Owner);
if AAncestor is TControl then TControl(Result).Parent:=TControl(AAncestor).Parent;
XMemoryStream.ReadComponent(Result);
finally
XMemoryStream.Free;
end;
end;
So now I want to use the cloned objects but how do I call them in my code?
For example how can I call the checked function of one of the cloned check boxes?
Thanks for your help :)
Others are right and it is better to use frame but if we want just use your code we must fix it first. there is a problem in your code and that is the Inc(panles); position. you must put this line after loop of for i:= 0 to pObjectName.ControlCount-1 do in the ClonePanle procedure, not in the CloneComponent function.
If you fix that, then you can use FindComponent function to access the components that you want as Marko Paunovic said.
For example the name of the component that you put on the first Panel that you defined as the first instance which other cloned panels are cloned from that is TestCheckBox. If you cloned 20 times the Panel that we talked about; you can access the TCheckBox of the 16th Cloned obejct like this and changing it's caption to whatever you want:
(I suppose that the panels variable was 0, when the program started.)
TCheckBox(FindComponent('clone_TestCheckBox15')).Caption:='aaaaa';
My question is how to set a column in dbgrid in Delphi 7 which will be with a checkbox items.
Thanks in advance.
The easiest and most complete method as tested by me is as follows:
In the private section of your unit, declare a global for retaining grid options. It will be used for restoring after temporary disabling text editing while entering the checkbox column - as this is maybe one of the little errors mentioned by Jordan Borisovin regarding the delphi.about.com article
private
GridOriginalOptions : TDBGridOptions;
In the OnCellClick event, if field is boolean, toggle and post change to database
procedure TForm1.DBGrid1CellClick(Column: TColumn);
begin
if (Column.Field.DataType=ftBoolean) then
begin
Column.Grid.DataSource.DataSet.Edit;
Column.Field.Value:= not Column.Field.AsBoolean;
Column.Grid.DataSource.DataSet.Post;
end;
end;
Drawing the checkbox for boolean fields of the grid
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
const
CtrlState: array[Boolean] of integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED) ;
begin
if (Column.Field.DataType=ftBoolean) then
begin
DBGrid1.Canvas.FillRect(Rect) ;
if (VarIsNull(Column.Field.Value)) then
DrawFrameControl(DBGrid1.Canvas.Handle,Rect, DFC_BUTTON, DFCS_BUTTONCHECK or DFCS_INACTIVE)
else
DrawFrameControl(DBGrid1.Canvas.Handle,Rect, DFC_BUTTON, CtrlState[Column.Field.AsBoolean]);
end;
end;
Now the new part, disable cell editing while in the boolean column. On the OnColEnter and OnColExit events:
procedure TForm1.DBGrid1ColEnter(Sender: TObject);
begin
if Self.DBGrid1.SelectedField.DataType = ftBoolean then
begin
Self.GridOriginalOptions := Self.DBGrid1.Options;
Self.DBGrid1.Options := Self.DBGrid1.Options - [dgEditing];
end;
end;
procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
if Self.DBGrid1.SelectedField.DataType = ftBoolean then
Self.DBGrid1.Options := Self.GridOriginalOptions;
end;
Even more, handle space key for toggling the checkbox
procedure TForm1.DBGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if ((Self.DBGrid1.SelectedField.DataType = ftBoolean) and (key = VK_SPACE)) then
begin
Self.DBGrid1.DataSource.DataSet.Edit;
Self.DBGrid1.SelectedField.Value:= not Self.DBGrid1.SelectedField.AsBoolean;
Self.DBGrid1.DataSource.DataSet.Post;
end;
end;
That's it!
If you're using the TClientDataset+TDatasetProvider+TDataset, you can manipulate the data array variant before it gets to the clientdataset and include an not updatable boolean field.
Once is done, all you need is to draw on the grid using the OnDrawColumnCell event. Here I doesn't used a CheckBox but just a bitmap (when user click it changes to selected/unselected).
Please excuse me for posting this as answer, I don't have the 50 reputation to add comments yet.
Mihai MATEI's answer is very close to the rare (as in really working) solution except for an use case where it bugs.
Whenever the users' first action on the grid is to click on the checkbox, the first click will work but the second will reveal the underlying DBGrid editor.
This happens because the "GridOriginalOptionsmechan" mechanism needs to be initialized.
To do so, just add the following code in the grid's OnEnter event:
procedure TForm1.DBGrid1Enter(Sender: TObject);
begin
DBGrid1ColEnter(Sender);
end;
That's it!
OK
I used this article for my problem. OK But the problem is that it wasn't working how it should. So I change my logic in the code. And implementing it by saving the selected rows from the dbgrid in a list.