How to select and then delete cell in TStringGrid in Delphi? - 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!

Related

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:

Selecting a row in TStringGrid from click on any cell within that row.

I have a TStringGrid object on a form which has 1 FixedCol and 1 FixedRow. I want to be able to select an entire row on the object when the user clicks on any cell within that row. This selection must also be visible to the user (I want the row to change colour).
EDIT: Have put goRowSelect in options on the object. Is there now a way to select the row on the click of one of the cells in the fixed column?
In order highlight entire row when ever the user clicks on any cell in that row, set the following StringGrid properties.
In design time: Go to options property and check the following sub-properties.
goEditing := TRUE;
goRowSelect := TRUE;
To achieve this at run time,
StringGrid1.Options := StringGrid1.Options + [goEditing];
StringGrid1.Options := StringGrid1.Options + [goRowSelect];
If you enable the option goFixedColClick of the string grid you can then use the OnFixedCellClick event to determine which row to select. You can then set StringGrid1.Selection := TGridRect(Rect(0, Row, n, Row)); where Row is the row clicked and n is the width of your StringGrid.

DGRid showing Selected but no Bookmark

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;

How to avoid TDbgrid scrolling when returning to a previous location

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.

TListBox Drag and Drop problems with MultiSelect enabled

I have a TListBox with multiselect and ExtendedSelect both set to true. I need to be able to drag multiple items in the list box to re-arrange them. My problem is what happens when the user clicks on an item that is already selected without holding down the CTRL or SHIFT key.
Case 1: DragMode is set to dmManual
The selection is cleared before the mouse down. This will not allow multiple items to be dragged.
Case 2: DragMode is set to dmAutomatic
The MouseDown event never fires. The selection is not cleared so dragging is OK, but the user cannot clear the selection by clicking on one of the selected items. This really causes a problem if all the items are selected or the next item the user wants to select was part of the current selection.
Note that this problem only happens if you assign something to the DragObject in the OnStartDrag procedure. I think the problem would go away if OnStartDrag would only start after the user moves the mouse. I have Mouse.DragImmediate := false set but I still get the StartDrag fired as soon as I click on an item in the list box.
I am using Delphi 7 for this project but I see the same behavior in Delphi 2007.
I have played with this for a while. And observe the same effects.
I would use Case2 and add a (Select All/Deselect All) button to the list. It even adds extra functionality and solves the most annoying part of the problem.
Use Case 2 and when the TListBox.OnMouseUp event fires check to see if multiple items are selected and were dragged. If multiple items are selected, but weren't dragged, then deselect all items apart from the clicked item.
I would use this method because Windows Explorer works this way.
Bit of a kludge but this works. DragMode on the ListBox is set to dmAutomatic.
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
iDropIdx, i: Integer;
pDropPoint: TPoint;
slSelected: TStrings;
begin
{Which item is being dropped onto?}
pDropPoint := Point(X, Y);
iDropIdx := ListBox1.ItemAtPos(pDropPoint, False);
slSelected := TStringList.Create;
try
{Copy the selected items to another string list}
for i := 0 to Pred(ListBox1.Items.Count) do
begin
if (ListBox1.Selected[i]) then
slSelected.Append(ListBox1.Items[i]);
end;
{Find the selected items in the listbox and swap them with the drop target}
for i := 0 to Pred(slSelected.Count) do
begin
ListBox1.Items.Exchange(ListBox1.Items.IndexOf(slSelected[i]), iDropIdx);
inc(iDropIdx);
end;
finally
slSelected.Free;
end;
end;
I'm not sure why this makes a difference but if I change the DragObject to be a TDragControlObjectEx (instead of a TDragObjectEx) I get the behavior I am looking for. Drag mode is set to Automatic.
I tried to look and see what this was affecting but I could not figure it out.

Resources