DGRid showing Selected but no Bookmark - delphi

Just found a weirdness with D5 and/or Zeos. I have a TDBgrid and DataSet.
I have a "Delete" function to delete selected Rows of the DBGrid.
If I Ctlr-Left-click to select Row (or Rows) it all works fine, but...
if I use the Vertical Scrollbar slider to move to the top of the DataSet then it shows the first Row in the select color.
If do not click the top line, (it already looks selected) then click "Delete" it crashes. Stepping through the code, at the first line SelectedRows.Count displays "1" but then at the Bookmark line I get a "Bookmark not found error" If I first click on the already selected Row after using the slider, then this does not crash.
for i:=0 to dbgridAddr.SelectedRows.Count-1 do
begin
dbgridAddr.DataSource.DataSet.GotoBookmark(pointer(dbgridAddr.SelectedRows.Items[i]));
How can I fix this ?
Thanks

A bit late to answer I guess, but maybe someone else can find it useful. The problem is that as you click within the boundaries of a DbGrid, a row shows up as selected even though it is not .
Add this to the OnDrawColumnCell event handler of your DBGrid:
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
// Only highlight the row if it is actually selected
if TDBGrid(Sender).SelectedRows.CurrentRowSelected then
begin
TDBGrid(Sender).Canvas.Brush.Color := clHighlight;
TDBGrid(Sender).Canvas.Font.Style := Font.Style + [fsBold];
TDBGrid(Sender).Canvas.Font.Color := clHighlightText;
end
else
begin
TDBGrid(Sender).Canvas.Brush.Color := clBtnFace;
TDBGrid(Sender).Canvas.Font.Style := Font.Style - [fsBold];
TDBGrid(Sender).Canvas.Font.Color := clWindowText;
end;
// Commit drawing
TDBGrid(Sender).DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
Add a block like this if you want to be able to see the cursor position in the grid when using the arrow keys
else if (gdSelected in State) then
begin
TDBGrid(Sender).Canvas.Brush.Color := clBtnFace;
TDBGrid(Sender).Canvas.Font.Style := Font.Style + [fsBold];
TDBGrid(Sender).Canvas.Font.Color := clWindowText;
end

Delphi 5:
You wrote:
if I use the Vertical Scrollbar slider to move to the top of the DataSet then it shows the first Row in the select color.
Even if it looks that way. This does not mean that it is also evaluated as marked.
You can test it with:
if dbgridAddr.SelectedRows.CurrentRowSelected then
ShowMessage('selected') else
ShowMessage('NOT selected');
Description
Position markers are not automatically removed from the list when records are deleted from the dataset.
It follows that some position markers may be invalid.
dbgridAddr.SelectedRows.Refresh;
Refresh to make sure that the list contains only valid entries.
Refresh trying to locate all records in the dataset that appear as a position marker in the Items property array.
All position marker for which there is no equivalent in the amount of data will be deleted.
If the position marker is invalid , Refresh returns a value of True and makes the DBGrid-object invalid so that a redraw is triggered, in which case all invalid records are deleted.
If all the position marker in the list should be valid, Refresh returns a value of False.
Never delete items ​​from lists with this construction.
for i:=0 to whatEver.Count-1 do
whatEver.delete(i);
e.g. Consider a list with 10 items on.
whatEver = 0 .. 9
If we now in the "for loop" with i = 1 do whatEver.delete(i);,
With this we delete the second item.
Now whatEver is only 0 .. 8 .
The loop is initialized to run through 0 to 9. Now you can understand if i = 9 then with whatEver.delete(i); attempts to access whatEver outside of 0 .. 8
This leads to errors.
Another example.
You delete incorrect records.
We will assume.
Before the lopp you've identified the following items (data record) - number to delete.
#3 , #6 , #8
for i:=0 to whatEver.Count-1 do
if (i=3) OR (i=6) OR (i=8) then whatEver.delete(i);
if i=3 then following is executed whatEver.delete(3);
afterwards
row 4 is now row 3
row 5 is now row 4
row 6 is now row 5
and so on
now with
if... OR (i=6) then whatEver.delete(6);
you delete the wrong item, the previous row 7 !
Also the bookmark pointer refers to #6, without refresh.
Please don't post now it is better to compare the content.
for i:=0 to whatEver.Count-1 do
if (whatEver[i] = 'blue') OR (whatEver[i] = 'green') OR (whatEver[i] = 'red')
then whatEver.delete(i);
I know that, it's just an example to show what happens when you delete items from the sequence out.
Do it better this way:
for i:= whatEver.Count-1 downto 0 do
whatEver.delete(i);
So that it is started from the end with the delete. Lower items keep their validity.
Edit:
I don't know what event of DBGrid you are using?
To test that there is no selection, only with a scroll.
Close your App und restart, do nothing only scroll the DBGrid.
Do not click into the DBGrid !!
Then press your testbutton.
procedure TForm1.Button2Click(Sender: TObject);
begin
if DBGrid1.SelectedRows.CurrentRowSelected then
begin
ShowMessage('selected');
end else begin
if DBGrid1.SelectedRows.Count>0 then
ShowMessage(' CurrentRow NOT selected'+#13#10+
'A part in your code must have select another row.')
else
ShowMessage('NOT selected');
end;
end;

