Grids Reference Page/pl

From Free Pascal wiki
Jump to: navigation, search

Deutsch (de) English (en) español (es) polski (pl) русский (ru)

Contents

Intencje

Ten tekst będzie próbą pokazania użytkownikowi niektórych aspektów komponentów grids w Lazarusie. Ma również służyć jako przewodnik dla użytkowników, którzy nigdy dotąd nie używali komponentów grids (doświadczeni użytkownicy zwykle potrzebują informacji na temat nowych funkcjonalności). Dlatego ten tekst będzie starał się osiągnąć następujące cele:

  1. Aby przybliżyć komponenty grids osobom, które nie miały lub miały bardzo mały kontakt z Delphi.
  2. Aby udokumentować różnice w porównaniu z Delphi komponentów grids.
  3. Aby udokumentować nowe funkcjonalności w komponentach grids Lazarusa.
  4. Spisać informacje i przykłady dla tych komponentów.

Wprowadzenie

Grid jest komponentem, który służy do wyświetlania prostych danych w formacie tabelarycznym. Łatwo zauważalną cechą charakterystyczną komponentów grids jest to, że składają się z komórek tworzących wiersze i kolumny, które nazywane są siatką (rzadziej kratką).

Tego typu informacji, które mogą być wyświetlane w siatce jest bardzo wiele i zależą przede wszystkim od tego, co użytkownik chce pokazać. Zazwyczaj na te informacje składają się tekst, kolory, obrazy lub kombinacje tych trzech elementów.

Biorąc pod uwagę jak różnorodne mogą być informacje, które można przedstawić, ta grupa gotowych komponentów grids ma na celu ułatwić użytkownikowi prezentację określonych rodzajów informacji.

Drzewo dziedziczenia

                      [TCustomControl]           
                              |                    
                              |                    
                         TCustomGrid               
                              |                    
                +-------------+------------+       
                |                          |       
          TCustomDrawGrid             TCustomDbGrid
                |                          |       
       +--------+--------+                 |       
       |                 |              TDbGrid    
   TDrawGrid      TCustomStringGrid                
                         |                         
                         |                         
                    TStringGrid                    

Pierwszy przykład

Jednym z celów tej strony jest pomoc ludziom, którzy słabo znają Lazarusa, dlatego zróbmy szybko wprowadzający przykład komponentu grids w działaniu. Niech będzie to tradycyjne „Witaj Świecie!” (ang. "Hello World") wykonane np. z komponentu TStringGrid.

  1. Tworzenie nowej aplikacji
    • Z głównego menu wybieramy: Projekt->Nowy projekt
    • W oknie dialogowym Create New Project wybierz Aplikacja i naciśnij "OK"
    • Pokaże się nowy, pusty formularz.
  2. Położenie komponentu grid na formularzu
    • Z palety komponentów wybierz zakładkę "Additional"
    • Kliknij na ikonę TStringGrid tstringgrid.png
    • Kliknij na formę, w pobliżu lewego górnego rogu. Pojawi się nowy pusty komponent grid.
  3. Położenie przycisku na formularzu
    • Z palety komponentów wybierz zakładkę "Standard"
    • Kliknij na ikonę TButton tbutton.png
    • Kliknij na pusty obszar formularza. Pojawi się nowy przycisk.
  4. Podwójnie kliknij na przycisk z kroku 3, aby zapisać następujący kod w procedurze obsługi przycisku:
    • StringGrid1.Cells [1,1]: = "Witaj Świecie!";
  5. Uruchom program, klikając ikonę "Uruchom" menu run.png
    • Po naciśnięciu przycisku Button1, tekst „Witaj Świecie!” powinien pojawić się w komórce; kolumna 1, wiersz 1.

Różnice pomiędzy komponentami grids Lazarusa i Delphi

Aktualnie komponenty grids wykazują kilka różnic w stosunku do komponentów w Delphi. Jest to spowodowane głównie tym, że komponenty grids Lazarusa były tworzone od podstaw, bez dążenia do pełnej kompatybilności.

Na późniejszym etapie, zgodność komponentów grids z Delphi stała się pożądana. Zaczęto je o wiele staranniej dostosowywać do tych z Delphi, ale nawet wówczas robiono to nie starając się na siłę dostosowywać każdą pojedynczą właściwość i metodę tak by była duplikatem z Delphi. Ponadto ich wewnętrzna budowa bardzo się różni, więc niektóre rzeczy nie są możliwe do wykonania w Lazarusie lub muszą być wykonywane w inny sposób. Komponenty grids będą ewoluować, więc lepsza zgodność też będzie osiągnięta, co z pewnością jest pożądane.

