Mega Code Archive

 
Categories / Delphi / Examples
 

Putting CheckBoxes into Grids

Title: Putting CheckBoxes into Grids Question: How do I add checkbox items to TGrids? Answer: If you have ever needed checkboxes inside a grid, and either haven't learned how to write custom components, or don't have the time to write one, here's a way to do it. Basically, what you will need to do it use the Objects property of the grid to store TCheckBox items. But you can store anything here, as long as it is a descendent of TObject. In a recent project I wrote, I needed a checkbox in the second column (index of 1) of a TStringGrid. All in all it's pretty simple, but you will have to make sure everything is handled properly behind the scenes. As stated in the Delphi documentation: "The string grid does not own the objects in the Objects array. Objects added to the Objects array still exist even if the string grid is destroyed. They must be explicitly destroyed by the application." In order to add a new checkbox (and a new row the grid), you can use code similar to the following: grdPreviousFiles.RowCount := grdPreviousFiles.RowCount + 1; RowToAdd := grdPreviousFiles.RowCount; grdPreviousFiles.Cells [0, RowToAdd] := IntToStr(RowToAdd); grdPreviousFiles.Objects[1, RowToAdd] := TCheckBox.Create(grdPreviousFiles); grdPreviousFiles.Cells [2, RowToAdd] := dlgOpenFile.FileName; grdPreviousFiles.Repaint(); This code creates a new checkbox in the new row, but does not change it's state. You will need something to tell you the dimensions of the checkmark within the box so everything looks OK. You may need to play around with the numbers until they look OK to you. Here's what I've come up with: const CHECKMARK_WIDTH = 13; CHECKMARK_HEIGHT = 13; These two constants can either be global to the form, local to the event handler, or you could even hardcode them in-line. The actual drawing of the checkboxes is handled in the OnDrawCell event handler for the grid: procedure TfrmMain.grdPreviousFilesDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var CheckBox : TCheckBox; DrawRect : TRect; DrawState : Integer; OldPenColor : TColor; OldFont : TFont; S : string; begin // Custom draw only the column that has the TCheckBox objects in it, and // only for the number of rows physically in the grid if (ACol = 1) and (ARow 0) and (ARow // Convert the object we're storing at the current location into a // CheckBox CheckBox := grdPreviousFiles.Objects[ACol, ARow] as TCheckBox; // Make sure there's a checkbox created for the cell before we try to // draw it in the grid if Assigned(CheckBox) then begin // Tells us what state to draw the CheckBox in case CheckBox.State of cbChecked : DrawState := DFCS_BUTTONCHECK or DFCS_CHECKED; cbUnchecked : DrawState := DFCS_BUTTONCHECK; else DrawState := DFCS_BUTTON3STATE or DFCS_CHECKED; end; // Determine the area we will use to draw the check mark and the // square around it DrawRect.Left := Rect.Left + (Rect.Right - Rect.Left - CHECKMARK_WIDTH) div 2; DrawRect.Top := Rect.Top + (Rect.Bottom - Rect.Top - CHECKMARK_HEIGHT) div 2; DrawRect.Right := DrawRect.Left + CHECKMARK_WIDTH; DrawRect.Bottom := DrawRect.Top + CHECKMARK_HEIGHT; // Keeps the current pen color so we can restore it after we draw // the square around the check mark OldPenColor := grdPreviousFiles.Canvas.Pen.Color; // Use the color defined by Windows - we don't want to 'assume' what // colors the user is using grdPreviousFiles.Canvas.Pen.Color := clBtnShadow; // This draws the basic square that will hold the check mark grdPreviousFiles.Canvas.Rectangle(DrawRect); // Restore the pen color back to it's original grdPreviousFiles.Canvas.Pen.Color := OldPenColor; // Draws the check mark - the DFCS_FLAT must be used, or the 3D style // will be used DrawFrameControl(grdPreviousFiles.Canvas.Handle, DrawRect, DFC_BUTTON, DrawState or DFCS_FLAT); end; end; // other code for OnDrawCell event goes here end; To take care of setting the Checked state of the checkbox, you can perform something similar to the below in the OnDblClick event handler: procedure TfrmMain.grdPreviousFilesDblClick(Sender: TObject); var CheckBox : TCheckBox; begin CheckBox := grdPreviousFiles.Objects[1, grdPreviousFiles.Row] as TCheckBox; if Assigned(CheckBox) then begin if (grdPreviousFiles.Cells[2, grdPreviousFiles.Row] '') and FileExists(grdPreviousFiles.Cells[2, grdPreviousFiles.Row]) then begin CheckBox.Checked := not CheckBox.Checked; grdPreviousFiles.Invalidate; end else MessageDlg('This file does not exist. It cannot be selected' + CR_LF + CR_LF + 'As a reminder, files in the list that do not exist' + CR_LF + 'are presented in boldface type', mtWarning, [mbOK], 0); end; end; The OnDrawCell event handler will take care of drawing the checkmark for you when it is called. When you are finished with the form that contains the grid with checkboxes, you should make sure you have properly disposed of them. You can do this in the OnClose event for the form: procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction); var Index : word; CheckBox : TCheckBox; begin for Index := 1 to grdPreviousFiles.RowCount do begin CheckBox := grdPreviousFiles.Objects[1, Index] as TCheckBox; CheckBox.Free(); end; end; --- If you wish to get a picture of what this looks like, please visit the following page: http://www.cimba.com/abovetherim/preview/preview5.html You should be able to use as many checkboxes in as many columns as you like, you just have to remember to manage of all of them yourself.