Related

How to select and then delete cell in TStringGrid in Delphi?

I know how to enter data into grid, but I need to make also delete button for selected rows. I've been looking threw internet, found almost nothing. how to delete thing, i just cant find anywere how to select specific cell and then, how to edit/delete or do anything with her.
Here is example what I mean by that:
I select by mouse or anything 1 cell, then it selects (for example) whole row in which this cell was. After that I click button delete. How to tell delete function which row was selected.
Found an anwser:
Create procedure:
procedure StringGridDeleteRow( AStringGrid:TStringGrid; ARow:integer );
var
nRow:integer;
begin
with AStringGrid do
begin
for nRow := ARow to RowCount - 2 do
Rows[nRow].Assign(Rows[nRow+1]);
Rows[RowCount-1].Clear;
RowCount := RowCount - 1
end;
end;
and then add this line to your button:
StringGridDeleteRow(StringGrid1, StringGrid1.Row);
It was easier than I thought!

Delphi: Bind a Chart with a DBGrid

I want to bind a Chart with a DBGrid.
Refer to the exhibit.
When I click in the DBGrid on number 3 (X-Axis), then the bar at position three should be highlighted(or a pointer to bar 3).
When I click on number 4 in the Grid then bar 4 is highlighted etc.
I'm using a TDBChart
Is there a way to do this?
Not knowing what the charting component is, cannot provide a working example, but the key which you are looking for is to use the AfterScroll event for the dataset in the grid. Since each row represents a different record, that event is fired when the grid selection moves to the row.
Edit: This doesn't highlight with a box, but does change the color of the value marks at the top of each bar. Hopefully this gets you on your way. MyQuery is what is feeding the datasource
var
savecolor : tcolor;
procedure MyForm.FormShow(Sender:TObject);
begin
...
SaveColor := dbchart1.series[0].marks.items[0].color;
...
end;
procedure MyForm.MyQueryBeforeScroll(DataSet : TDataSet);
begin
dbchart1.series[0].marks.items[MyQuery.recno-1].color := SaveColor;
end;
procedure MyForm.MyQueryAfterScroll(DataSet:TDataSet);
begin
dbchart1.series[0].marks.items[MyQuery.recno-1].color := clRed;
end;