Różnice

Poniżej wymienione będą znane różnice (kolejność jest nieistotna)

  • Edytor komórek (Cell Editor)
  • Zachowanie podczas projektowania
  • Rysowanie komórek wykazuje pewne różnice, zobacz w sekcji o dostosowywaniu komponentów grids.

Nowe funkcje

  • Kolumny
  • Zdarzenia
  • Edytor siatki (Grid Editor)

Korekty w celu poprawy zgodności z Delphi

Oto lista właściwości i korekt, które mogą być wykonane w celu dostosowania wyglądu lub zachowania komponentów grids Lazarusa do tych z Delphi. Korekty te odnoszą się do nowo utworzonej siatki. Wpisy z etykietą [Kod] należy wykonać w kodzie, a z etykietą [Projekt] można zmieniać w czasie projektowania.

  • [Projekt] TitleStyle := tsStandard;
  • [Projekt] DefaultRowHeight := 24;
  • [Kod] EditorBorderStyle := bsNone; //to może działać tylko w Windows
  • [Kod] UseXORFeatures := true;
  • [Kod] AllowOutBoundEvents := False; //rewizja SVN r10992 lub dalsza
  • [Kod] FastEditing := False; //wspierane w dbgrid, StringGrid rew. r10992 lub dalsza
  • [Projekt] AutoAdvance := aaNone;

Powiązania komponentów grids

Informacja

Punktem wyjścia dla referencji o TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid i TStringGrid jest referencje modułu Grids.pas

Natomiast dla TCustomDBGrid i TDBgrid jest referencje modułu DBGrids.pas

Do niedawna nie było w tych linkach zbyt dużo treści, częściowo z powodu braku narzędzia, które pozwala na łatwe utrzymanie tych zasobów: ale takie narzędzie zostało wykonane i luki są obecnie wypełniane.

Zazwyczaj jakichkolwiek informacje o komponentach grids dla Delphi powinny pomóc nam także w Lazarusie (ale nie zapominajmy o tych kilku różnicach pomiędzy Delphi i Lazarusem, które są udokumentowane); przy czym traktuj takie dane jako tymczasowe źródło informacji, które nie zawsze będą działały tak samo jak w Delphi.

TODO: Dalsza część tego rozdziału zniknie, a jego zawartość zostanie przeniesiona do referencje modułu Grids.pas

TCustomGrid

Wszystko zobaczysz w: Referencje TCustomGrid

property OnBeforeSelection: TOnSelectEvent

property OnCompareCells: TOnCompareCells

property OnPrepareCanvas: TOnPrepareCanvasEvent

property OnDrawCell: TOnDrawCell

property OnEditButtonClick: TNotifyEvent

property OnSelection: TOnSelectEvent

property OnSelectEditor: TSelectEditorEvent

property OnTopLeftChanged: TNotifyEvent

procedure AutoAdjustColumns;

procedure BeginUpdate;

procedure Clear;

procedure DeleteColRow(IsColumn: Boolean; index: Integer);

function EditorByStyle(Style: TColumnButtonStyle): TWinControl;

procedure EndUpdate(UO: TUpdateOption); overload;

procedure EndUpdate(FullUpdate: Boolean); overload;

procedure EndUpdate; overload;

procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer);

function IscellSelected(aCol,aRow: Integer): Boolean;

function IscellVisible(aCol, aRow: Integer): Boolean;

procedure LoadFromFile(FileName: string);

function MouseToCell(Mouse: TPoint): TPoint; overload;

function MouseToLogcell(Mouse: TPoint): TPoint;

function MouseToGridZone(X,Y: Integer): TGridZone;

function CellToGridZone(aCol,aRow: Integer): TGridZone;

procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer);

procedure SaveToFile(FileName: string);

procedure SortColRow(IsColumn: Boolean; index:Integer); overload;

procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload;

property AllowOutboundEvents:boolean;

Właściwość AllowOutboundEvents jest chroniona w TCustomGrid, a publiczna w TCustomDrawGrid i klasach potomnych. Zwykle, gdy użytkownik kliknie na puste miejsce poza obszarem komórek (na przykład jeśli siatka składa się z trzech wierszy, ale użytkownik kliknie w fikcyjnym czwartym rzędzie) obecny fokus (aktywność) komórki będzie przeniesiony do komórki najbliższej temu kliknięciu, wywołane zostanie zewnętrzne zdarzenie; domyślną wartością jest tutaj true (prawda) o ile została zachowana ta wartość siatki od samego początku. Ta właściwość została dodana w celu symulowania zachowania Delphi, gdzie zewnętrzne zdarzenia nie są dostępne, aby zachować kompatybilność z Delphi należy ustawić tę właściwość na false (fałsz).

