I use this code to save current selected record after refresh the dataset
bm := table1.GetBookmark;
try
table1.Refresh;
table1.GotoBookmark(bm);
finally
table1.FreeBookmark(bm);
end;
But if I select a record at the middle of the grid, after executing this code it still be current selected record but it jump or make the grid scroll to be the first record in the current view. How I can keep current position ?
Remember current focused row and then set it after refresh.
Here is example for C# code, but I think, for deplhi it's the same:
var row = _gridView.FocusedRowHandle;
_gridView.RefreshData();
_gridView.SelectRow(row);
Related
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;
Is it possible to get the row number that is displayed from the underlying dataset in the top-most row of a DBGrid, without that top-most row being the currently selected row, when the number of records in the underlying dataset is greater than the number of rows displayed in the DBGrid, and the DBGrid has been scrolled.
Here's my problem. From a drag/drop event handler attached to the DBGrid I can determine which visible row of the DBGrid the drop event is associated with using MyGrid.MouseCoord(X,Y).Y. When the underlying dataset contains less than or the same number of records as the number of rows displayed in the DBGrid, this value is also the row number of the associated record in the underlying dataset.
When the underlying dataset contains more records than the number of visible rows in the DBGrid, MyGrid.MouseCoord(X, Y).Y and TDataSet(MyGrid.DataSource.DataSet).RecNo are the same only when the first row of the dataset appears on the first row of the grid.
Is there some way to identify the record number in the underlying dataset (or the offset) of the top-most displayed record in a DBGrid without that DBGrid row being selected? I know that if I actually select the top-most row of the DBGrid then I can use TDataSet(MyGrid.DataSource.DataSet).RecNo to get the current record number of the underlying dataset. However, from DBGrid.OnDragOver or DBGrid.OnDragDrop events I only have a reference to the DBGrid and the mouse coordinates (from which I can determine which row of the grid was the target of the drop).
For example, if I can determine that the DBGrid is displaying the third record in the underlying dataset in the top-most row of the grid, my problem is solved. Likewise, if I can read an underlying TField of a particular row (say, the top-most row) without that row being select, I have what I need. However, I cannot see a way to do this.
Any suggestions will be greatly appreciated.
Edit: I previously posted a blog about dragging and dropping into a DBGrid. With this new info, I can solve a previously known problem. I will be updating that blog sometime this week, and will add a link to that blog here once I have done so.
There is an additional issue. When the number of visible rows is less than the number of underlying records, we need to also accomdate calculating the drop when it occurs after the last visible row. When dropping after the last visible row, MouseCoord(x,y).Y returns -1.
Here is a modification to Uwe's code that accomplishes that end:
function TDBGridHelper.RecNoFromVisibleRow(Value: Integer): Integer;
begin
if Value = -1 then
begin
Result := DataSource.DataSet.RecNo - Row + TopRow + VisibleRowCount
end
else
begin
Result := DataSource.DataSet.RecNo - Row + TopRow + Value;
if dgTitles in Options then
Dec(Result);
end;
end;
Edit: As I mentioned in the original question, I was interested in this answer in order to fix a behavior in my code that implements drag and drop into a DBGRid. With this answer I have updated my drag and drop behavior, and I have written about this update in my blog. You can find this discussion, including links to the original blog post, at the following URL: Dragging and Dropping into a DBGrid Revisited
As long as there is only an offset between DataSet.RecNo and the visible rownumber in the grid, you might get the required information from the protected members Row and TopRow, which can be accessed by a class helper. Something like this:
function TDbGridHelper.RecNoFromVisibleRow(Value: Integer): Integer;
begin
Result := DataSource.DataSet.RecNo - Row + TopRow + Value;
if dgTitles in Options then
Dec(Result);
end;
In the code below, we do some operations (not deletions) on some selected rows.
However, sometimes, when finished, the top selected row has scrolled so that it is displayed 1/2 way down the grid. Is there a way to avoid this scrolling? (If my code to traverse the selected row below is improper for some unrelated reason, I welcome corrections.)
Function TForm.DoSomethingToSelectedRows;
var
KeyAtStart: Integer;
begin
Result := TRUE;
KeyAtStart := DataSet.FieldByName('Key').AsInteger;
DataSet.DisableControls;
DataSet.First;
try
while Result AND (NOT DataSet.EOF) do DataSet
begin
if DBGrid1.SelectedRows.CurrentRowSelected then
Result := ... do something ...
fMPODataTls.GetDS.Next;
end;
finally
DataSet.Locate('Key', KeyAtStart, []); // re-position where we started
DataSet.EnableControls;
end;
end;
Before looping through the dataset, you can keep note of the top row that the grid is displaying and the number of total records that is displayed. With this information, after you relocate the record, you can position the record to the exact row by moving either to the top or to the bottom and then moving back again.
You can carry out the moving by MoveBys. Unfortunately the Row and RowCount properties of TDBGrid is protected, for that you have to use what is widely known as 'protected hack'.
There's code example in this answer, together with checking bookmarks, so that you don't end up on a wrong record.
There is no guarantee that the current record in your grid is exactly positioned at the center of the visible rows, but doing a Locate when it has scrolled out of sight will make it (the new current record) the middle row of the grid, hence the apparent scrolling.
If you want to re-position the grid as it was, you have to memorize which record is at the top row.
If you are using a TClientDataset, you don't have to mess with the user's cursor, you can clone it and do your edits there.
If I have DBGrid1 linked to ClientDataSet1, and I have the cursor on row 5 of the DBGrid (and the ClientDataSet) and within view of the the first row, and delete the first row using a cloned cursor like below, I'd see the first row disappear and my selected row would go up by one row height.
Function TForm.DeleteFirstRow;
var
myCDS: TClientDataSet;
begin
myCDS := myCDS.Create(nil);
try
myCDS.CloneCursor(ClientDataSet1, False);
myCDS.First;
myCDS.Delete;
finally
myCDS.Free;
end;
If I was scrolled all the way down to row 500 or so, out of view of the first row, and executed it again, the selected row would not move at all in the grid.
I'm making a simple program for use in automotive parts shop. Here's how it's supposed to look: Link
The problem is the small window on the left. It should be opened when double clicked on any of the rows in DBGrid in the main window, and it should show all of the selected item's characteristics in DBEdit fields. If the Save button is clicked it should save the changes from the DBEdit fields into the database, but otherwise it should just ignore the changes.
I succeeded opening another form by double click on a field in DBGrid by using this code:
procedure TForm1.DBGrid1DblClick(Sender: TObject);
begin
if not Assigned(Form2)
then Form2 := TForm2.Create(Application);
Form2.Show;
end;
The only problem now is how to get the program to detect which row in the DBGrid is selected and then show its contents in DBEdit fields in the smaller window.
Can anyone tell me how to do this, please?
Thanks!
Just add the TDBEdits to TForm2, and connect them to the same DataSource as the DBGrid is using. They will automatically show the contents of the same row that is selected in the DBGrid, and you can edit or insert into the DataSource's DataSet and have the new or changed data appear in the DBGrid automatically.
There are many ways to achieve this. I'll describe two:
If you link the DBEdits in the little window to the same DataSource as the DBGrid's, then you are all set
OR
You can pass whatever info you want inside DBGrid1DblClick from Form1 to Form2. This option, by itself, has many possibilities.
Update
For the DataSource to be visible in Form2, unit to make Form2's unit use Form1's unit.
I got a dbGrid with X rows.
I want to update a field-value in the second row with a timer (for example, show a countdown).
Thats no problem, but I want to be able to change the selected row and keep updating the second row.
When the selection changes in the grid, the current record of the connected dataset changes as well, and thats a problem because the code in the timer points to the selected record.
How could that be solved?
Thanks!
If dataset connected to dbGrid is TClientDataSet, you can drop another TClientDataSet and clone data from grid's dataset.
Since both datasets will point to same data, you can change values in cloned dataset, and that data will show in dbGrid without tampering with dbGrids dataset.
Try this very straightforward approach:
if DataSource1.DataSet.State in dsEditModes then
DataSource1.DataSet.Post; { or Cancel, depends on your needs }
try
DataSource1.DisableControls;
Bookmark := ClientDataSet1.GetBookmark;
try
if ClientDataSet1.Locate(SecondRowId, 'Id', []) then
begin
ClientDataSet1.Edit;
ClientDataSet1['Counter'] := Counter;
ClientDataSet1.Post;
end;
ClientDataSet1.GotoBookmark(Bookmark);
finally
CLientDataSet1.FreeBookmark(Bookmark);
end;
finally
DataSource1.EnableControls;
end;