Focus cells in reverse order for any utility don't support RTL [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Focus cells in reverse order in DBGRID
Some times I get some components that descendant from DBGRID, for some reason it is not support RTL, that guide me to think of change the behavior of tab key to appear that is support RTL, the only thing I need for that to change the direction of tab key.
I am sure that my question will help any user needing RTL (Like Hebrew and Arabic) for using some components that don't support RTL.
That Leading me to ask: how to change the keystroke action when keys are pressed to reverse action of the Tab key as follows:
Tab key ---> Shift Tab;
Shift Tab ---> Tab key;
The following code, without an OnKeyUp handler, does what you seem to want
type
TMyDBGrid = class(TDBGrid);
procedure TForm1.DBGrid1KeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
begin
if Key = VK_Tab then begin
Key := 0;
if ssShift in Shift then
DBGrid1.SelectedIndex := DBGrid1.SelectedIndex + 1
else begin
if TMyDBGrid(DBGrid1).Col = 1 then begin
// The following goes to the rightmost cell in the next row
// if the focus is already on the leftmost column, as specified
// in the original version of the q
DBGrid1.DataSource.DataSet.Next;
TMyDbGrid(DBGrid1).Col := DBGrid1.Columns.Count;
end
else
DBGrid1.SelectedIndex := DBGrid1.SelectedIndex - 1;
end;
end;
end;
Update In a comment to this, you asked for a fuller implementation. Here it is:
procedure TForm1.DBGrid1KeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
procedure TabForwards;
begin
if TMyDBGrid(DBGrid1).Col = DBGrid1.Columns.Count then begin
DBGrid1.DataSource.DataSet.Next;
if DBGrid1.DataSource.DataSet.Eof then
DBGrid1.DataSource.DataSet.Append;
TMyDbGrid(DBGrid1).Col := 1;
end
else
DBGrid1.SelectedIndex := DBGrid1.SelectedIndex + 1;
end;
procedure TabBackwards;
begin
if DBGrid1.DataSource.DataSet.State = dsInsert then begin
DBGrid1.DataSource.DataSet.Cancel;
Exit;
end;
if TMyDBGrid(DBGrid1).Row = 1 then begin
if TMyDBGrid(DBGrid1).Col = 1 then
TMyDBGrid(DBGrid1).Col := DBGrid1.Columns.Count
else
DBGrid1.SelectedIndex := DBGrid1.SelectedIndex - 1;
end
else begin
if TMyDBGrid(DBGrid1).Col = 1 then begin
DBGrid1.DataSource.DataSet.Prior;
TMyDbGrid(DBGrid1).Col := DBGrid1.Columns.Count;
end
else
DBGrid1.SelectedIndex := DBGrid1.SelectedIndex - 1;
end;
end;
begin
Caption := IntToStr(TMyDbGrid(DBGrid1).RowCount);
if cbNormal.Checked then
Exit;
if Key = VK_Tab then begin
Key := 0;
if ssShift in Shift then
TabForwards
else
TabBackwards;
end;
end;
Notice that this uses TabForwards and TabBackwards sub-procedures and effectively reverses the roles of Tab and Shift Tab. The reason for this is because it separates thinking, and talking, about the movement behaviour from the key combination which produces it, which I found easier easier to code and to describe what the movement behaviour actually is.
With Tab and Shift Tab behaving as standard, this is how the DBGrid behaves:
Tabbing forwards
If the cursor is in the New Record row, focus move to the next cell in the row until it reaches the RH column, then it wraps to the first column.
Otherwise, focus moves to the next cell in the current row until it reaches the RH column, then it wraps to the first column of the next row, if there is one, otherwise it calls Append on the grid's Dataset, and moves to the first column of the New Record row.
In the New Record row, pressing the Tab Backwards key abandons the new record.
Tabbing backwards
If the cursor is in the New Record row, the new record is abandoned. Otherwise, focus moves to the prior cell in the current row until it reaches the LH column, then it wraps to the RH column of the prior row. Once it reaches the LH column of the first row, it wraps to the RH column in that row.
That is what the second example does.
This does not, however, do what you said in your comment
when I reach last cell in DBgrid, the tab key (not shift-Tab) need to open new record.
and this is why:
Your way, the behaviour of the grid in response to the Tab key is different in the last cell compared with every other one. In the other cells, Tab will move left and wrap upwards to the prior row, whereas in the last cell it will move downwards. That would puzzle me as a user. Anyway, if that's really what you want, you can rearrange the second example to behave that way.
When you use Tab and Shift Tab in the normal way, the behaviour is as follows:

Delphi, Show button in a different TGridPanelLayout cell