TCustomStringGrid

TCustomStringGrid służy jako podstawa dla TStringGrid. Może być stosowana jako pochodna komponentu TStringGrid, która ma ukrywać publiczne właściwości. Patrz: nowe pośrednie komponenty grids, aby otrzymać więcej informacji.

Następujące właściwości lub metody są publiczne i dostępne także w TStringGrid.

Zobacz całe Referencje TCustomStringGrid

procedure AutoSizeColumn(aCol: Integer);

Ta procedura dostosowuje szerokość kolumny do rozmiaru najszerszego tekstu, jaki znajduje się pośród wszystkich wierszy kolumny aCol. Wskazówka: zobacz opcję goDblClickAutoSize, która umożliwia automatyczną zmianę szerokości kolumny, gdy użyjemy podwójnego kliknięcia na obramowaniu tejże kolumny.

procedure AutoSizeColumns;

Automatycznie zmienia szerokość wszystkich kolumn, dopasować ją do najdłuższego tekstu zawartego pośród wszystkich wierszy poszczególnych kolumn. Jest to szybka metoda zastosowania AutoSizeColumn() dla każdej kolumny w siatce.

procedure Clean; overload;

Czyści wszystkie komórki siatki, także te stałe (typu fixed).

procedure Clean(CleanOptions: TGridZoneSet); overload;

Czyści wszystkie komórki w siatce wskazane w parametrze CleanOptions. Zobacz TGridZoneSet aby uzyskać więcej informacji. Poniżej kilka przykładów:

  • Czyści wszystkie komórki: grid.Clean([]); (działa tak samo jak grid.clean)
  • Czyści wszystkie normalne (nie stałe - non fixed) komórki: grid.Clean([gzNormal]);
  • Czyści wszystkie komórki oprócz nagłówków kolumn siatki: Grid.Clean([gzNormal, gzFixedRows]);

procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TGridZoneSet); overload;

Działa tak samo jak Clean(CleanOptions:TGridZoneSet), ale jej działanie ograniczone jest obszarem określonym parametrami StartCol, StartRow, EndCol i EndRow. Przykład:

  • Czyści kolumny o ideksach od 4 do 6, ale bez nagłówków kolumn: można to zrobić na wiele sposobów, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); itp.

procedure Clean(aRect: TRect; CleanOptions: TGridZoneSet); overload;

Działa tak samo jak Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), ale przy pomocy TRect zamiast wskazywania pojedynczych współrzędnych komórek. Przydatne do czyszczenia wybranego obszaru: grid.Clean(Grid.Selection,[]);

property Cols[index: Integer]: TStrings read GetCols write SetCols;

Pobierz/wstaw listę ciągów string z/do kolumny siatki o wskazanym indeksie, począwszy od wiersza o indeksie 0 do wiersza o indeksie RowCount-1.

Przykłady
  • Przykład wstawiania: Wstawia treść do trzeciej kolumny siatki z kontrolki ListBox:
Grid.Cols[2] := ListBox1.Items;
  • Przykład pobrania: Wstawia treść kontrolki ListBox z kolumny siatki o indeksie 4:
procedure TForm1.FillListBox1;
var 
  StrTempList: TStringList;
begin
  StrTempList := TStringList(Grid.Cols[4]);
  if StrTempList<>nil then begin
    ListBox1.Items.Assign(StrTempList);
    StrTempList.Free;
  end;
end;   
Uwagi

Ta właściwość działa inaczej w Lazarusie i w Delphi, gdy pobieramy dane z siatki. W Lazarusie tworzony jest tymczasowy obiekt typu TStringList w celu pobrania zawartości kolumny. Użytkownik jest odpowiedzialny za zwolnienie tego obiektu po użyciu.

Oznacza to także, że zmiany w zwracanej liście nie będą miały wpływu na zawartość siatki lub układu.

Zobacz powyższy przykład pobrania.

property Rows[index: Integer]: TStrings read GetRows write SetRows;

Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1.

Notes.

This property works differently in Lazarus and in Delphi when getting the data from the grid. In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use.

This means also that changes in the returned list will not affect the grid's content or layout.

Examples
  • Set Example: Set the content of the third row in the grid from a ListBox:
Grid.Rows[2] := ListBox1.Items;
  • Get Example: Set the content of a Listbox from the grid's row index 4:
procedure TForm1.FillListBox1;
var 
  StrTempList: TStringList;
