I want to make something like that. I have a list in my StringGrid and i want to delete one row by selecting cell and then clicking the button. Then this list should show again in StringGrid without this row. The biggest problem i have with deleting row, i tried whis one procedure but it only deleted row in StringGrid, not in list, i think.
procedure DeleteRow(Grid: TStringGrid; ARow: Integer);
var
i: Integer;
begin
for i := ARow to Grid.RowCount - 2 do
Grid.Rows[i].Assign(Grid.Rows[i + 1]);
Grid.RowCount := Grid.RowCount - 1;
end;
Please someone for help. :)
If you're using a standard VCL TStringGrid (without using the live bindings made available in recent versions), you can use an interposer class to access the protected TCustomGrid.DeleteRow method.
The following code has been tested in Delphi 2007. It uses a simple TStringGrid dropped on a form, with the default columns and cells, and a standard TButton.
The TForm.OnCreate event handler simply populates the grid with some data to make it easier to see the deleted row. The button click event deletes row 1 from the stringgrid every time it's clicked.
Note: The code does no error checking to make sure that there are enough rows. This is a demo application and not an example of production code. Your actual code should check the number of rows available before attempting to delete one.
// Interposer class, named to indicate it's use
type
THackGrid=class(TCustomGrid);
// Populates stringgrid with test data for clarity
procedure TForm1.FormCreate(Sender: TObject);
var
i, j: Integer;
begin
for i := 1 to StringGrid1.ColCount - 1 do
StringGrid1.Cells[i, 0] := Format('Col %d', [i]);
for j := 1 to StringGrid1.RowCount - 1 do
begin
StringGrid1.Cells[0, j] := Format('Row #d', [j]);
for i := 1 to StringGrid1.ColCount - 1 do
begin
StringGrid1.Cells[i, j] := Format('C: %d R: %d', [i, j]);
end;
end;
end;
// Deletes row 1 from the stringgrid every time it's clicked
// See note above for info about lack of error checking code.
procedure TForm1.Button1Click(Sender: TObject);
begin
THackGrid(StringGrid1).DeleteRow(1);
end;
If you're using a more recent version, and have attached the data to the grid using live bindings, you can just delete the row from the underlying data and let the live bindings handle removing the row.
The selected row can be retrieved StringGrid1.selected and the you can call the following procedure.
procedure TUtils.DeleteRow(ARowIndex: Integer; AGrid: TStringGrid);
var
i, j: Integer;
begin
with AGrid do
begin
if (ARowIndex = RowCount) then
RowCount := RowCount - 1
else
begin
for i := ARowIndex to RowCount do
for j := 0 to ColumnCount do
Cells[j, i] := Cells[j, i + 1];
RowCount := RowCount - 1;
end;
end;
end;
Related
I am currently making "noughts and crosses" as homework. I generated a 10x10 array of TButton objects, but I don't know how they are called and how to control them:
Form1: TForm1;
pole: array[1 .. 10, 1 .. 10] of TButton;
h:TButton;
for i:=1 to 10 do
for j:=1 to 10 do
begin
h:=TButton.Create(Self);
h.Parent:=Self;
h.Width:=50;
h.Height:=50;
h.Left:=((i+1)*50)-100;
h.top:=((j+1)*50)-100;
h.OnClick := hClick;
end;
Are my buttons even in that array? I must say I am confused a bit here.
You have to assign every newly created button object to appropriate array entry.
Another important thing - inside common event handler you probably want to determine what button is pressed. Possible way - use object field Tag
for i:=1 to 10 do
for j:=1 to 10 do begin
h:=TButton.Create(Self);
pole[i, j] := h;
...
h.OnClick := hClick;
h.Tag := 10 * i + j; //store both row and column
end;
procedure ...hClick(Sender: TObject);
var
i, j: integer;
begin
i := (Sender as TButton).Tag div 10; // extract row and column
j := (Sender as TButton).Tag mod 10;
...
end;
At the end of for-loop add
pole[i][j] := h;
Because every iteration you just overwrite variable 'h' and nothing gets added into array.
I'm not sure how i would capture the row selected by a mouse click and then press a button to delete that selected row in a stringGrid in delphi.
procedure DeleteRow(Grid: TStringGrid; ARow: Integer);
var
i: Integer;
begin
for i := ARow to Grid.RowCount - 2 do
Grid.Rows[i].Assign(Grid.Rows[i + 1]);
Grid.RowCount := Grid.RowCount - 1;
end;
procedure TManageUsersForm.RemoveRowButtonClick(Sender: TObject);
var
Recordposition : integer;
begin
UserStringGrid.Options := UserStringGrid.Options + [goEditing];
UserStringGrid.Options := UserStringGrid.Options + [goRowSelect];
end;
So the first procedure is for deleting a row and the second makes sure when a user clicks a cell the whole row is highlighted not just that 1 cell.
The mouse click is the most important part!!
Thank You :)
The mouse click is not the most important part. Users can select a row either by keyboard or mouse, it doesn't matter, you'd just want to delete the current row. In the case of a mouse click, or otherwise, you can get the current row by Row.
procedure DeleteCurrentRow(Grid: TStringGrid);
var
i: Integer;
begin
for i := Grid.Row to Grid.RowCount - 2 do
Grid.Rows[i].Assign(Grid.Rows[i + 1]);
Grid.RowCount := Grid.RowCount - 1;
end;
Call it like;
DeleteCurrentRow(UserStringGrid);
I imagine the problem you might be having is to work out which grid row the user has clicked on. One way is:
procedure TForm1.StringGrid1Click(Sender: TObject);
var
StringGrid : TStringGrid;
Row : Integer;
GridRect : TGridRect;
begin
// The Sender argument to StringGrid1Click is actually the StringGrid itself,
// and the following "as" cast lets you assign it to the StringGrid local variable
// in a "type-safe" way, and access its properties and methods via the temporary variable
StringGrid := Sender as TStringGrid;
// Now we can retrieve the use selection
GridRect := StringGrid.Selection;
// and hence the related GridRect
// btw, the value returned for Row automatically takes account of
// the number of FixedRows, if any, of the grid
Row := GridRect.Top;
Caption := IntToStr(Row);
{ ...}
end;
See the OLH about TGridRect.
Hopefully the above will be sufficient to get you going - you've obvious already got most of the way yourself. Or, you could try the method suggested in the other answer, which is a little more "direct" but this way might be a bit more instructive as a "how to". Your choice ...
I want to delete multiple selected records which I have displayed at TDBAdvGrid. A number of records are being selected by checking checkbox in front of them. After clicking on Delete button it triggers procedure as follows. It is returning values of selected rows id successfully only problem with deletion of all records. It deletes only first selected record.
procedure TForm5.Button3Click(Sender: TObject);
var
i,j,idn: Integer;
State: Boolean;
begin
j := 0;
for i := 1 to DBAdvGrid1.RowCount - 1 do
begin
if DBAdvGrid1.GetCheckBoxState(1,i,state) then
begin
if state then
begin
idn := StrToInt(DBAdvGrid1.Cells[6,i]);
UniQuery1.SQL.Text := 'Delete from userplays where id = :id';
UniQuery1.ParamByName('id').AsInteger := idn;
UniQuery1.ExecSQL;
end;
end;
end;
end;
It is deleting only first record in lineup. After deleting first record it breaks for loop and control goes back to TDBAdvGrid with updated data after delete.
Final code based on suggestion is as follows
j := 0;
DBAdvGrid1.BeginUpdate;
for i := 1 to DBAdvGrid1.RowCount - 1 do
begin
showmessage('inside rowcount loop');
if DBAdvGrid1.GetCheckBoxState(1,i,state) then
begin
if state then
begin
DBAdvGrid1.DataSource.DataSet.First;
DBAdvGrid1.DataSource.DataSet.MoveBy(i - 1 - j);
DBAdvGrid1.DataSource.DataSet.Delete;
j := j+1;
continue;
end;
end;
end;
This code works perfectly only when PageMode := False
Based on the suggestion above, I would like to make another suggestion...
//DBAdvGrid1.BeginUpdate;
query.First;
for i := 1 to DBAdvGrid1.RowCount - 1 do
begin
if DBAdvGrid1.GetCheckBoxState(1,i,state) then
begin
if state then
begin
//you can do whatever you want with the current selected record
end;
end;
query.Next;
end;
I think this is easier then using the MoveBy...
I even never changed PageMode, it's still on true. No idea if it's coincidence or not.
You can use cyBookmarks in the OnCheck event , with Virtual Table.
It creates a list of all records selected, and you can list them with
cyBookmarks.bookmarklits.count, so my example is like this :
procedure TForm2.cyDBGrid1CheckBoxClick(Sender: TObject);
begin
if cyBookmarks1.AddCurrentRecord then
begin
cyBookmarks1.BookmarkList.InsertBookmark(cyDBGrid1.CheckedList.CurrentRecord);
cyDBGrid1.Invalidate ;
end
else
begin
cyBookmarks1.RemoveCurrentRecord;
cyDBGrid1.Invalidate;
end;
if cyBookmarks1.BookmarkList.Count>0 then
begin
VirtualTable1.GotoBookmark(cyBookmarks1.BookmarkList.Items[0]);
lbl1.Caption:=VirtualTable1.FieldByName('N_ENREGISTREMENT').AsString;
end;
end;
I face a issue with dynamically resizing the column width of a TJVListview in Delphi XE4 (in Windows 7 environment). Application takes longer time for column resize and sometimes throws access violation if there are huge data on the listview. We are using the below code for resizing the columns.
for i := 0 to LV.Columns.Count -1 do
begin
if LV.Columns.Items[i].Tag = 0 then
begin
LV.Columns.Items[i].Width := ColumnTextWidth;
LV.Columns.Items[i].Width := ColumnHeaderWidth;
end;
end;
Previously the same code used to work fine with Delphi 2009. The problem I noticed only when we are using customdrawitem event(Where we are placing images inside the listview). For the normal listview with only text display the above code is working fine.
I tried using the Column AutoSize property by setting it true, but it is of no use.
Any suggestion on how to overcome this issue. Actually, we are using the TJVlistview component in number of places in our application.
Regards,
Siran.
cODE :
1) In my form I have a JVListview, Button and imagelist. Button for loading into List view.
2) in Advancecustomdrawitem, I try to place a BMP control and also perform alternative row color change...
procedure TForm1.Button1Click(Sender: TObject);
var
i, ii: Integer;
ListItem: TListItem;
strVal : String;
begin
strVal := 'Test String';
try
ListView.Items.BeginUpdate;
LockWindowUpdate(listview.handle);
try
ListView.Clear;
for i := 1 to 15 do
begin
ListItem := ListView.Items.Add;
ListItem.SubItems.Add(strVal +'_' +IntToStr(i));
ListItem.SubItems.Add(strVal +'_' +IntToStr(i));
ListItem.SubItems.Add(strVal +'_' +IntToStr(i));
ListItem.SubItems.Add(strVal +'_' +IntToStr(i));
ListItem.SubItems.Add(strVal +'_' +IntToStr(i));
end;
finally
// for resizing the columns based on the text size
FitToTextWidth(ListView);
ListView.Items.EndUpdate;
LockWindowUpdate(0);
end;
except
on E: Exception do
MessageDlg(PWideChar(E.Message), TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
end;
end;
procedure TForm1.FitToTextWidth(LV: TListView);
var
i : integer;
begin
// Set the Column width based on based on textwidth and headerwidth
for i := 0 to LV.Columns.Count -1 do
begin
if LV.Columns.Items[i].Tag = 0 then
begin
LV.Columns.Items[i].Width := ColumnTextWidth;
LV.Columns.Items[i].Width := ColumnHeaderWidth;
end;
end;
end;
procedure TForm1.LISTVIEWAdvancedCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
Var
R : TRect;
C : TCanvas;
B : TBitMap;
begin
// Set C
C := (Sender as TListView).Canvas;
// Set R
R := Item.DisplayRect(drLabel);
B := TBitMap.Create;
B.Transparent := True;
B.TransparentColor := clWhite;
// based on item index set the image and change the row color
if odd(item.Index) = true then
begin
ImageList.GetBitmap(0,B);
TJvListItem( Item ).Brush.Color := clWhite;
TJvListItem( Item ).Font.Color := clBlack;
end
else
begin
ImageList.GetBitmap(1,B);
TJvListItem( Item ).Brush.Color := clMoneyGreen;
TJvListItem( Item ).Font.Color := clBlack;
end;
C.Draw(R.Left + 5 ,R.Top, B);
B.Free;
end;
The above code works well with Delphi 2009... but currently trying migrating to XE4 in Win 7 environment.. my problem here is, it takes lot of time in loading the list view (When performing column resizing dynamically by calling FitToTextWidth method) .. but without this method it is working fine but without column resizing...
When you set the width of a column to any one of the automatic constants, the control have to evaluate the length of the items/subitems to be able to calculate the necessary width. This takes time.
Also, when you set the width of a column, the VCL ListView updates all columns.
You have six columns, setting the width of any one of them involves 6 column updates, together with the spurious call in your FitToTextWidth procedure, your code is causing reading all items/subitems of a column 42 times (due to the code path in VCL: 1 time for 1st col, 2 times for 2nd -> 21 times for setting the width of 6 columns). Enclose your width setting in Begin/EndUpdate calls and remove the extra call, and you'll finish it in 6 rounds.
procedure TForm1.FitToTextWidth(LV: TListView);
var
i : integer;
begin
// Set the Column width based on based on textwidth and headerwidth
LV.Columns.BeginUpdate;
try
for i := 0 to LV.Columns.Count -1 do
begin
if LV.Columns.Items[i].Tag = 0 then
begin
// LV.Columns.Items[i].Width := ColumnTextWidth;
LV.Columns.Items[i].Width := ColumnHeaderWidth;
end;
end;
finally
LV.Columns.EndUpdate;
end;
end;
As I don't get any AV with your test case, I cannot comment on that.
I am developing a program and I have a StringGrid on it; when a particular button is pressed, my program saves the stringgtid into c:\myfolder\tab9.txt. I would like to put a progress bar that indicate how many time remains at the end of the saving process because sometime the grid has a lot of rows and it could take some time. I am using this code:
procedure SaveSG(StringGrid:TStringGrid; const FileName:TFileName);
var
f: TextFile;
i,k: Integer;
begin
AssignFile(f, FileName);
Rewrite(f);
with StringGrid do
begin
Writeln(f, ColCount); // Write number of Columns
Writeln(f, RowCount); // Write number of Rows
for i := 0 to ColCount - 1 do // loop through cells of the StringGrid
for k := 0 to RowCount - 1 do
Writeln(F, Cells[i, k]);
end;
CloseFile(F);
end;
I call the procedure in this way: SaveSG(StringGrid1,'c:\myfolder\myfile.txt');. My problem is that I don't understand how to do a progress bar that indicate the progress of the saving. At the moment I've only declared ProgressBar1.Position:=0 and ProgressBar1.Max:=FileSize. Do you have any suggestions?
How many cells are we talking about? Your main bottleneck is that you're writing to file for each cell, instead doing buffered writing.
I suggest that you fill TStringList with data from TStringGrid, and use TStringList.SaveToFile() method.
I've tested following procedure on StringGrid with 10,000,000 cells (10,000 rows x 1,000 columns), and it saves data to disk in less than one second:
procedure SaveStringGrid(const AStringGrid: TStringGrid; const AFilename: TFileName);
var
sl : TStringList;
C1, C2: Integer;
begin
sl := TStringList.Create;
try
sl.Add(IntToStr(AStringGrid.ColCount));
sl.Add(IntToStr(AStringGrid.RowCount));
for C1 := 0 to AStringGrid.ColCount - 1 do
for C2 := 0 to AStringGrid.RowCount - 1 do
sl.Add(AStringGrid.Cells[C1, C2]);
sl.SaveToFile(AFilename);
finally
sl.Free;
end;
end;