Hi I am working with XE6 and I am using a TGridPanelLayout with 4 col and 4 rows. On the first cell I am displaying a Button. What I would like to do is, that when I click on this Button, get that Button to appear in a different cell. But I cannot find how to do it, so far I tried this, but nothing happens.
procedure TForm4.Button1Click(Sender: TObject);
begin
GridMyPannel.ControlCollection.BeginUpdate;
GridMyPannel.ControlCollection.AddControl(Button1, 2, 2);
Button1.Parent := GridMyPannel;
end;
I am really new on Delphi. Could anyone give me an example of how I could do it?
A TGridPanel has a ControlCollection property which allows access to the Row and Column properties that also appear on your TButton once you've placed in inside your TGridpanel. A TButton (or rather its superclass TControl) does not have a Row or Column property. So we need to get a grip of the TControlItem wrapper the TGridpanel uses.
procedure TForm8.Button1Click(Sender: TObject);
var
selectedControl: TControl;
itemIndex: Integer;
selectedControlItem: TControlItem; // This knows about "Row" and "Column"
begin
// This is the button we've clicked
selectedControl := Sender as TControl;
itemIndex := GridPanel1.ControlCollection.IndexOf(selectedControl);
if (itemIndex <> -1) then begin
selectedControlItem := GridPanel1.ControlCollection.Items[itemIndex];
selectedControlItem.Row := Random(GridPanel1.RowCollection.Count);
selectedControlItem.Column := Random(GridPanel1.ColumnCollection.Count);
end;
end;
The above code finds the button and changes its Row and Column property to a random value. Note that you didn't specify whether the TButton is the only control within the TGridpanel. Is that the case?
I did the below in normal VCL and XE3 and with a TGridPanel (no TGridPanelLayout in my Delphi).
The problem with the GridPanel is that it does not allow controls (Buttons, etc) to be placed in any cell (like Cell:1,1) without having controls in the cells before that cell. GridPanel always fills itself from Index 0 upwards.
So the trick is to fool it. Now depending on whether you already have other cells in the GridPanel you will have to make place for the button to go to and also put something else in its place if the button was in a cell of lower index.
Have a look at the form before the button is pressed:
Note that I have not created a ControlItem at cell 1,0 yet.
I want to move Button 1 to cell 1,0. I cannot do that unless I first place something else in its place (cell 0,0). I have to create a new ControlItem at cell 1,0 to house button1.
procedure TForm1.Button1Click(Sender: TObject);
begin
// Places CheckBox1 in the same cell as BUtton1
GridPanel1.ControlCollection.ControlItems[0,0].Control := CheckBox1;
// Create a new ControlItem for Button1 and in the same breath move
// Button1 to it
GridPanel1.ControlCollection.AddControl(Button1,1,0);
// You know what this does. :)
CheckBox1.Parent := GridPanel1;
end;
The result:

hyperlink field in DBGRID

I'm doing a small internal software to search branch lines in my company. In addition to the branch lines I also put an e-mail field in the database as shown below:
My intention is to click on the registered e-mail and the software via the ShellExecute open a window to send the e-mail. I'm using the option dgRowSelect as TRUE and because of that the OnCellClick event does not correctly identify which cell was clicked.
In my searches have not found any way to do this. Then I thought of using a TLabel within the field. I can use the onclick event in the TLabel and also change the cursor icon.
If TLabel is a good solution, how can I add a TLabel in DBGrid?
Or what would be another good solution?
I'm guessing that purpose of dgRowSelect=true is for highlighting whole selected row.
TLabel is not the way I would go - I would set dgRowSelect=false and paint selected row in OnDrawColumnCell or create my own fixed dbgrid.
There was a similar question:
how can i colour whole row in DBGrid with rowselect turned off?
Anyway if you want to use dgRowSelect=true and get valid info about clicked cell, here it is:
type THackDBGrid=class(TDBGrid);
procedure TForm1.dbgrd1CellClick(Column: TColumn);
var p:TPoint;
col:TGridCoord;
i: Integer;
grid:THackDBGrid;
begin
p := Mouse.CursorPos;
grid := THackDBGrid(Column.Grid);
p := grid.ScreenToClient(p);
col := grid.MouseCoord(p.X,p.Y);
i := grid.RawToDataColumn(col.X);
Label1.Caption := 'Column index: ' + IntToStr(i);
end;

Resources