begin
  StrTempList := TStringList(Grid.Rows[4]);
  if StrTempList<>nil then begin
    ListBox1.Items.Assign(StrTempList);
    StrTempList.Free;
  end;
end;  
  • An Example that doesn't work, and its Fix: Retrieved string list is read only
// this will not work and will cause memory leak
// because returned StringList is not being freed
Grid.Rows[1].CommaText := '1,2,3,4,5';
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; 

// fixing the first case
Lst:=TStringList.Create;
Lst.CommaText := '1,2,3,4,5';
Grid.Rows[1] := Lst;
Lst.Free;

property UseXORFeatures;

Boolean property, default value: False;

This property controls how the dotted focus rectangle appears in the grid. When True, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When False, the user can control the color of the dotted focus rectangle using the FocusColor property

It also controls the look of the column/row resizing. When True, a line shows visually the size that the the column or row will have if the user ends the operation. When False, the column or row resizing takes effect just as the user drags the mouse.

Grids Howto

Customizing grids

Grid are components derived from the TCustomControl class, and don't have a native widget associated with them which means that grids are not restricted by the look of current interface theme. This can be both an advantage and a disadvantage: usually programmers want to create a uniform-look application. The good news is that Lazarus grids are flexible enough to get something from both worlds; programmers can easily make grids look similar to other native controls, or they can customize the grid to the finest detail so they can obtain almost the same look in any platform or widget interface (that is, with the exception of scrollbars, because their look is still determined by the current theme).

Properties and Events for customizing grids

Some properties can affect the way the grid looks by acting when the cell is about to be painted in PrepareCanvas/OnPrepareCanvas by changing default canvas properties like brush color or font. Following is a list of such properties:

  • AlternateColor. With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.
  • Color. This sets the primary color used to draw non fixed cells background.
  • FixedColor. This is the color used to draw fixed cells background.
  • Flat. this eliminates the 3d look of fixed cells.
  • TitleFont. Font used to draw the text in fixed cells.
  • TitleStyle. This property changes the 3D look of fixed cells, there are 3 settings:
    • tsLazarus. This is the default look
    • tsNative. This tries to set a look that is in concordance with current widgetset theme.
    • tsStandard. This style is a more contrasted look, like delphi grids.
  • AltColorStartNormal. boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.
  • BorderColor. This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;
  • EditorBorderStyle. If set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.
  • FocusColor. The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.
  • FocusRectVisible. turns on/off the drawing of focused cell.
  • GridLineColor. color of grid lines in non fixed area.
  • GridLineStyle. Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.
  • SelectedColor. Color used to draw cell background on selected cells.
  • UseXORFeatures. If set, focus rect is drawn using XOR mode so it should make visible the focus rect in combination with any cell color ackground. It also affects the moving columns look.
  • DefaultDrawing. boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.
  • AutoAdvance. where the cell cursor will go when pressing enter or tab/shift tab, or after editing.
  • ExtendedColSizing. If true user can resize columns not just at the headers but along the columns height.

Other properties that also affect the grids look.

Options. Options property is a set with some elements to enable diverse functionality but some are related directly with grid's look. This options can be set at designtime or runtime.

  • goFixedVertLine, goFixedHorzLine it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
  • goVertLine, goHorzLine the same as previous, but for normal browseable area. A grid can be made to simulate a listbox by unsetting both of this elements.
  • goDrawFocusSelected if this element is enabled a selection background is painted in focused cell in addition to focused dotted rectangle (note this doesn't work yet when goRowSelect option is set, in such case row is always painted as if goDrawFocusSelected is set)
  • goRowSelect select the full row instead of individual cells
  • goFixedRowNumbering if set, grid will do numbering of rows in first fixed column
  • goHeaderHotTracking if set, the grid will try to show a different look when the mouse cursor is overing any fixed cell. In order for this to work, desired cell zone needs to be enabled with property HeaderHotZones. Try combining this option with property TitleStyle:=tsNative to get themed hot tracking look.
  • goHeaderPushedLook if set, this element enables a pushed look when clicking any fixed cell. The zone of "pushable" cells is enabled using HeaderPusedZones property.

(write more)

Description of grid's drawing process

Like other custom controls, the grid is drawn using the paint method. In general terms the grid is drawn by painting all rows, and each row by painting its individual cells.

The process is as follow:

  • First the visible cells area is determined: each row is tested to see if it intersects the canvas clipping region; if it's ok, then the visible area is painted by drawing columns of each row.
  • The column and row values are used to identify the cell that is about to be painted and again each column is tested for intersection with the clippling region; if everything is ok, some additional properties like the cell's rectangular extent and visual state are passed as arguments to the DrawCell method.
  • As the drawing process is running, the visual state of each cell is adjusted according to grid options and position within grid. The visual state is retained in a varible of type TGridDrawState which is a set with following elements:
    • gdSelected The cell will have a selected look.
    • gdFocused The cell will have a focused look.
    • gdFixed Cell have to be painted with fixed cell look.
    • gdHot the mouse is over this cell, so paint it with hot tracking look
    • gdPushed the cell is being clicked, paint it with pushed look
  • DrawCell. The DrawCell method is virtual and may be overriden in descendant grids to do custom drawing. The information passed to DrawCell helps to identify the particular cell is being painted, the physical area ocuppied in screen and its visible status. See DrawCell reference for details. For each cell the following occurs:
  • PrepareCanvas. In this method, if the DefaultDrawing property is set, the grid canvas is setup with default properties for brush and font based on current visual state. For several design and runtime properties, the text alignment is set to match programmer selection in custom columns if they exists. If DefaultDrawing is false, brush color is set to clWindow and Font color to clWindowText, the text alignment is set with grids defaultTextStyle property value.
  • OnPrepareCanvas. If the programmer wrote an event handler for OnPrepareCanvas event, it is called at this point. This event can be used for doing simple customization like changing cell's background color, font's properties like color, fontface and style, Text layout like different combinations of left, center, top, bottom, right alignment, etc. Any change made to the canvas in this event would be lost, because the next cell drawing will reset canvas again to a default state. So it's safe doing changes only for particular cell or cells and forget about it for the rest. Using this event sometimes helps to avoid using the OnDrawCell grid event, where users would be forced to duplicate the grid's drawing code. Todo: samples of what can be made and what to leave for OnDrawCell?...
  • OnDrawCell. Next if no handler for OnDrawCell event was specified, the grid calls the DefaultDrawCell method which simply paints the cell background using the current canvas brush color and style. If the OnDrawCell handler exists, the grid first paints the cell background but only if DefaultDrawing property was set, then it calls OnDrawCell event to do custom cell painting. Usually programmers want to do custom drawing only for particular cells, but standard drawing for others; in this case, they can restrict custom operation to certain cell or cells by looking into ACol, ARow and AState arguments, and for other cells simply call DefaultDrawCell method and let the grid to take care of it.
  • Text. At this point (only for TStringGrid) if DefaultDrawing property is true, the cell's text content is painted.
  • Grid lines. The last step for each cell is to paint the grid lines: if grid options goVertLine, goHorzLine, goFixedVertLine and goFixedHorzLine are specified the cell grid is drawn at this point. Grids with only rows or only cols can be obtained by changing these options. If the programmer elected to have a "themed" look it is done at this point also (see property TitleStyle).
  • FocusRect. When all columns of current row have been painted it is time to draw the focus rectangle for the current selected cell or for the whole row if goRowSelect option is set.

Grid's cell selection

The location of a grid's current (focused) cell (or row) can be changed using keyboard, mouse or through code. In order to change cell focus successfully to another position, we must test the target position to see if it is allowed to receive cell focus. When using keyboard, the property AutoAdvance performs part of the process by finding what should be the next focused cell. When using mouse clicks or moving by code, focus will not move from the current cell unless the target cell is permitted to receive focus.

The grid calls function SelectCell to see if a cell is focusable: if this function returns true, then the target cell identified with arguments aCol and aRow is focusable (the current implementation of TCustomGrid simply returns true). TCustomDrawGrid and hence TDrawGrid and TStringGrid override this method to check first if cell is any wider than 0; normally you don't want a 0 width cell selected so a cell with this characteristics is skipped automatically in the process of finding a suitable cell. The other thing the overriden method SelectCell does is to call the user configurable event OnSelectCell: this event receives the cell coordinates as arguments and always returns a default result value of true.

Once a cell is known to be focusable and we are sure a movement will take place, first the method BeforeMoveSelection is called; this in turns triggers the OnBeforeSelection event. This method's arguments are the coordinates for the new focused cell, at this point any visible editor is hidden too. The "before" word means that selection is not yet changed and current focused coordinates can be accessed with grid.Col and grid.Row properties.

After that, the internal focused cell coordinates are changed and then method MoveSelection is called; this method's purpose is to trigger the OnSelection event if set (this is a notification that the focused cell has, by this time, already changed and cell coordinates are now available through grid.row and grid.col properties).

Note that is not good to use OnSelectCell event to detect cell focus changes, as this event will be triggered several times even for the same cell in the process of finding a suitable cell. Is better to use OnBeforeSelection or OnSelection events for this purpose.

Several differences with Delphi have been identified

  • In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
  • In Delphi, the cell's text is drawn before entering the OnDrawCell event (see bug report #9619).
  • SelectCell and OnSelectCell behaviour is probably different - can't really comment on the differences. In Lazarus they are used in functionality like AutoAdvance which as far as I know doesn't exist in Delphi.

When above customizaton is not enough, derived grids

Derived grids usually have to override following methods:
DrawAllRows: Draws all visible rows.
DrawRow: Draw All Cells in a Row.
DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.
DrawCell:
DrawCellGrid:
DrawCellText:
DrawFocusRect:
(write me).

Operations

Focusing a cell

Focusing a cell in TStringGrid is easy. Note that counting starts from zero not 1. So to focus row 10, column 9, do:

StringGrid1.row := 9;
StringGrid1.col := 8;

Save and Retrieve Grid Content

The SaveToFile procedure allows you save the TStringGrid format, attributes & values to a XML file. Previously you must set the SaveOptios property as follow:

soDesign:     Save & Load ColCount,RowCount,FixedCols,FixedRows,
              ColWidths, RowHeights and Options (TCustomGrid)
soPosition:   Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
soContent:    Save & Load Text (TCustomStringGrid)

The LoadFromFile procedure allows you to load into a StringGrid instance, attributes, formats & values, from a XML file. First, you must set some of this options of the SaveOptions property (on your TStringGrid instance) SaveOptions

 soDesign:     Save & Load ColCount,RowCount,FixedCols,FixedRows,
               ColWidths, RowHeights and Options (TCustomGrid)
 soPosition:   Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)
 soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
 soContent:    Save & Load Text (TCustomStringGrid)

Example:

  1. First, go to menu "File -> New -> Application";
  2. Put an empty TStringGrid;
  3. Put a TButton and TOpenDialog;
  4. Add the event OnCreate for the Form;
  5. Add the event OnClick for the Button.
unit Unit1; 
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,
  Buttons, StdCtrls, XMLCfg;
 
type
 
  { TForm1 }
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Form1Create(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 
 
var
  Form1: TForm1; 
 
implementation
 
{ TForm1 }
 
procedure TForm1.Form1Create(Sender: TObject);
begin
 //sets the SaveOptions at creation time of the form 
 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
end;
 
 
procedure TForm1.Button1Click(Sender: TObject);
begin
 //Ask if thew Execute method of the OpenDialog was launched 
 //when this occurs, the user selects an XML file to Load
 //wich name was stored in the FileName prop.
 
 if opendialog1.Execute then
 Begin
   //Clear the grid 
   StringGrid1.Clear;
   //Load the XML
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Refresh the Grid
   StringGrid1.Refresh;
 End;
end;
 
initialization
  {$I unit1.lrs}
 
end.

The sample xml file: (Copy the text below into a txt file. Don't forget put the xml header :-))

<?xml version="1.0"?>
<CONFIG>
  <grid version="3">
    <saveoptions create="True" position="True" content="True"/>
    <design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20">
      <options>
        <goFixedVertLine value="True"/>
        <goFixedHorzLine value="True"/>
        <goVertLine value="True"/>
        <goHorzLine value="True"/>
        <goRangeSelect value="True"/>
        <goDrawFocusSelected value="False"/>
        <goRowSizing value="False"/>
        <goColSizing value="False"/>
        <goRowMoving value="False"/>
        <goColMoving value="False"/>
        <goEditing value="False"/>
        <goTabs value="False"/>
        <goRowSelect value="False"/>
        <goAlwaysShowEditor value="False"/>
        <goThumbTracking value="False"/>
        <goColSpanning value="False"/>
        <goRelaxedRowSelect value="False"/>
        <goDblClickAutoSize value="False"/>
        <goSmoothScroll value="True"/>
      </options>
    </design>
    <position topleftcol="1" topleftrow="1" col="1" row="1">
      <selection left="1" top="1" right="1" bottom="1"/>
    </position>
    <content>
      <cells cellcount="10">
        <cell1 column="0" row="0" text="Title Col1"/>
        <cell2 column="0" row="1" text="value(1.1)"/>
        <cell3 column="0" row="2" text="value(2.1)"/>
        <cell4 column="0" row="3" text="value(3.1)"/>
        <cell5 column="0" row="4" text="value(4.1)"/>
        <cell6 column="1" row="0" text="Title Col2"/>
        <cell7 column="1" row="1" text="value(1.2)"/>
        <cell8 column="1" row="2" text="value(2.2)"/>
        <cell9 column="1" row="3" text="value(3.2)"/>
        <cell10 column="1" row="4" text="value(4.2)"/>
      </cells>
    </content>
  </grid>
</CONFIG>

--Raditz 21:06, 11 Jan 2006 (CET) from ARGENTINA

Grid Cell Editors

The grid uses cell editors to change the content of cells.

For a specialized grid like TStringGrid, the editor is the usual single line text editor control, but sometimes it's desirable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually; if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily.

Sometimes the information the user should enter in a cell is restricted to a limited list of words; in this case typing the information directly might introduce errors and validating routines might need to be implemented. We can avoid this by using a cell editor that presents the user with a list containing only the legal values.

This is also the case for generic grids like TDrawGrid where the user needs some kind of structure to hold the data that will be shown in the grid. In this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid.

Builtin cell editors

The grids.pas unit already includes some of the most used cell editors ready for use in grids. It is also possible to create new cell editors (custom cell editors) if the built-in editors are not appropiate for a specific task.

The builtin cell editors are Button, Edit, and Picklist.

Using cell editors

Users can specify what editor will be used for a cell using one of two methods.

  1. Using a custom column and selecting the ButtonStyle property of the column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
  2. Using OnSelectEditor grid event. Here the user specifies in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.

Description of editor styles

The following is a description of the editor styles. They are enumerated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used to remain compatible with Delphi's DBGrid.

  • cbsAuto
This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content depends on several factors. For TCustomGrids it uses a TStringCellEditor class derived from TCustomMaskEdit. This editor is specialized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns, if the programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that has a field of type boolean, it behaves as if cbsCheckBoxColumn editor style was specified. This is the recommended value for Custom Cell Editors. TODO: related OnEditingDone.
  • cbsEllipsis
This editor style is the most generic one. When used, a button appears in the editing cell and programmers could use the OnEditButtonClick grid event to detect when the user has pressed the button and take any action programmed for such a cell. For example a programmer could use this editor style to pop up a calendar dialog to allow the user easily to select a specific date. Other possibilities could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc.
OnEditButtonClick is just a notification, to find out in which cell a button has been clicked by taking a look at the grid.Row and grid.Col properties.
A DBGrid has specific properties to retrieve the active column or field and because this event occurs in the active record, it could update the information in the active field.
This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
  • cbsNone
This editor style instructs the grid not to use any editor for a specific cell or column; it behaves then, as if the grid were readonly for such a cell or column.
  • cbsPickList
Used to present the user with a list of values that can be entered. This editor style is implemented using TPickListCellEditor, a component derived from TCustomComboBox. The list of values that are shown is filled in one of two ways depending on the method used to select the editor style.
  1. When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]
  2. In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). An example would be:
var Lst:TPickListCellEditor; 
  begin [...] 
    Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); 
    Lst.clear; 
    Lst.Items.add('One'); 
    Lst.items.add('Two'); 
    Editor:=Lst; 
  end;
The value in a TStringGrid grid will automatically reflect the value selected. If necessary the programmer could detect the moment the value is selected by writing an event handler for the grid's OnPickListSelect event, so additional steps can be taken (for example, to process the new value). TODO: related OnEditingDone.
  • cbsCheckboxColumn
It can be useful when the data content associated with the column is restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of data in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the data of a column by using a checkbox representation that the user can toggle by using a mouse click or pressing the SPACE key.
If a columns' ButtonStyle property is set to cbsAuto and DBGrid detects that the field associated with the column is a boolean field, then the grid uses this editor style automatically. This automatic selection can be disabled or enabled using DBGrid's OptionsExtra property; setting dgeCheckboxColumn element to false disables this feature.
The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
At any moment, the field value can be in one to three states: Unchecked, Checked or Grayed. Internally these states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.
This editor style doesn't use real TCheckbox components to handle user interaction: the visual representation is given by three built-in bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap; the handler of this event gets the state of the checkbox in the parameter CheckedState of type TDBGridCheckboxState and a bitmap parameter that the programmer could use to specify custom bitmaps.
  • cbsButtonColumn
This editor style is used to show a button on every cell on column. Like in the case of cbsCheckboxColumn this editor do not use real buttons, the appearance is defined by current widgetset theme.
The user knows what particular button was pressed by handling the grid's OnEditButtonClick and checking grid's col and row. Note that in this particular case, grid's col and row do not identify the currently selected cell, but the cell of the clicked button. Once the OnEditButtonClick event has been handled, and if the user has not modified the grid's col or row in this handler, the grid automatically resets the col and row to reflect the currently selected cell. While handling the OnEditButtonClick the current grid selection is available in grid.Selection which is a TRect property, left and right represent Column indexes, Top and Bottom are row indexes.
The button's caption is the corresponding cell string.

Example: How to set a custom cell editor

See lazarus/examples/gridcelleditor/gridcelleditor.lpi

Example: How to add a button editor

// Conditionally show button editor in column index 1 or 2 if 
// cell in column index 1 is empty 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer; 
  var Editor: TWinControl);
begin
  if (aCol = 1) or (aCol = 2) then
    if StringGrid1.Cells[1,aRow] = '' then
    begin
      Editor := StringGrid1.EditorByStyle(cbsEllipsis);
    end;
  end;
 
// Triggering Action ...
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
begin
  if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
  if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
end;

Example: How to align column text in StringGrids

  procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
    aState: TGridDrawState);
  var
    MyTextStyle: TTextStyle;
  begin
    if (Col=2) or (Col=3) then
    begin
      MyTextStyle := StringGrid1.Canvas.TextStyle;
      if Col=2 then
        MyTextStyle.Alignment := taRightJustify 
      else 
      if Col=3 then
        MyTextStyle.Alignment := taCenter;
      StringGrid1.Canvas.TextStyle := MyTextStyle;
    end;
  end;

Validating Entered Values

Lazarus version 0.9.29 introduces StringGrid OnValidateEntry event of type TValidateEntryEvent which has following declaration:

  TValidateEntryEvent =
    procedure(sender: TObject; aCol, aRow: Integer;
              const OldValue: string; var NewValue: string) of object;
aCol,aRow are the cell coordinates of cell being validated.
OldValue is the value that was in cells[aCol,aRow] before editing started.
NewValue is the value that will be finally inserted in cells[aCol,aRow].

Because of the way StringGrid works by setting the cell value while user is editing (see grid's OnSetEditText event and SetEditText method) when the OnValidateEntry event triggers, the cell already contains the entered value being valid or not, using event arguments OldValue and NewValue the cell value can be validated and changed accordingly.

Usually validation occurs when user has moved to another cell, if validation fails is desireable to keep the cell editor visible so the entered value can be corrected by user. To let the grid know that validation has failed an exception needs to be raised, the grid will handle the exception to Application.HandleException and any movement is cancelled. For example, suppose that cell[1,1] should hold only values 'A' or 'B' validation could be made with:

 procedure TForm1.GridValidateEntry(sender: TObject; aCol,
   aRow: Integer; const OldValue: string; var NewValue: String);
 begin
   if (aCol=1) and (aRow=1) then begin
     if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
       // set a new valid value so user can just press RETURN to continue for example.
       NewValue := 'A';
       // another option is reverting to previous cell value (which is assumed to be valid)
       // NewValue := OldValue;
       raise Exception.Create('Only A or B are allowed here');
     end else begin
       // if no exception is raised, is assumed that value is valid, yet if necessary
       // final value can be changed by filling NewValue with a different value
 
       // computer knows better :)
       if grid.Cells[1,1]='A' then 
         NewValue := 'B' 
       else 
         NewValue := 'A';
     end;
   end;
 end;

Sorting Columns or Rows

StringGrid has built-in sorting capabilities using SortColRow() method, the first parameter is a boolean value which indicates true if a column is to be sorted or false for a row, the next parameter is the column or row index, the next parameters are optional and selects subrange of rows (for column sorting) or columns (for row sorting) to be sorted, if the last parameters are not specified, the whole column or row is sorted. Sorting uses QuickSort algorithm, it could be changed if a descendant grid overrides the sort() method and calls doCompareCells for cell compare.

By default it sorts cell content as strings either in ascending or descending order which is selectable with property SortOrder, by default it uses Asceding order.

  // sort column 3 in ascending order
  grid.SortColRow(true, 3);
 
  // sort column 3 in descending order, skip fixed rows a top
  grid.SortOrder := soDescending; // or soAscending
  grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);

For custom sorting of numbers, dates, states, etc. StringGrid has the OnCompareCells event which users can handle for example this way:

procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol,
  BRow: Integer; var Result: integer);
begin
  result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
  // result will be either <0, =0, or >0 for normal order
  // for inverse order, just invert the sign of result
  // result := -result;
end;

Please notice that SortOrder do not work if OnCompareCells is used.

Tłumaczenie

Na język polski przełożył: --Sławomir Załęcki 21:10, 10 September 2010 (CEST)