Difference between revisions of "Grids Reference Page/pl"

From Lazarus wiki
Jump to navigationJump to search
m (Text replace - "delphi>" to "syntaxhighlight>")
(poprawka linku)
 
(72 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{Grids Reference Page}}
 
{{Grids Reference Page}}
 
+
<div style="font-size:1.88em;margin:0.75em 0;">Strona referencyjna siatek Grid</div>
 
== Intencje ==
 
== 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).
+
Ten tekst będzie próbą pokazania użytkownikowi niektórych aspektów komponentów siatek (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 siatek (ponieważ doświadczeni użytkownicy zazwyczaj potrzebują jedynie informacji dotyczących nowych funkcji).
Dlatego ten tekst będzie starał się osiągnąć następujące cele:
+
W niniejszym tekście postaram się zatem osiągnąć następujące cele:
# Aby przybliżyć komponenty grids osobom, które nie miały lub miały bardzo mały kontakt z Delphi.
+
# Przedstawić komponenty siatki osobom z niewielkim lub żadnym doświadczeniem w Delphi.
# Aby udokumentować różnice w porównaniu z Delphi komponentów grids.
+
# Udokumentować różnice w odniesieniu do komponentów siatek Delphi.
# Aby udokumentować nowe funkcjonalności w komponentach grids Lazarusa.
+
# Udokumentować nową funkcjonalność w siatkach Lazarusa.
# Spisać informacje i przykłady dla tych komponentów.
+
# Utworzyć referencje i przykłady dla tych komponentów.
  
 
== Wprowadzenie ==
 
== 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ą).
+
Siatka (Grid) to komponent, który umożliwia wyświetlanie danych w formacie tabelarycznym. Najbardziej oczywistą cechą siatek jest to, że składają się one z komórek tworzących rzędy i kolumny, podobnie jak w arkuszu kalkulacyjnym.
  
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.
+
Ilość typów informacji, które można wyświetlić w siatce, jest różna i zależy głównie od tego, co użytkownik chce pokazać. Ogólnie informacje te składają się z tekstów, kolorów, obrazów lub kombinacji tych trzech.
  
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.
+
Biorąc pod uwagę ogromną różnorodność informacji, które można przedstawić, istnieje szereg siatek, których celem jest ułatwienie użytkownikowi pokazania określonych rodzajów informacji. Na przykład istnieje siatka przeznaczona do wyświetlania tekstu: '''StringGrid'''. Dokumentację dla tej siatki można znaleźć [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tstringgrid.html tutaj]
  
 
== Drzewo dziedziczenia ==
 
== Drzewo dziedziczenia ==
<pre>
+
 
                       [TCustomControl]          
+
                       [[TCustomControl]]
                               |                  
+
                               |
                               |                  
+
                               |
                         TCustomGrid              
+
                         TCustomGrid
                               |                  
+
                               |
                 +-------------+------------+      
+
                 +-------------+------------+
                 |                          |      
+
                 |                          |
 
           TCustomDrawGrid            TCustomDbGrid
 
           TCustomDrawGrid            TCustomDbGrid
                 |                          |      
+
                 |                          |
       +--------+--------+                |      
+
       +--------+--------+                |
       |                |              TDbGrid   
+
       |                |              [[TDBGrid]]
   TDrawGrid      TCustomStringGrid                 
+
   [[TDrawGrid]]     TCustomStringGrid                 
                         |                        
+
                         |
                         |                        
+
                         |
                     TStringGrid                  
+
                     [[TStringGrid]]
</pre>
 
  
 
== Pierwszy przykład ==
 
== 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.
+
Ponieważ jednym z celów tej strony jest pomoc osobom z niewielką lub żadną wcześniejszą wiedzą o Lazarusie, zróbmy wprowadzający przykład użycia siatek. Dlaczego nie, stwórzmy tradycyjny przykład „Witaj Świecie!” (ang. "Hello World") za pomocą komponentu TStringGrid.
#Tworzenie nowej aplikacji
+
#Stwórz nową aplikację.
#*Z głównego menu wybieramy: Projekt->Nowy projekt
+
#*Z menu głównego wybierz: Projekt->Nowy projekt...
#*W oknie dialogowym Create New Project wybierz Aplikacja i naciśnij "OK"
+
#*W oknie dialogowym Utwórz nowy projekt wybierz Aplikacja i naciśnij „OK”
#*Pokaże się nowy, pusty formularz.
+
#*Zostanie wyświetlony nowy pusty formularz.
#Położenie komponentu grid na formularzu
+
#Umieść siatkę (grid) na formularzu
#*Z palety komponentów wybierz zakładkę "Additional"
+
#*Z palety komponentów wybierz zakładkę „Additional”
#*Kliknij na ikonę TStringGrid [[Image:tstringgrid.png]]
+
#*Kliknij ikonę TStringGrid [[image:tstringgrid.png]]
#*Kliknij na formę, w pobliżu lewego górnego rogu. Pojawi się nowy pusty komponent grid.
+
#*Kliknij formularz w lewym górnym rogu. Pojawi się nowa pusta siatka.
#Położenie przycisku na formularzu
+
#Umieść przycisk na formularzu
#*Z palety komponentów wybierz zakładkę "Standard"
+
#*Z palety komponentów wybierz zakładkę „Standard”
#*Kliknij na ikonę TButton [[Image:tbutton.png]]
+
#*Kliknij ikonę TButton [[Image:tbutton.png]]
 
#*Kliknij na pusty obszar formularza. Pojawi się nowy przycisk.
 
#*Kliknij na pusty obszar formularza. Pojawi się nowy przycisk.
#Podwójnie kliknij na przycisk z kroku 3, aby zapisać następujący kod w procedurze obsługi przycisku:
+
Kliknij dwukrotnie przycisk z kroku 3 i zapisz następujący kod w module obsługi przycisku kliknięcia:
#*StringGrid1.Cells [1,1]: = "Witaj Świecie!";
+
#*<syntaxhighlight lang="Pascal">StringGrid1.Cells[1,1] := 'Witaj Świecie!';</syntaxhighlight>
#Uruchom program, klikając ikonę "Uruchom" [[Image:menu_run.png]]
+
#Uruchom program, klikając ikonę odtwarzania [[Image:menu_run.png]]
#*Po naciśnięciu przycisku Button1, tekst „Witaj Świecie!” powinien pojawić się w komórce; kolumna 1, wiersz 1.
+
#*po naciśnięciu przycisku Button1 w komórce w kolumnie 1 i wierszu 1 powinien pojawić się tekst „Witaj Świecie!”. (zwróć uwagę, że indeksy lewej kolumny i górnego wiersza są równe 0)
 +
 
 +
== Różnice pomiędzy komponentami siatki Lazarusa i Delphi ==
 +
Obecne elementy siatek różnią się od siatek Delphi na kilka sposobów. Jest to spowodowane głównie tym, że kiedy po raz pierwszy opracowano siatki dla Lazarusa, były one tworzone od zera bez żadnych prób uczynienia ich w pełni kompatybilnymi z Delphi.
  
== Różnice pomiędzy komponentami grids Lazarusa i Delphi ==
+
Na późniejszym etapie kompatybilność z siatkami Delphi stała się pożądanym celem, a siatki Lazarus zaczęły być bardziej zgodne z interfejsem sieci Delphi. Jednak nawet to zostało zrobione bez próby dopasowania każdej właściwości lub metody siatki Lazarusa do swojego odpowiednika w Delphi. Ponadto (ponieważ wewnętrzne elementy siatki Lazarusa bardzo różnią się od wewnętrznych elementów siatki Delphi) niektóre funkcje Delphi nie są dostępne lub muszą być emulowane w inny sposób w siatce Lazarusa niż w przypadku siatki Delphi. Osiągnęliśmy większą kompatybilność z Delphi w miarę ewolucji siatek Lazarusa i jest to pożądane.
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 ===
 
=== Różnice ===
Poniżej wymienione będą znane różnice (kolejność jest nieistotna)
+
Znane różnice wymieniono poniżej (kolejność nie ma znaczenia).
 
*Edytor komórek (Cell Editor)
 
*Edytor komórek (Cell Editor)
 
*Zachowanie podczas projektowania
 
*Zachowanie podczas projektowania
*Rysowanie komórek wykazuje pewne różnice, zobacz w sekcji o dostosowywaniu komponentów grids.
+
*Pewne różnice występują podczas rysowania komórek, zobacz sekcję [https://wiki.freepascal.org/Grids_Reference_Page/pl#Dostosowywanie_siatek Dostosowywanie siatek].
  
 
=== Nowe funkcje ===
 
=== Nowe funkcje ===
Line 72: Line 70:
 
*Edytor siatki (Grid Editor)
 
*Edytor siatki (Grid Editor)
  
=== Korekty w celu poprawy zgodności z Delphi ===
+
=== Sposoby na to, by siatka Lazarus była bardziej zgodna 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.
 
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.
 +
 +
Możesz sprawić, by siatka Lazarusa wyglądała i zachowywała się tak, jak jej odpowiednik w Delphi. Poniżej wymienione są ustawienia właściwości, które pozwolą to osiągnąć. Te dostosowania są oparte na nowo utworzonej siatce.
 +
Wpisy oznaczone jako [Kod] należy ustawić w kodzie, wpisy [Projekt] można zmienić w czasie projektowania.
  
 
*[Projekt] TitleStyle := tsStandard;
 
*[Projekt] TitleStyle := tsStandard;
Line 79: Line 80:
 
*[Kod]    EditorBorderStyle := bsNone;  //to może działać tylko w Windows
 
*[Kod]    EditorBorderStyle := bsNone;  //to może działać tylko w Windows
 
*[Kod]    UseXORFeatures := true;
 
*[Kod]    UseXORFeatures := true;
*[Kod]    AllowOutBoundEvents := False; //rewizja SVN r10992 lub dalsza
+
*[Kod]    AllowOutBoundEvents := False; //rewizja SVN r10992 lub nowsza
*[Kod]    FastEditing := False; //wspierane w dbgrid, StringGrid rew. r10992 lub dalsza
+
*[Kod]    FastEditing := False; //obsługiwane w dbgrid. StringGrid wymaga wersji SVN r10992 lub nowszej
 
*[Projekt] AutoAdvance := aaNone;
 
*[Projekt] AutoAdvance := aaNone;
  
== Powiązania komponentów grids ==
+
== Charakterystyka siatek ==
 
=== Informacja ===
 
=== Informacja ===
 
Punktem wyjścia dla referencji o TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid i TStringGrid jest [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html referencje modułu Grids.pas]
 
Punktem wyjścia dla referencji o TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid i TStringGrid jest [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html referencje modułu Grids.pas]
Line 89: Line 90:
 
Natomiast dla TCustomDBGrid i TDBgrid jest  
 
Natomiast dla TCustomDBGrid i TDBgrid jest  
 
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html referencje modułu DBGrids.pas]
 
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html 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.
 
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.
Line 98: Line 97:
 
=== TCustomGrid ===
 
=== TCustomGrid ===
 
Wszystko zobaczysz w: [[doc:lcl/grids/tcustomgrid.html|Referencje TCustomGrid]]
 
Wszystko zobaczysz w: [[doc:lcl/grids/tcustomgrid.html|Referencje TCustomGrid]]
==== property OnBeforeSelection: TOnSelectEvent ====
+
==== Właściwość AllowOutboundEvents ====
==== property OnCompareCells: TOnCompareCells ====
+
 
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====
+
Właściwość chroniona w klasie TCustomGrid oraz publiczna w klasie TCustomDrawGrid i klasach potomnych. Zwykle, gdy użytkownik kliknie punkt nad pustą przestrzenią za komórkami (na przykład, jeśli siatka ma trzy wiersze, ale użytkownik kliknie wyimaginowany czwarty wiersz), komórka, w której aktualnie znajduje się fokus, przesunie się do komórki najbliższej punktu, który właśnie kliknął. Nazywamy to wydarzeniem wychodzącym. Wartość domyślna to True, ponieważ tak właśnie działa siatka od samego początku. Ta właściwość została dodana, aby symulować zachowanie Delphi, gdy zdarzenia wychodzące nie są dostępne, więc aby umożliwić zgodność z Delphi, ustaw tę właściwość na False.
==== property OnDrawCell: TOnDrawCell ====
+
 
==== property OnEditButtonClick: TNotifyEvent ====
+
==== Właściwość Columns ====
==== property OnSelection: TOnSelectEvent ====
+
 
==== property OnSelectEditor: TSelectEditorEvent ====
+
Lazarus zawiera właściwość '''columns''' w siatkach TStringGrid i TDrawGrid. Ta właściwość dodaje to, co nazywamy kolumnami niestandardowymi. Kolumny niestandardowe to zbiór obiektów, które przechowują właściwości, które mają zastosowanie do całych kolumn, na przykład tytuły kolumn (w przypadku StringGrid zastąpi wartość określoną w odpowiedniej właściwości Cells[ColumnIndex, RowTitle]), wyrównanie tekstu, kolor tła, preferowany edytor itp.
==== property OnTopLeftChanged: TNotifyEvent ====
+
 
==== procedure AutoAdjustColumns; ====
+
Kolumny niestandardowe dodają dodatkowe właściwości lub zastępują domyślne wartości właściwości w normalnych kolumnach siatki. Ponadto, wartość '''grid.ColCount''' może się zwiększyć lub zmniejszyć w celu uwzględnienia liczby niestandardowych kolumn dołączanych do siatki. A to oznacza, że '''grid.ColCount''' = '''grid.FixedCols''' + '''grid.Columns.Count'''.
==== procedure BeginUpdate; ====
+
 
==== procedure Clear; ====
+
Na przykład, jeśli mamy siatkę bazową z ColCount := 5 i FixedCols := 2 i wykonamy:
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====
+
* Dodaj 3 niestandardowe kolumny, to siatka bazowa będzie dokładnie taka sama, jak poprzednio, 2 stałe kolumny i 3 normalne kolumny.
==== function  EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====
+
* Dodaj 1 niestandardową kolumnę, to siatka bazowa będzie miała ColCount := 3, czyli 2 stałe kolumny i 1 normalną kolumnę.
==== procedure EndUpdate(UO: TUpdateOption); overload; ====
+
* Dodaj 4 niestandardowe kolumny, to siatka bazowa będzie miała ColCount := 6, czyli 2 stałe kolumny i 4 normalne kolumny.
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====
+
 
==== procedure EndUpdate; overload; ====
+
Z tego możemy wywnioskować, że:
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====
+
 
==== function  IscellSelected(aCol,aRow: Integer): Boolean; ====
+
* Stałe właściwości lub liczba kolumn nie są odpowiednio ulepszane ani modyfikowane przez kolumny niestandardowe.
==== function  IscellVisible(aCol, aRow: Integer): Boolean; ====
+
* '''grid.ColCount''' zwykle różni się od '''grid.Columns.Count''' ('''grid.ColCount'''='''grid.Columns.Count''' tylko wtedy, gdy ' ''FixedCols'''=0)
==== procedure LoadFromFile(FileName: string); ====
+
 
==== function  MouseToCell(Mouse: TPoint): TPoint; overload; ====
+
W czasie projektowania  w Inspektorze obiektów użytkownik może uzyskać dostęp do właściwości '''columns''', aby wywołać edytor kolumn. Stamtąd można dodawać, usuwać lub modyfikować niestandardowe kolumny. Edytor wyświetla listę aktualnych kolumn niestandardowych; wybierając elementy z tej listy, inspektor obiektów zostaje wypełniony właściwościami dostępnymi dla każdej kolumny. Lista niestandardowych kolumn jest również dostępna w widoku drzewa komponentów Inspektora obiektów, gdzie kolumny można dodawać, usuwać lub modyfikować. Pojawiają się na niższym poziomie pod siatką kontenerów.
==== function  MouseToLogcell(Mouse: TPoint): TPoint; ====
+
 
==== function  MouseToGridZone(X,Y: Integer): TGridZone; ====
+
W czasie wykonywania programu, kolumny można modyfikować za pomocą kodu w następujący sposób:
==== function  CellToGridZone(aCol,aRow: Integer): TGridZone; ====
+
<syntaxhighlight lang="pascal">
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====
+
  var
==== procedure SaveToFile(FileName: string); ====
+
    c: TGridColumn;
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====
+
  begin
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====
+
    // dodaj niestandardową kolumnę siatki
==== property AllowOutboundEvents:boolean; ====
+
    c := Grid.Columns.Add;
Właściwość AllowOutboundEvents jest chroniona w TCustomGrid, a publiczna w TCustomDrawGrid i klasach potomnych.
+
    // modyfikuj kolumnę
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).
+
    c.title.caption := 'Price';       // Ustaw tytuły kolumn
 +
    c.align := taRightJustify;       // Wyrównaj zawartość kolumny do prawej
 +
    c.color := clMoneyGreen;         // Zmień domyślny kolor na clMoneyGreen
 +
    c.Index := 0;                     // Niech to będzie pierwsza kolumna
 +
    // uzyskać dostęp do istniejącej kolumny
 +
    grid.columns[0].Width := 60;     // Zmień szerokość kolumny 0 na 60 pikseli
 +
    // usuń istniejącą kolumnę
 +
    grid.columns.delete(0);           // Usuń kolumnę 0
 +
    ....
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
Dodatkowo, podczas korzystania z niestandardowych kolumn, siatki nie pozwalają na bezpośrednią modyfikację '''grids.colcount'''; dodawanie lub usuwanie kolumn powinno odbywać się za pomocą właściwości '''columns'''. Wyjaśnienie jest takie, że istnieje niespójność w stopniowym usuwaniu niestandardowych kolumn za pomocą '''ColCount''', gdy '''ColCount''' osiągnie '''FixedCols''', siatka nie ma więcej niestandardowych kolumn. Jeśli teraz zwiększymy '''ColCount''', nowa kolumna nie będzie kolumną niestandardową, ale normalną.
 +
 
 +
Obecnie nie ma planów, aby siatki wykorzystywały tylko kolumny niestandardowe.
 +
 
 +
==== Właściwość AutoFillColumns ====
 +
 
 +
Często siatki są szersze niż pozioma przestrzeń potrzebna kolumnom - pozostawia to nieprzyjemny pusty obszar po prawej stronie ostatniej kolumny. Siatki LCL zapewniają mechanizm rozszerzania szerokości określonych kolumn w taki sposób, że puste miejsce jest wypełniane automatycznie.
 +
 
 +
W tym celu należy dodać '''Columns''' zgodnie z opisem [[#W.C5.82a.C5.9Bciwo.C5.9B.C4.87_Columns|powyżej]] lub przez ustawienie właściwości <code>ColCount</code> oraz właściwości siatki <code>AutoFillColumns</code>, która musi być ustawiona na <code>True</code>. Każda kolumna ma właściwość <code>SizePriority</code>. Gdy ma wartość <code>0</code>, szerokość kolumny jest pobierana z właściwości <code>Width</code> kolumny. Ale gdy ma wartość niezerową, szerokość kolumny jest dostosowywana do średniego dostępnego rozmiaru pozostałego dla wszystkich kolumn z niezerowym <code>SizePriority</code>.
 +
 
 +
==== Procedura SaveToFile(AFileName: String) ====
 +
Zapisuje siatkę do pliku XML. Właściwość <tt>SaveOptions</tt> określa, co dokładnie ma zostać zapisane:
 +
<syntaxhighlight lang="Pascal">
 +
type
 +
  TGridSaveOptions = (
 +
    soDesign,             // Zapisz strukturę siatki (liczba kolumn/wierszy i opcje - Options)
 +
    soAttributes,        // Zapisz atrybuty siatki (czcionka - Font, pędzel - Brush, styl tekstu TextStyle)
 +
    soContent,            // Zapisz zawartość siatki (tekst w StringGrid)
 +
    soPosition            // Zapisz kursor siatki i pozycję zaznaczenia
 +
  );
 +
  TSaveOptions = set of TGridSaveOptions;
 +
</syntaxhighlight>
 +
 
 +
==== Procedura LoadFromFile(AFileName: String) ====
 +
Ładuje plik XML utworzony przez <tt>SaveToFile</tt>. Ponownie, <tt>SaveOptions</tt> określają, co dokładnie ma zostać załadowane z pliku.
 +
 
 +
=== TCustomDBGrid ===
 +
TCustomDBGrid jest podstawą dla TDBGrid.
 +
 
 +
Nie ujawniają właściwości Col i Row. Aby przejść do określonej kolumny, użyj np. właściwość SelectedIndex.
 +
 
 +
Ciekawą metodą publiczną jest AutoAdjustColumns.
 +
 
 +
==== Procedura AutoAdjustColumns ====
 +
 
 +
Ta procedura ustawia szerokość kolumny na rozmiar najszerszego znalezionego tekstu. Można go używać po załadowaniu zestawu danych/ustawieniu go jako Aktywny - Active.
 +
 
 +
Jednak w przeciwieństwie do TCustomStringGrid.AutoAdjustColumns (patrz poniżej), spowoduje to ustawienie bardzo szerokich kolumn, chyba że masz włączoną właściwość dgAutoSizeColumns.
 +
 
 +
==== Procedura InplaceEditor ====
 +
Zobacz przykład z błędu 23103 - ''i wstaw wyjaśnienie, co robi/dlaczego jest to potrzebne. Weryfikuje wartości wejściowe? Zmienia to, co jest wyświetlane?''
 +
<syntaxhighlight lang="pascal">
 +
 
 +
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
 +
var
 +
  S: String;
 +
begin
 +
  if (Key in [',','.']) then
 +
  begin
 +
    //w przeciwieństwie do Delphi nie wszystkie edytory InPlaceEditors są edytorami typu string, więc sprawdź!
 +
    if (DBGrid1.InplaceEditor is TStringCellEditor) then
 +
    begin
 +
      S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
 +
      if Pos(',',S) > 0 then
 +
        Key := #0
 +
      else
 +
        Key := ',';
 +
    end;
 +
  end;
 +
end;
 +
</syntaxhighlight>
  
 
=== TCustomStringGrid ===
 
=== 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: [[new intermediate grids|nowe pośrednie komponenty grids]], aby otrzymać więcej informacji.
+
Klasa TCustomStringGrid służy jako podstawa dla klasy TStringGrid. Może być używana dla pochodnych komponentów TStringGrid, które chcą ukryć opublikowane właściwości.
 +
 
 +
Następujące właściwości lub metody są publiczne i są również dostępne dla TStringGrid.
  
Następujące właściwości lub metody są publiczne i dostępne także w TStringGrid.
+
Zobacz pełną [[doc:lcl/grids/tcustomstringgrid.html|dokumentację TCustomStringGrid]]
  
Zobacz całe [[doc:lcl/grids/tcustomstringgrid.html|Referencje TCustomStringGrid]]
+
==== Procedura AutoSizeColumn(aCol: Integer) ====
==== 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.
 
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; ====
+
==== Procedura AutoAdjustColumns lub 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.
+
Automatycznie zmienia szerokość wszystkich kolumn, dopasowując je do najdłuższego tekstu w każdej kolumnie. Jest to szybka metoda zastosowania funkcji AutoSizeColumn() dla każdej kolumny w siatce.
  
==== procedure Clean; overload; ====
+
==== Procedura Clean; overload; ====
 
Czyści wszystkie komórki siatki, także te stałe (typu fixed).
 
Czyści wszystkie komórki siatki, także te stałe (typu fixed).
  
==== procedure Clean(CleanOptions: TGridZoneSet); overload; ====
+
==== Procedura 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 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 komórki: grid.Clean([]); (działa tak samo jak grid.clean)
Line 151: Line 223:
 
*Czyści wszystkie komórki oprócz nagłówków kolumn siatki: Grid.Clean([gzNormal, gzFixedRows]);
 
*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; ====
+
==== Procedura 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:
+
Wykonuje to samo co 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.
 
*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; ====
+
==== Procedura 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,[]);
+
Wykonuje to samo co Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), ale przy pomocy TRect zamiast wskazywania pojedynczych współrzędnych komórek. Przydatne do czyszczenia zaznaczonego obszaru: grid.Clean(Grid.Selection,[]);
 +
 
 +
==== Procedura SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
 +
 
 +
Zapisuje zawartość siatki w pliku w formacie tekstowym, gdzie wartości są rozdzielone przecinkami (CSV) (dodanym w Lazarus r32179).
 +
 
 +
Argument AFilename określa nazwę pliku, w którym zostanie zapisana zawartość. Jeśli plik istnieje, zawartość zostanie nadpisana. Jeśli nie istnieje, plik zostanie utworzony.
 +
 
 +
ADelimiter (argument opcjonalny) jest używany do dostarczenia niestandardowego separatora, jeśli jest to wymagane. Domyślnie tworzony jest format CSV (tzn. ADelimiter:=',';) dla pliku rozdzielanego tabulatorami ADelimiter powinien mieć wartość #9.
 +
 
 +
Parametr WithHeader służy do określenia, czy „nagłówek wiersza” powinien zostać uwzględniony, czy nie. Nagłówek wiersza to lista nazw pól na początku pliku wyjściowego; jego zawartość pochodzi z ostatniego stałego (Fixed) wiersza w siatce. Istnieje wyjątek od tej reguły: jeśli siatka zawiera niestandardowe kolumny, zawartość nagłówka wiersza pochodzi z niestandardowych tytułów kolumn, a nie ze stałych komórek wiersza.
 +
 
 +
Jeśli WithHeader ma wartość True, a siatka nie zawiera stałego wiersza ani niestandardowych kolumn, zawartość nagłówka wiersza zostanie pobrana z pierwszego wiersza w siatce.
 +
 
 +
Normalne wyjście danych CSV rozpocznie się od pierwszego niestałego (Non-Fixed) wiersza w siatce.
 +
 
 +
==== Procedura LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true); ====
 +
 
 +
Wczytuje zawartość siatki z pliku w formacie CSV (dodanym w Lazarus r32179).
 +
 
 +
Kolumny będą dodawane lub usuwane do lub z siatki w zależności od potrzeb, zgodnie z liczbą pól zawartych w każdym wierszu pliku CSV. Wczytanie pliku CSV nie zmieni liczby stałych (Fixed) wierszy, które już istniały w siatce.
 +
 
 +
Argument ''AFileName'' określa nazwę pliku źródłowego z zawartością CSV.
 +
 
 +
Opcjonalny parametr ''ADelimiter'' może być użyty do określenia innego separatora lub ogranicznika. Przykład: dla pliku rozdzielanego tabulatorami, ''ADelimiter'' powinno mieć wartość #9. Innym popularnym formatem pliku jest plik rozdzielany średnikami, gdzie ''ADelimiter'' powinien być ;
 +
 
 +
Parametr ''WithHeader'' służy do określenia, czy pierwszy wiersz w pliku CSV powinien być traktowany jako „wiersz nagłówka”, czy nie. Jeśli siatka ma ustalone wiersze i ''WithHeader'' ma wartość True, nagłówki kolumn dla ostatniego stałego (Fixed) wiersza zostaną pobrane z wiersza nagłówka. Należy jednak pamiętać, że jeśli siatka zawiera niestandardowe kolumny, wówczas wiersz nagłówka będzie używany jako źródło dla tytułów kolumn, a niestandardowe tytuły kolumn są zawsze wyświetlane w pierwszym stałym wierszu siatki lub ukryte, jeśli w siatce nie ma ustalonych wierszy.
 +
 
 +
Jeśli procedura ''LoadFromCSVFile'' ma trudności z załadowaniem pliku CSV (np. cudzysłowy lub spacje są nieprawidłowo interpretowane), możesz ręcznie załadować siatkę za pomocą np. [[CsvDocument]]... i oczywiście zawsze mile widziana jest łatka dla ''LoadFromCSVFile''.
  
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====
+
==== Właściwość 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.
 
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łady =====
*Przykład wstawiania: Wstawia treść do trzeciej kolumny siatki z kontrolki ListBox:
+
* Przykład wstawiania: Wstawia treść do trzeciej kolumny siatki z kontrolki ListBox:
Grid.Cols[2] := ListBox1.Items;
+
<syntaxhighlight lang="pascal">
*Przykład pobrania: Wstawia treść kontrolki ListBox z kolumny siatki o indeksie 4:
+
Grid.Cols[2] := ListBox1.Items;
procedure TForm1.FillListBox1;
+
</syntaxhighlight>
var  
+
 
  StrTempList: TStringList;
+
* Przykład pobrania: Wstawia treść kontrolki ListBox z kolumny siatki o indeksie 4:
begin
+
<syntaxhighlight lang="pascal">
  StrTempList := TStringList(Grid.Cols[4]);
+
procedure TForm1.FillListBox1;
  if StrTempList<>nil then begin
+
var  
    ListBox1.Items.Assign(StrTempList);
+
  StrTempList: TStringList;
    StrTempList.Free;
+
begin
  end;
+
  StrTempList := TStringList(Grid.Cols[4]);
end;  
+
  if StrTempList<>nil then begin
 +
    ListBox1.Items.Assign(StrTempList);
 +
    StrTempList.Free;
 +
  end;
 +
end;</syntaxhighlight>
 +
 
 
===== Uwagi =====
 
===== Uwagi =====
 
Ta właściwość działa inaczej w Lazarusie i w Delphi, gdy pobieramy dane z siatki.
 
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.
 
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.
+
Oznacza to także, że zmiany wykonywane w tej zwróconej liście ciągów string nie będą miały żadnego wpływu na zawartość siatki lub układu.
  
 
Zobacz powyższy przykład pobrania.
 
Zobacz powyższy przykład pobrania.
  
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====
+
==== Właściwość 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.  
+
Pobierz/ustaw listę ciągów string z/do wiersza siatki o danym indeksie, począwszy od indeksu kolumny 0 do kolumny ColCount-1.
===== Notes. =====
+
===== Uwagi =====
This property works differently in Lazarus and in Delphi when getting the data from the grid.  
+
Ta właściwość działa inaczej w Lazarusie i Delphi podczas pobierania danych z siatki.
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.  
+
W Lazarusie tworzony jest tymczasowy obiekt typu TStringList do pobierania zawartości wiersza. Obowiązkiem użytkownika jest zwolnienie tego obiektu po użyciu.
  
This means also that changes in the returned list will not affect the grid's content or layout.  
+
Oznacza to również, że jakiekolwiek zmiany w tej zwróconej liście ciągów string nie wpłyną na zawartość ani układu, ani siatki.
  
===== Examples =====
+
===== Przykłady =====
*Set Example: Set the content of the third row in the grid from a ListBox:
+
* Przykład ustawiania: Ustaw zawartość trzeciego wiersza siatki jako zawartość ListBox:
Grid.Rows[2] := ListBox1.Items;
+
<syntaxhighlight lang="Pascal">Grid.Rows[2] := ListBox1.Items;</syntaxhighlight>
*Get Example: Set the content of a Listbox from the grid's row index 4:
+
 
procedure TForm1.FillListBox1;
+
* Przykład pobrania: Ustaw zawartość pola listy z 4 indeksu wiersza siatki:
var  
+
<syntaxhighlight lang="pascal">
  StrTempList: TStringList;
+
procedure TForm1.FillListBox1;
begin
+
var  
  StrTempList := TStringList(Grid.Rows[4]);
+
  StrTempList: TStringList;
  if StrTempList<>nil then begin
+
begin
    ListBox1.Items.Assign(StrTempList);
+
  StrTempList := TStringList(Grid.Rows[4]);
    StrTempList.Free;
+
  if StrTempList<>nil then begin
  end;
+
    ListBox1.Items.Assign(StrTempList);
end;
+
    StrTempList.Free;
*An Example that doesn't work, and its Fix: Retrieved string list is read only
+
  end;
  // this will not work and will cause memory leak
+
end;
  // because returned StringList is not being freed
+
</syntaxhighlight>
 +
* Przykład, który nie działa, oraz jego poprawka: Pobrana lista ciągów jest tylko do odczytu
 +
<syntaxhighlight lang="pascal">
 +
  // te dwa przykłady nie zadziałają i spowodują wycieki pamięci,
 +
  // ponieważ zwrócone listy ciągów nie są zwalniane
 
  Grid.Rows[1].CommaText := '1,2,3,4,5';
 
  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';  
 
  Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g';  
 
   
 
   
  // fixing the first case
+
  // naprawienie pierwszego przypadku
 
  Lst:=TStringList.Create;
 
  Lst:=TStringList.Create;
 
  Lst.CommaText := '1,2,3,4,5';
 
  Lst.CommaText := '1,2,3,4,5';
 
  Grid.Rows[1] := Lst;
 
  Grid.Rows[1] := Lst;
  Lst.Free;
+
  Lst.Free;</syntaxhighlight>
  
==== property UseXORFeatures; ====
+
==== Właściwość UseXORFeatures ====
Boolean property, default value: False;
+
Właściwość typu Boolean, wartość domyślna: 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]]
+
Ta właściwość kontroluje wygląd kropkowanego prostokąta fokusu w siatce (pokazuje się, gdy klikniemy myszką na jakiejś komórce). Gdy ma wartość True, prostokąt jest malowany przy użyciu operacji rastrowej XOR. Dzięki temu możemy zobaczyć prostokąt fokusu bez względu na kolor tła komórek. Gdy ma wartość False, użytkownik może kontrolować kolor kropkowanego prostokąta fokusu za pomocą [[FocusColor property|właściwości FocusColor]]
  
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.
+
Kontroluje również wygląd zmiany rozmiaru kolumny/wiersza. Gdy ma wartość True, linia pokazuje wizualnie rozmiar kolumny lub wiersza, jeśli użytkownik zakończy operację. Gdy ma wartość False, zmiana rozmiaru kolumny lub wiersza zaczyna obowiązywać, gdy użytkownik przeciąga myszą.
  
== Grids Howto ==
+
===TValueListEditor===
=== Customizing grids ===
+
[[TValueListEditor]] to kontrolka (formant) pochodzący z TCustomStringGrid i służy do edycji par typu klucz-wartość.
Grid are components derived from the [http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html 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 ===
+
====Właściwość DisplayOptions====
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:
+
Kontroluje różne aspekty wyglądu TValueListEditor.
*'''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.<br>
 
*'''TitleFont.''' Font used to draw the text in fixed cells.<br>
 
*'''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.<br>
 
*'''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.
+
====Właściwość TitleCaptions====
*'''goFixedVertLine, goFixedHorzLine''' it draws a vertical or horizontal line respectively delimiting cells or columns in fixed area, active by default.
+
Ustawia wartości dla tytułów kolumn (jeśli właściwość DisplayOptions zawiera wartość doColumnTitles).
*'''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.
+
Jeśli DisplayOptions nie ma wartości doColumnTitles, używane są domyślne tytuły.
*'''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)
+
====Właściwość Strings====
 +
Zapewnia dostęp do listy ciągów zawierających pary klucz-wartość.<br>
 +
Pary klucz-wartość muszą mieć format typu:<br>
 +
'NazwaKlucza=Wartość'
  
=== Description of grid's drawing process ===
+
====Właściwość ItemProps====
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.  
+
Możesz użyć tej właściwości, aby kontrolować sposób edytowania elementów w kolumnach „Value” (Wartość).<br>
 +
Jest to kontrolowane przez ustawienie właściwości EditStyle i ReadOnly elementu ItemProp.
  
The process is as follow:
+
====Właściwość KeyOptions====
*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.  
+
KeyOptions to zbiór wartości porządkowych TKeyOptions kontrolujących, czy użytkownik może modyfikować zawartość kolumny „Key” (Klucz).
*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.
+
*KeyEdit: użytkownik może edytować nazwę klucza
*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:
+
*KeyAdd: użytkownik może dodawać klucze (przez wciśnięcie klawisza Insert w siatce). KeyAdd wymaga KeyEdit.
**''gdSelected'' The cell will have a selected look.
+
*KeyDelete: użytkownik może usuwać pary klucz-wartość (przez naciśnięcie kombinacji Ctrl+Delete).
**''gdFocused'' The cell will have a focused look.
+
*KeyUnique: jeśli ustawione, klucze muszą mieć unikalne nazwy. Próba wprowadzenia zduplikowanego klucza spowoduje zgłoszenie wyjątku.
**''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===
+
====Właściwość DropDownRows====
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.
+
Jeśli edytor komórek jest listą wyboru (ValueEdit1.ItemProps['key1'].EditStyle=esPickList), to ta właściwość ustawia DropDownCount, czyli ilość wierszy wyświetlanej listy. Wartość domyślna to 8.
  
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.
+
====Funkcja DeleteRow====
 +
Usuwa parę Klucz-Wartość wiersza o podanym indeksie, całkowicie usuwając wiersz.
  
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.  
+
====Funkcja InsertRow====
 +
Wstawia wiersz w siatce i ustawia parę Klucz-Wartość. Zwraca indeks nowo wstawionego wiersza.
  
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).
+
====Funkcja IsEmptyRow====
 +
Zwraca wartość True, jeśli komórki indeksowanego wiersza są puste (Keys[aRow]=''; Values[aRow]='').
  
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.
+
====Funkcja FindRow====
 +
Zwraca indeks wiersza, który ma określoną nazwę klucza.
  
===Several differences with Delphi have been identified===
+
====Funkcja RestoreCurrentRow====
*In Lazarus TCustomGrid.DrawCell method is not abstract and its default implementation does basic cell background filling.
+
Cofa edycję w bieżącym wierszu (jeśli edytor jest nadal aktywny). Dzieje się tak, gdy użytkownik naciśnie klawisz Escape.
*In Delphi, the cell's text is drawn before entering the OnDrawCell event (see [http://www.freepascal.org/mantis/view.php?id=9619 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 ===
+
====Zmienione zachowanie niektórych właściwości pochodzących z TCustomStringGrid====
Derived grids usually have to override following methods:<br>
+
 
DrawAllRows: Draws all visible rows.<br>
+
=====Właściwość Options=====
DrawRow: Draw All Cells in a Row.<br>
+
Ze względu na charakter TValueListEditor jego właściwość Options ma pewne ograniczenia
DrawRow draws all cells in the row by first checking if cell is within clipping region, and only draws the cell if it is.<br>
+
*goColMoving nie jest dozwolony w opcjach (nie można go ustawić).
 +
*goAutoAddRows można ustawić tylko wtedy, gdy KeyAdd jest włączony w KeyOptions. Ustawienie KeyAdd automatycznie ustawi goAutoAddRows.
 +
*goAutoAddRowsSkipContentCheck jest niedozwolone (na razie powoduje awarię w TValueListeditor: wymaga naprawy).
 +
 
 +
=====Właściwość FixedRows=====
 +
Może mieć wyłącznie wartość równą 1 (pokaż tytuły kolumn) lub 0 (nie pokazuj tytułów kolumn).
 +
 
 +
=====Właściwość ColCount=====
 +
Może mieć wyłącznie wartość równą 2.
 +
 
 +
=====Właściwość Objects=====
 +
Uwaga! Nie zaleca się używania tej właściwości w ogóle.<br>
 +
Kiedy jakiś (wewnętrzny) kod manipuluje właściwością Strings, zmiany dla Objects zostaną utracone.
 +
 
 +
====Ogólne uwagi dotyczące korzystania z TValueListEditor====
 +
Podczas manipulowania zawartością ValueListEditor (siatki) można bezpośrednio manipulować podstawową właściwością Strings.<br>
 +
Pamiętaj jednak, że jeśli to zrobisz, <b>musisz</b> ukryć edytor, w przeciwnym razie ciągi mogą nie być zsynchronizowane z zawartością siatki, którą widzisz na ekranie.<br><br>
 +
Jeśli chcesz wstawić lub usunąć wiersze itp., preferowanym sposobem jest użycie metod publicznych z TValueListEditor: DeleteRow(), InsertRow(), MoveRow(), ExchangeRow() lub różnych metod Sort().<br><br>
 +
Próba użycia metod z klasy przodka do manipulowania wierszami lub kolumnami (np. Columns.Add) może spowodować awarię.
 +
 
 +
====Zapisywanie i ładowanie zawartości TValueListEditor====
 +
 
 +
=====Korzystanie z właściwości Strings=====
 +
Jeśli chcesz tylko zapisać i załadować zawartość TValueListEditor (np. par Klucz/Wartość, a nie układ), możesz użyć metod Strings.SaveToFile i Strings.LoadFromFile.<br>
 +
W przypadku LoadFromFile właściwość <tt>RowCount</tt> zostanie automatycznie dostosowana.<br>
 +
Również w przypadku LoadFromFile nie są wykonywane żadne kontrole poprawności (więc wiersze, które nie reprezentują pary Klucz/Wartość, zostaną zakończone jako klucze bez wartości).
 +
 
 +
=====Korzystanie z TValueList.SaveToFile i TValueList.LoadFromFile=====
 +
When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.<br>
 +
SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.
 +
<br>
 +
<br>
 +
In contrast to it's ancestors TValueList.LoadFromFile performs sanity checks on the file it tries to read. In particular RowCount must be specified and there cannot be a cell with RowIndex&nbsp;>&nbsp;RowCount or ColumnIndex other than 0 or 1. <br>
 +
If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.<br>
 +
<br>
 +
TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.
 +
 
 +
Korzystając z funkcji SaveToFile i LoadFromFile z TValueList, zyskujesz dodatkowe korzyści związane z możliwością zapisywania i ładowania układu, podobnie jak w przypadku innych siatek.<br>
 +
SaveToFile zapisze również informacje o tym, czy TValueListEditor używa ColumnTitles, a jeśli tak, zapisuje je.
 +
<br>
 +
<br>
 +
W przeciwieństwie do swoich przodków, TValueList.LoadFromFile sprawdza poprawność pliku, który próbuje odczytać. W szczególności właściwość RowCount musi być określona i nie może istnieć komórka z RowIndex > RowCount lub ColumnIndex innym niż 0 lub 1.<br>
 +
Jeśli plik nie wydaje się być prawidłowym plikiem siatki TValueListEditor, zgłaszany jest wyjątek.<br>
 +
<br>
 +
TValueList.SaveToFile i TValueList.LoadFromFile nie działają poprawnie w wersjach Lazarusa <= 2.1 r62044.
 +
=====Korzystanie z metod SaveToCSVFile i LoadFromCSV=====
 +
Te metody <b>nie</b> powinny być obecnie używane, ponieważ w przypadku TValueListEditor są one wadliwe.
 +
 
 +
== Praca z siatkami ==
 +
=== Dostosowywanie siatek ===
 +
Komponenty Grid pochodzą z klasy [http://lazarus-ccr.sourceforge.net/docs/lcl/controls/tcustomcontrol.html TCustomControl] i nie mają skojarzonego z nią natywnego widżetu, co oznacza, że ​​siatki nie są ograniczone przez wygląd aktualnego motywu interfejsu. Może to być zarówno zaletą, jak i wadą: zwykle programiści chcą stworzyć aplikację o jednolitym wyglądzie. Dobrą wiadomością jest to, że siatki Lazarusa są wystarczająco elastyczne, aby uzyskać coś z obu światów; programiści mogą łatwo sprawić, by siatki wyglądały podobnie do innych natywnych kontrolek, lub mogą dostosować siatkę w najdrobniejszych szczegółach, aby uzyskać prawie taki sam wygląd na dowolnej platformie lub interfejsie widżetów (tzn. z wyjątkiem pasków przewijania, ponieważ ich wygląd jest wciąż zdeterminowany aktualnym motywem).
 +
 
 +
=== Właściwości i Zdarzenia dostosowujące siatek ===
 +
Niektóre właściwości mogą wpływać na wygląd siatki, działając, gdy komórka ma zostać pomalowana w zdarzeniu PrepareCanvas/OnPrepareCanvas, zmieniając domyślne właściwości płótna (canvas), takie jak kolor pędzla lub czcionka. Poniżej znajduje się lista takich właściwości:
 +
 
 +
*'''AlternateColor.''' Dzięki tej właściwości użytkownik może zmienić kolor tła pojawiający się w naprzemiennych wierszach. Ma to na celu umożliwienie łatwiejszego odczytania danych z wierszy siatki przez użytkownika.
 +
*'''Color.''' Ta właściwość ustawia podstawowy kolor używany do rysowania tła niestałych (Non-Fixed) komórek.
 +
*'''FixedColor.''' Jest to kolor używany do rysowania tła dla stałych komórek (Fixed).
 +
*'''Flat.''' Ta właściwość wyłącza trójwymiarowy wygląd stałych komórek.<br>
 +
*'''TitleFont.''' Czcionka używana do rysowania tekstu w stałych komórkach.<br>
 +
*'''TitleStyle.''' Ta właściwość zmienia wygląd 3D stałych komórek, istnieją 3 ustawienia:
 +
**''tsLazarus.'' To jest domyślny wygląd preferowany przez Lazarusa
 +
**''tsNative.'' Ta wartość próbuje ustawić wygląd zgodny z bieżącym motywem zestawu widżetów.
 +
**''tsStandard.'' Ten styl jest bardziej kontrastowy, podobnie jak siatki Delphi.
 +
*'''AltColorStartNormal.''' Wartość logiczna. Jeśli jest True: alternatywny kolor jest zawsze w drugim rzędzie po stałych wierszach, pierwszy wiersz po stałych wierszach będzie zawsze w domyślnym kolorze. Jeśli jest False: domyślny kolor jest ustawiany na pierwszym wierszu, tak jakby nie było żadnych stałych wierszy.
 +
*'''BorderColor.''' Ustawia kolor obramowania siatki używany, gdy Flat:=True i BorderStyle:=bsSingle.
 +
*'''EditorBorderStyle.''' Jeśli ustawione na bsNone w systemie Windows, to edytory komórek nie będą miały obramowania, tak jak w Delphi. Ustaw domyślnie na bsSingle, ponieważ obramowanie może być specyficzne dla motywu w niektórych zestawach widżetów i umożliwiać jednolity wygląd.<br>
 +
*'''FocusColor.''' Kolor używany do rysowania komórki, na której aktualnie ustawione jest skupienie (fokus), jeśli UseXORFeatures nie jest ustawiony, domyślnie jest to clRed.
 +
*'''FocusRectVisible.''' Włącza/wyłącza rysowanie skupionej komórki.
 +
*'''GridLineColor.''' Kolor linii siatki w obszarze niestałych komórek.
 +
*'''GridLineStyle.''' Styl pióra używany do rysowania linii w obszarze niestałych komórek, możliwe wybory to: ''psSolid'', ''psDash'', ''psDot'', ''psDashDot'', ''psDashDotDot'', ''psinsideFrame'' , ''psPattern'', ''psClear''. Domyślnie jest to ''psSolid''.
 +
*'''SelectedColor.''' Kolor używany do rysowania tła komórki na wybranych, zaznaczonych komórkach.
 +
*'''UseXORFeatures.''' Jeśli ustawiony jest na True, to prostokąt skupienia (focus) jest rysowany w trybie XOR, więc powinien być widoczny w połączeniu z dowolnym kolorem tła komórki. Wpływa również na wygląd przesuwanych kolumn.
 +
*'''DefaultDrawing.''' Wartość logiczna. Zwykle siatki przygotowują płótno (canvas) siatki przy użyciu pewnych właściwości zgodnie z rodzajem malowanej komórki. Jeśli użytkownik napisze własną procedurę obsługi zdarzeń OnDrawCell, ustawiony DefaultDrawing również maluje tło komórki. Jeśli użytkownik sam rysuje komórkę, lepiej wyłączyć tę właściwość, aby malowanie się nie duplikowało. W StringGrid ustawiony DefaultDrawing rysuje również tekst w każdej komórce.
 +
*'''AutoAdvance.''' Determinuje to, gdzie ma zostać przeniesiony kursor komórki po naciśnięciu klawisza Enter lub po zakończeniu edycji.
 +
*'''TabAdvance.''' Determinuje to, gdzie ma zostać przeniesiony kursor komórki po naciśnięciu klawisza Tab or Shift-Tab.
 +
*'''ExtendedColSizing.''' Jeśli ma wartość True, użytkownik może zmienić rozmiar kolumn nie tylko w nagłówkach, ale wzdłuż ich wysokości.
 +
 
 +
Inne właściwości, które również wpływają na wygląd siatek.
 +
 
 +
'''Options'''.
 +
:Właściwość Options to zestaw zawierający kilka elementów umożliwiających różnorodną funkcjonalność, ale niektóre są bezpośrednio związane z wyglądem siatki. Te opcje można ustawić w czasie projektowania lub w czasie wykonywania.
 +
*'''goFixedVertLine, goFixedHorzLine''' Rysuje pionową lub poziomą linię, odpowiednio oddzielającą komórki lub kolumny w obszarze stałych komórek, domyślnie aktywne.
 +
*'''goVertLine, goHorzLine''' Tak samo jak poprzednio, ale dla normalnego obszaru do przeglądania. Można stworzyć siatkę, która będzie symulować pole listy (ListBox), wyłączając oba te elementy.
 +
*'''goDrawFocusSelected''' Jeśli ten element jest włączony, tło zaznaczenia jest malowane także w skupionej (fixed) komórce oprócz zaznaczonego kropkowanego prostokąta (pamiętaj, że to nie działa jeszcze, gdy ustawiona jest opcja goRowSelect, w takim przypadku wiersz jest zawsze malowany tak, jakby ustawiono goDrawFocusSelected).
 +
*'''goRowSelect''' Wybierz do zaznaczania cały wiersz zamiast pojedynczych komórek.
 +
*'''goFixedRowNumbering''' Jeśli ustawione, siatka wykona automatyczną numerację wierszy w pierwszej stałej kolumnie.
 +
*'''goHeaderHotTracking''' Jeśli jest ustawione, siatka będzie próbowała pokazać inny wygląd komórki, gdy kursor myszy znajdzie się nad dowolną stałą komórką. Aby to zadziałało, żądana strefa komórki musi być włączona za pomocą właściwości HeaderHotZones. Spróbuj połączyć tę opcję z właściwością TitleStyle:=tsNative, aby uzyskać wygląd, który na bieżąco śledzi wygląd motywu widżetów.
 +
*'''goHeaderPushedLook''' Jeśli jest ustawione, ten element włącza wygląd wciśnięcia po kliknięciu dowolnej stałej komórki. Strefa komórek, które mogą być „wciskane” jest włączana za pomocą właściwości HeaderPusedZones.
 +
 
 +
(dopisz więcej)
 +
 
 +
=== Opis procesu rysowania siatki ===
 +
Podobnie jak inne niestandardowe kontrolki, siatka jest rysowana metodą malowania. Ogólnie rzecz biorąc, siatkę rysuje się malując wszystkie wiersze, a każdy wiersz malując poszczególne komórki.
 +
 
 +
Proces ten wygląda następująco:
 +
*Najpierw określany jest obszar widocznych komórek: każdy wiersz jest testowany, aby sprawdzić, czy krzyżuje się z granicą płótna (canvas); jeśli wszystko jest w porządku, widoczny obszar jest malowany przez rysowanie kolumn każdego wiersza.
 +
*Wartości kolumn i wierszy służą do identyfikacji komórki, która ma zostać pomalowana, a każda kolumna jest ponownie testowana pod kątem krzyżowania się z granicą płótna; jeśli wszystko jest w porządku, niektóre dodatkowe właściwości, takie jak prostokątny zasięg komórki i stan wizualny, są przekazywane jako argumenty do metody DrawCell.
 +
*W trakcie procesu rysowania stan wizualny każdej komórki jest dostosowywany zgodnie z opcjami siatki i położeniem w siatce. Stan wizualny zachowany jest w zmiennej typu TGridDrawState, która jest zbiorem składającym się z następujących elementów:
 +
**''gdSelected'' Komórka będzie miała wygląd wybranej, zaznaczonej.
 +
**''gdFocused'' Komórka będzie miała wygląd skupionej (focus).
 +
**''gdFixed'' Komórka musi być pomalowana jak komórka stała.
 +
**''gdHot'' Mysz jest nad tą komórką, więc pomaluj ją z bieżącym podświetleniem
 +
**''gdPushed'' Komórka jest klikana, więc pomaluj ją jako wciśniętą
 +
 
 +
*'''DrawCell.''' Metoda DrawCell jest wirtualna i może zostać zastąpiona w siatkach potomnych w celu wykonania niestandardowego rysowania. Informacje przekazywane do DrawCell pomagają zidentyfikować konkretną komórkę, która jest malowana, fizyczny obszar zajmowany na ekranie i widoczny status. Zobacz odwołanie do DrawCell, aby uzyskać szczegółowe informacje. Dla każdej komórki wykonywane są następujące operacje:
 +
*'''PrepareCanvas.''' W tej metodzie, jeśli ustawiona jest właściwość DefaultDrawing, płótno siatki jest skonfigurowane z domyślnymi właściwościami pędzla i czcionki na podstawie bieżącego stanu wizualnego. W przypadku kilku właściwości wyglądu i środowiska wykonawczego wyrównanie tekstu jest ustawiane tak, aby było zgodne z wyborem programisty w niestandardowych kolumnach, jeśli takie istnieją. Jeśli DefaultDrawing ma wartość False, kolor pędzla jest ustawiony na clWindow, a kolor czcionki na clWindowText, wyrównanie tekstu jest ustawiane za pomocą wartości właściwości siatek defaultTextStyle.
 +
*'''OnPrepareCanvas.''' Jeśli programista napisał obsługę zdarzenia dla zdarzenia OnPrepareCanvas, jest on wywoływany w tym momencie. To zdarzenie może służyć do wykonywania prostych dostosowań, takich jak zmiana koloru tła komórki, właściwości czcionki, takie jak kolor, czcionka i styl, układ tekstu, na przykład różne kombinacje wyrównania do lewej, do środka, do góry, do dołu, do prawej itp. Wszelkie zmiany wprowadzone na płótnie w tym zdarzeniu zostałoby utracone, ponieważ następujące potem rysowanie komórki ponownie zresetuje obszar roboczy do stanu domyślnego. Więc bezpieczniej jest robić zmiany tylko dla konkretnej komórki lub komórek i zapomnieć o pozostałych. Użycie tego zdarzenia czasami pomaga uniknąć użycia zdarzenia siatki OnDrawCell, w którym użytkownicy byliby zmuszeni do powielenia kodu rysowania siatki. Do zrobienia: informacje o tym, co można zrobić tutaj, a co zostawić dla zdarzenia OnDrawCell?...
 +
*'''OnDrawCell.''' Następnie, jeśli nie określono obsługi zdarzenia OnDrawCell, siatka wywołuje metodę DefaultDrawCell, która po prostu maluje tło komórki przy użyciu bieżącego koloru i stylu pędzla płótna. Jeśli istnieje procedura obsługi OnDrawCell, siatka najpierw maluje tło komórki, ale tylko wtedy, gdy ustawiono właściwość DefaultDrawing, a następnie wywołuje zdarzenie OnDrawCell w celu wykonania niestandardowego malowania komórek. Zwykle programiści chcą wykonywać niestandardowe rysowanie tylko dla określonych komórek, ale standardowe rysowanie dla innych; w tym przypadku mogą ograniczyć niestandardową operację do określonej komórki lub komórek, zaglądając do argumentów ACol, Arow i AState, a dla innych komórek po prostu wywołując metodę DefaultDrawCell, aby pozwolić zająć się tym siatce.
 +
*'''Text.''' W tym momencie (tylko dla TStringGrid), jeśli właściwość DefaultDrawing ma wartość True, malowana jest zawartość tekstowa komórki.
 +
*'''Grid lines'''. Ostatnim krokiem dla każdej komórki jest malowanie linii siatki: jeśli określono opcje siatki goVertLine, goHorzLine, goFixedVertLine i goFixedHorzLine, siatka komórek jest rysowana w tym miejscu. Siatki zawierające tylko wiersze lub tylko kolumny można uzyskać, zmieniając te opcje. Jeśli programista wybrał wygląd „motywu” (tsNative), jest to również robione w tym momencie (zobacz właściwość TitleStyle).
 +
*'''FocusRect.''' Po pomalowaniu wszystkich kolumn bieżącego wiersza nadszedł czas na narysowanie prostokąta skupienia (focus) dla aktualnie zaznaczonej komórki lub dla całego wiersza, jeśli ustawiono opcję goRowSelect.
 +
 
 +
====Różnice w porównaniu z Delphi====
 +
*W Lazarusie metoda TCustomGrid.DrawCell nie jest abstrakcyjna i jej domyślna implementacja wykonuje podstawowe wypełnianie tła komórki.
 +
*W Delphi tekst komórki jest rysowany przed wejściem do zdarzenia OnDrawCell (patrz [http://www.freepascal.org/mantis/view.php?id=9619 raport o błędzie #9619]).
 +
 
 +
===Wybór komórki w siatce===
 +
Lokalizację bieżącej (skupionej (focused)) komórki (lub wiersza) siatki można zmienić za pomocą klawiatury, myszy lub kodu. Aby skutecznie zmienić skupienie z jednej komórki na inną, musimy przetestować pozycję docelową, aby sprawdzić, czy może ona otrzymać skupienie na komórce. W przypadku korzystania z klawiatury właściwość część procesu wykonuje AutoAdvance, znajdując następną komórkę, na której ma wykonać skupienie. Podczas korzystania z kliknięć myszą lub poruszania się po kodzie, skupienie nie zostanie przeniesione z bieżącej komórki, chyba że komórka docelowa może takie skupienie otrzymać.
 +
 
 +
Siatka wywołuje funkcję SelectCell, aby sprawdzić, czy na komórce można wykonać skupienie: jeśli ta funkcja zwraca wartość True, to znaczy, że na komórce docelowej, zidentyfikowanej za pomocą argumentów aCol i aRow jest możliwe wykonanie skupienia (bieżąca implementacja TCustomGrid po prostu zwraca True). TCustomDrawGrid, a zatem także TDrawGrid i TStringGrid przesłaniają tę metodę, aby najpierw sprawdzić, czy komórka jest szersza niż 0; normalnie nie chcesz, aby wybrana została komórka o szerokości 0, więc komórka z tymi cechami jest automatycznie pomijana w procesie wyszukiwania odpowiedniej komórki. Inną rzeczą, jaką robi zastąpiona metoda SelectCell, jest wywołanie konfigurowanego przez użytkownika zdarzenia OnSelectCell: to zdarzenie odbiera współrzędne komórki jako argumenty i zawsze zwraca w wyniku domyślną wartość True.
 +
 
 +
Gdy wiadomo, że komórka może uzyskać skupienie i jesteśmy pewni, że nastąpi ruch, najpierw wywoływana jest metoda BeforeMoveSelection; to z kolei wyzwala zdarzenie OnBeforeSelection. Argumentami tej metody są współrzędne nowej komórki ze skupieniem. W tym momencie każdy widoczny edytor jest również ukryty. Słowo „before” (przed) oznacza, że ​​zaznaczenie nie zostało jeszcze zmienione, a dostęp do aktualnych współrzędnych komórki skupionej można uzyskać za pomocą właściwości grid.Col i grid.Row.
 +
 
 +
Następnie wewnętrzne współrzędne skupionej komórki są zmieniane, a potem wywoływana jest metoda MoveSelection; celem tej metody jest wyzwolenie zdarzenia OnSelection, jeśli jest ustawione (jest to powiadomienie, że skupiona komórka już się zmieniła, a współrzędne komórki są teraz dostępne we właściwościach grid.row i grid.col).
 +
 
 +
Należy zauważyć, że nie jest dobrze używać zdarzenia OnSelectCell do wykrywania zmian skupienia komórki, ponieważ to zdarzenie zostanie wyzwolone kilka razy nawet dla tej samej komórki w procesie wyszukiwania odpowiedniej komórki. Lepiej jest użyć do tego celu zdarzeń OnBeforeSelection lub OnSelection.
 +
 
 +
====Różnice w porównaniu z Delphi====
 +
* Zachowanie SelectCell i OnSelectCell jest prawdopodobnie inne — nie można tak naprawdę komentować różnic. W Lazarusie są one używane w funkcjonalności takiej jak AutoAdvance, która, o ile wiem, nie istnieje w Delphi.
 +
 
 +
=== Gdy powyższe dostosowanie nie wystarczy, siatki pochodne ===
 +
Siatki pochodne zazwyczaj muszą zastąpić następujące metody:<br>
 +
DrawAllRows: Rysuje wszystkie widoczne wiersze.<br>
 +
DrawRow: Rysuje wszystkie komórki w rzędzie.<br>
 +
DrawRow rysuje wszystkie komórki w rzędzie, najpierw sprawdzając, czy komórka znajduje się w obszarze przycinania, i rysuje tylko komórkę, jeśli tak jest.<br>
 
DrawCell:<br>
 
DrawCell:<br>
 
DrawCellGrid:<br>
 
DrawCellGrid:<br>
 
DrawCellText:<br>
 
DrawCellText:<br>
 
DrawFocusRect:<br>
 
DrawFocusRect:<br>
(write me).<br>
+
(napisz do mnie).<br>
 +
 
 +
=== Co dzieje się w metodzie <tt>TCustomGrid.Paint</tt>? ===
 +
Poniższa lista pokazuje wewnętrzną kolejność wywołań metod dla malowania <tt>TCustomGrid</tt> (lub potomka). Każda pozycja na listach reprezentuje wywołania metod podczas operacji malowania. Powinno to pomóc w znalezieniu właściwego punktu do zmiany zachowania, jeśli chodzi o tworzenie klas potomnych z <tt>TCustomGrid</tt>.
 +
 
 +
*'''<tt>DrawEdges</tt>''': Rysuje zewnętrzną granicę siatki.
 +
*'''<tt>DrawAllRows</tt>''' (virtual): Rysuje wszystkie wiersze w widocznej części siatki. Jest to jedyna metoda wywoływana w procesie malowania, która jest zadeklarowana jako ''<tt>virtual</tt>'' (wirtualna).
 +
**'''<tt>DrawRow</tt>''' (virtual): Jest wywoływana dla każdego wiersza wewnątrz obecnego widoku.
 +
***'''<tt>DrawCell</tt>''' (virtual): Jest najpierw wywoływana dla każdej „normalnej” (tj. niestałej) komórki w rzędzie.
 +
****'''<tt>PrepareCanvas</tt>''' (virtual): Ustawia style rysowania na płótnie zgodnie z wizualnymi właściwościami bieżącej komórki.
 +
****'''<tt>DrawFillRect</tt>''': Rysuje tło komórki ze stylami ustawionymi w <tt>PrepareCanvas</tt>.
 +
****'''<tt>DrawCellGrid</tt>''' (virtual): Rysuje linie obramowania komórki.
 +
***'''<tt>DrawFocusRect</tt>''' (virtual): W <tt>TCustomGrid</tt> ta metoda nic nie robi. W siatkach potomnych ta metoda jest używana do rysowania prostokąta skupienia wewnątrz aktywnej komórki.
 +
***'''<tt>DrawCell</tt>''' (virtual): (patrz powyżej) Jest wywoływana dla każdej stałej (fixed) komórki w widocznym obszarze  wiersza.
 +
*'''<tt>DrawColRowMoving</tt>''': Aktywna tylko podczas przenoszenia kolumny lub wiersza. Ta metoda rysuje linię wskazującą nową pozycję wiersza/kolumny.
 +
*'''<tt>DrawBorder</tt>''': W razie potrzeby (<tt>Flat=TRUE</tt> i <tt>BorderStyle=bsSingle</tt>) rysuje wewnętrzną linię obramowania.
  
=== Operations ===
+
==== Dodatkowe metody rysowania w <tt>TCustomGrid</tt> ====
==== Focusing a cell ====
+
Te metody są zadeklarowane i (częściowo) zaimplementowane w <tt>TCustomGrid</tt>, ale nie są wywoływane bezpośrednio z tego miejsca. Są używane przez klasy potomne do rysowania zawartości komórki.
  
Focusing a cell in TStringGrid is easy. Note that counting starts from zero not 1. So to focus row 10, column 9, do:
+
*'''<tt>DrawCellText</tt>''' (virtual): Zapisuje/rysuje tekst, który jest przekazywany jako parametr do komórki. Tekst jest formatowany przy użyciu stylów aktywnych w <tt>Canvas.TextStyle</tt> (zobacz także <tt>PrepareCanvas</tt>).
 +
*'''<tt>DrawThemedCell</tt>''' (virtual): Jest używany tylko dla stałych komórek i tylko jeśli <tt>TitleStyle=tsNative</tt>. Rysuje tło komórki za pomocą <tt>ThemeServices</tt>.
 +
*'''<tt>DrawColumnText</tt>''' (virtual): Jest używany tylko dla komórek nagłówka kolumny.
 +
**'''<tt>DrawColumnTitleImage</tt>''': Jeśli siatka ma przypisany <tt>TitleImageList</tt> i <tt>Columns.Title[x].Title.ImageIndex</tt> zawiera prawidłową wartość, to ten wybrany obraz jest rysowany w komórce.
 +
**'''<tt>DrawCellText</tt>''' : (patrz wyżej)
 +
*'''<tt>DrawTextInCell</tt>''' (virtual): Jest używany do „normalnych” (tj. niestałych) komórek. W <tt>TCustomGrid</tt> ta metoda nic nie robi. W siatkach potomnych ta metoda służy do rysowania zawartości komórek. W <tt>TSringGrid</tt> metoda ta wywołuje <tt>DrawCellText</tt> (patrz wyżej).
  
<pre>
+
==== Metody rysowania wprowadzone przez <tt>TCustomDrawGrid</tt> ====
StringGrid1.row := 9;
+
Jak widać, klasa bazowa <tt>TCustomGrid</tt> nie rysuje żadnej zawartości w komórkach. Odbywa się to w '''<tt>TCustomDrawGrid</tt>'''.
StringGrid1.col := 8;
+
'''<tt>TCustomDrawGrid</tt>''' nadpisuje metodę <tt>DrawCell</tt> w następujący sposób:
</pre>
+
*'''<tt>DrawCell</tt>''' (override):
 +
**'''<tt>PrepareCanvas</tt>''': wywołuje odziedziczoną metodę z '''<tt>TCustomGrid</tt>'''.
 +
**'''<tt>DefaultDrawCell</tt>''' (virtual): Ta metoda jest wywoływana tylko wtedy, gdy procedura obsługi zdarzeń dla <tt>OnDrawCell</tt> NIE jest przypisana.
 +
***'''<tt>DrawFillRect / DrawThemedCell</tt>''' (od <tt>TCustomGrid</tt>): Rysuje tło komórki. Jeśli <tt>TitleStyle=tsNative</tt>, wówczas tło stałych komórek jest rysowane za pomocą <tt>DrawThemedCell</tt>.
 +
***'''<tt>DrawCellAutonumbering</tt>''' (virtual): Kiedy <tt>goFixedAutonumbering</tt> jest włączona w <tt>Options</tt>, to numery wierszy są rysowane w pierwszej ustalonej kolumnie przy użyciu <tt>TCustomGrid.DrawCellText</tt>.
 +
***'''<tt>DrawColumnText</tt>''' (z <tt>TCustomGrid</tt>): Jest wywoływana tylko dla komórek nagłówka kolumny (zobacz ''Dodatkowe metody rysowania w <tt>TCustomGrid</tt>'').
 +
***'''<tt>DrawTextInCell</tt>''' (z <tt>TCustomGrid</tt>): Jest wywoływana tylko dla 'normalnych' (tj. niestałych) komórek (zobacz ''Dodatkowe metody rysowania w <tt>TCustomGrid</tt>'').
 +
**'''<tt>DrawCellGrid</tt>''' (virtual): Rysuje obramowanie komórki.
  
==== Save and Retrieve Grid Content ====
+
=== Zapisz i Odzyskaj zawartość siatki ===
  
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.
+
Metody '''SaveToFile''' i '''LoadFromFile''' umożliwiają siatce zapisywanie i pobieranie układu i danych do/z pliku w formacie XML. TStringGrid, odziedziczony z TCustomStringGrid, ma również możliwość „eksportowania” i „importowania” swojej zawartości do/z pliku w formacie wartości oddzielonych przecinkami, lepiej znanego jako format pliku CSV. Jest to opisane w dokumentacji do metod '''SaveToCSVFile''' i '''LoadFromCSVFile''' (do zrobienia: utworzyć łącza).
Previously you must set the SaveOptios property as follow:
 
  
  soDesign:    Save & Load ColCount,RowCount,FixedCols,FixedRows,
+
Rodzaj informacji, które można zapisać, a następnie pobrać podczas używania '''SaveToFile''' i '''LoadFromFile''', jest określony przez właściwość SaveOptions (typu TSaveOptions), która jest zestawem opcji opisanych w następujący sposób:
               ColWidths, RowHeights and Options (TCustomGrid)
+
  soDesign:    Zapisz i wczytaj ColCount,RowCount,FixedCols,FixedRows,
  soPosition:  Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)
+
               ColWidths, RowHeights i Options (TCustomGrid)
  soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)
+
  soPosition:  Zapisz i wczytaj wartości pozycji Scroll, Row, Col i Selection (TCustomGrid)
  soContent:    Save & Load Text (TCustomStringGrid)
+
  soAttributes: Zapisz i wczytaj kolory, wyrównanie tekstu i układ itp. (TCustomDrawGrid)
 +
  soContent:    Zapisz i wczytaj tekst (TCustomStringGrid)
 +
soAll:        Ustaw wszystkie opcje (soAll:=[soDesign,soPosition,soAttributes,soContent];)
 +
Nie wszystkie opcje mają zastosowanie do wszystkich rodzajów siatek, na przykład soContent nie mają zastosowania do siatek pochodnych TCustomGrid, takich jak TDrawGrid, ponieważ ten rodzaj siatki nie zawiera pojęcia „treści”. TStringGrid jest specjalnym rodzajem siatki, która „wie”, jak obsługiwać łańcuchy i może użyć opcji soContent, jeśli jest określona.
  
The  '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a
+
Opcja soAttributes nie jest używana w standardowych siatkach Lazarusa, jest dostępna w przypadku siatek potomnych.
'''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)
 
  
 +
Podczas używania '''LoadFromFile''' siatka używa również właściwości SaveOptions, aby wiedzieć, jakie informacje należy pobrać z pliku, więc np. jest całkowicie możliwe określenie SaveOptions:=[soDesign,soContent] przy zapisywaniu i tylko SaveOptions :=[soContent] podczas ładowania.
 +
 +
Dla TStringGrid domyślną wartością SaveOptions jest [soContent], dla innego rodzaju siatek SaveOptions jest pustym zbiorem.
 +
 +
'''Uwaga:'''
 +
Jeden typowy problem podczas zapisywania i pobierania danych siatki występuje, gdy użytkownik określi właściwość SaveOptions przed '''SaveToFile''', ale nie przed '''LoadFromFile'''. W przypadku użycia '''LoadFromFile''' jakiś czas po użyciu '''SaveToFile''' właściwość SaveOptions jest ustawiona prawidłowo, ale jeśli '''LoadFromFile''' zostanie wykonane przy następnym uruchomieniu programu, właściwość SaveOptions może nie zostać poprawnie skonfigurowane, z tego powodu zaleca się, aby zawsze określać właściwość SaveOptions tuż przed '''LoadFromFile''' lub robić to globalnie podczas uruchamiania programu, jak w poniższym przykładzie:
 
----
 
----
 
+
'''Przykład:'''
'''Example:'''
+
# Najpierw przejdź do menu "Plik -> Nowy -> Aplikacja";
# First, go to menu "File -> New -> Application";
+
# Umieść pusty TStringGrid w formularzu;
# Put an empty TStringGrid;
+
# Umieść na formularzu TButton i TOpenDialog;
# Put a TButton and TOpenDialog;
+
# Dodaj zdarzenie OnCreate do formularza;
# Add the event OnCreate for the Form;
+
# Dodaj zdarzenie OnClick dla przycisku.
# Add the event OnClick for the Button.
+
<syntaxhighlight lang="pascal">
<syntaxhighlight>
 
 
unit Unit1;  
 
unit Unit1;  
  
Line 383: Line 622:
 
procedure TForm1.Form1Create(Sender: TObject);
 
procedure TForm1.Form1Create(Sender: TObject);
 
begin
 
begin
  //sets the SaveOptions at creation time of the form
+
  //ustawia SaveOptions w momencie tworzenia formularza
 
  stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
 
  stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
 
end;
 
end;
Line 390: Line 629:
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
  //Ask if thew Execute method of the OpenDialog was launched
+
  //Pytaj, czy uruchomiono metodę Execute w OpenDialog,
  //when this occurs, the user selects an XML file to Load
+
  //a gdy to nastąpi, użytkownik wybiera plik XML do załadowania,
  //wich name was stored in the FileName prop.
+
  //którego nazwa była przechowywana we właściwości FileName.
  
 
  if opendialog1.Execute then
 
  if opendialog1.Execute then
  Begin
+
  begin
   //Clear the grid
+
   //Wyczyść siatkę
 
   StringGrid1.Clear;
 
   StringGrid1.Clear;
   //Load the XML
+
   //Załaduj plik XML
 
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
 
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Refresh the Grid
+
   //Odśwież siatkę
 
   StringGrid1.Refresh;
 
   StringGrid1.Refresh;
  End;
+
  end;
 
end;
 
end;
  
Line 408: Line 647:
 
   {$I unit1.lrs}
 
   {$I unit1.lrs}
  
end.
+
end.</syntaxhighlight>
</syntaxhighlight>
+
 
 
----
 
----
'''The sample xml file:'''
+
'''Przykładowy plik xml:'''
(Copy the text below into a txt file. Don't forget put the xml header :-))
+
(Skopiuj poniższy tekst do pliku txt. Nie zapomnij umieścić nagłówka xml :-))
<syntaxhighlight lang="xml">
+
<syntaxhighlight lang="xml"><?xml version="1.0"?>
<?xml version="1.0"?>
 
 
<CONFIG>
 
<CONFIG>
 
   <grid version="3">
 
   <grid version="3">
Line 461: Line 699:
 
</CONFIG>
 
</CONFIG>
 
</syntaxhighlight>
 
</syntaxhighlight>
----
 
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''
 
  
=== Grid Cell Editors ===
+
=== Edytory komórek siatki ===
 +
 
 +
Siatka używa edytorów komórek do zmiany zawartości komórek.
  
The grid uses cell editors to change the content of cells.  
+
W przypadku wyspecjalizowanej siatki, takiej jak TStringGrid, edytor jest zwykłą jednowierszową kontrolką edytora tekstu, ale czasami pożądane jest posiadanie innych sposobów wprowadzania informacji. Na przykład:
  
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.  
+
#pokaż okno dialogowe otwierania pliku, po to by znaleźć lokalizację pliku, żeby użytkownik nie musiał ręcznie wpisywać pełnej ścieżki
 +
#jeśli tekst w komórce reprezentuje datę, wyświetl kalendarz, abyśmy mogli łatwo wybrać konkretną datę.
  
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.  
+
Czasami informacje, które użytkownik powinien wprowadzić w komórce, są ograniczone do ograniczonej listy słów; w takim przypadku bezpośrednie wpisywanie informacji może wprowadzać błędy i może zaistnieć potrzeba zaimplementowania procedur sprawdzania poprawności. Możemy tego uniknąć, używając edytora komórek, który przedstawia użytkownikowi listę zawierającą tylko prawidłowe wartości.
  
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.
+
Dotyczy to również ogólnych siatek, takich jak TDrawGrid, w których użytkownik potrzebuje jakiejś struktury do przechowywania danych, które będą wyświetlane w siatce. W takiej sytuacji informacje wprowadzone w edytorze komórek aktualizują wewnętrzną strukturę, aby odzwierciedlić zmiany w siatce.
  
==== Builtin cell editors ====
+
==== Wbudowane edytory komórek ====
  
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.
+
Moduł grids.pas zawiera już niektóre z najczęściej używanych edytorów komórek gotowych do użycia w siatkach. Możliwe jest również tworzenie nowych edytorów komórek (niestandardowe edytory komórek), jeśli wbudowane edytory nie są odpowiednie do określonego zadania.
  
The builtin cell editors are Button, Edit, and Picklist.
+
Wbudowane edytory komórek to Button, Edit i Picklist.
  
==== Using cell editors ====
+
==== Korzystanie z edytorów komórek ====
  
Users can specify what editor will be used for a cell using one of two methods.
+
Użytkownicy mogą określić, jaki edytor będzie używany dla danej komórki, korzystając z jednej z dwóch metod.
  
#'''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.
+
#'''Korzystanie z niestandardowej kolumny i wybranie właściwości ButtonStyle kolumny'''. W pierwszej metodzie użytkownik może wybrać styl edytora, który będzie wyświetlany. Dostępne wartości to: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
#'''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.
+
#'''Używanie zdarzenia siatki OnSelectEditor'''. W drugiej metodzie, w parametrze Editor użytkownik określa, którego edytora użyć dla komórki zidentyfikowanej dla kolumny aCol i wiersza ARow w siatce pochodnej TCustomDrawGrid lub TColumn w TCustomDBGrid. Do tego celu służy publiczna funkcja siatek EditorByStyle(), która jako parametr przyjmuje jedną z następujących wartości: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. Ta metoda ma pierwszeństwo przed metodą pierwszą, gdy używane są kolumny niestandardowe. W tym miejscu można określić niestandardowy edytor komórek (patrz rozdział 5 [http://www.freepascal.org/~michael/articles/grids/grids.pdf tego dokumentu] autorstwa Michaëla Van Canneyta, który wyjaśnia jak poprawnie zaimplementować niestandardowy edytor). To zdarzenie jest również miejscem do zamontowania edytora z wartościami specyficznymi dla komórki, wiersza lub kolumny (np. niestandardowe menu kontekstowe dla edytora komórek).
  
==== Description of editor styles ====
+
Ustawienie właściwości ''ButtonStyle'' działa tylko wtedy, gdy kolumna jest tworzona za pomocą polecenia ''StringGrid1.Columns.Add;''. Użycie wyrażenia takiego jak ''StringGrid1.ColCount:=X;'' spowoduje wyjątek.
 +
Ustawienie właściwości ButtonStyle można wykonać za pomocą podobnego kodu:
 +
<syntaxhighlight lang="pascal">
 +
if ColCB< StringGrid1.Columns.Count
 +
  then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;
 +
</syntaxhighlight>
  
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.
+
==== Opis stylów edytora ====
 +
Poniżej znajduje się opis stylów edytora. Są to wartości wyliczeniowe typu TColumnButtonStyle, dlatego są poprzedzone przedrostkiem „cbs”. Ten typ został użyty, aby zachować kompatybilność z DBGrid firmy Delphi.
  
 
*'''cbsAuto'''
 
*'''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.
+
:To jest domyślny styl edytora dla siatek potomnych TCustomGrid. Rzeczywista klasa edytora, która będzie używana do edycji zawartości komórki, zależy od kilku czynników. W przypadku TCustomGrids używana jest klasa TStringCellEditor pochodząca z TCustomMaskEdit. Ten edytor wyspecjalizowany jest do edycji jednowierszowych ciągów string. Jest on następnie domyślnie używany w TStringGrid i TDrawGrid. W przypadku korzystania z kolumn niestandardowych, jeśli programista wypełnił właściwość PickList kolumny, działa to tak, jakby ustawiono styl edytora cbsPickList. W przypadku TCustomDBGrid, który ma pole typu boolean, zachowuje się on tak, jakby określono styl edytora cbsCheckBoxColumn. Jest to zalecana wartość dla niestandardowych edytorów komórek. Do zrobienia: odniesienie do OnEditingDone.
 +
 
 
*'''cbsEllipsis'''
 
*'''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.  
+
:Ten styl edytora jest najbardziej ogólny. Gdy jest używany, w komórce edycji pojawia się przycisk, a programiści mogą użyć zdarzenia siatki OnEditButtonClick, aby wykryć, kiedy użytkownik nacisnął przycisk i podjąć dowolną akcję zaprogramowaną dla takiej komórki. Na przykład programista może użyć tego stylu edytora, aby wyświetlić okno dialogowe kalendarza, aby umożliwić użytkownikowi łatwe wybranie określonej daty. Innymi możliwościami może być wyświetlenie okna dialogowego otwierania pliku w celu znalezienia plików, kalkulatora, aby użytkownik mógł wprowadzić liczbowy wynik obliczeń itp.
  
: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.
+
:OnEditButtonClick to tylko powiadomienie, aby dowiedzieć się, w której komórce został kliknięty przycisk, sprawdź właściwości grid.Row i grid.Col.
  
: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.
+
:A DBGrid ma specyficzne właściwości do pobierania aktywnej kolumny lub pola, a ponieważ to zdarzenie występuje w aktywnym rekordzie, może aktualizować informacje w aktywnym polu.
 +
 
 +
:Ten styl edytora jest zaimplementowany przy użyciu TButtonCellEditor, bezpośredniego potomka TButton.
  
:This editor style is implemented using TButtonCellEditor, a direct descendant of TButton.
 
 
*'''cbsNone'''
 
*'''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.
+
:Ten styl edytora instruuje siatkę, aby nie używała żadnego edytora dla określonej komórki lub kolumny; zachowuje się wtedy tak, jakby siatka była tylko do odczytu dla takiej komórki lub kolumny.
 +
 
 
*'''cbsPickList'''
 
*'''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.
+
:Służy do przedstawienia użytkownikowi listy wartości, które można wprowadzić. Ten styl edytora jest zaimplementowany przy użyciu TPickListCellEditor, komponentu wywodzącego się z TCustomComboBox. Lista wyświetlanych wartości jest wypełniana na jeden z dwóch sposobów, w zależności od metody użytej do wyboru stylu edytora.
:#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]
+
:#W przypadku używania kolumn niestandardowych programiści mogą wprowadzić listę wartości za pomocą właściwości PickList kolumny. [DLA POCZĄTKUJĄCYCH: TODO: dokładna procedura edycji listy]
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList). An example would be:  
+
:#W OnSelectEditor programiści pobierają instancję TPickListCellEditor za pomocą funkcji EditorByStyle(cbsPickList). [[#Przyk.C5.82ad:_Praca_z_PickList.2C_jak_ustawi.C4.87_j.C4.85_tylko_do_odczytu_i_jak_wype.C5.82ni.C4.87_w_czasie_wykonywania|Zobacz tutaj przykład]]
var Lst:TPickListCellEditor;
+
:Wartość w siatce TStringGrid automatycznie odzwierciedla wybraną wartość. W razie potrzeby programista może wykryć moment wybrania wartości, zapisując procedurę obsługi zdarzenia dla zdarzenia OnPickListSelect siatki, dzięki czemu można podjąć dodatkowe kroki (na przykład w celu przetworzenia nowej wartości). TODO: powiązanie z OnEditingDone.
  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'''
 
*'''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.
+
:Może to być przydatne, gdy zawartość danych powiązana z kolumną jest ograniczona do pary wartości, na przykład tak-nie, prawda-fałsz, włącz-wyłącz, 1-0 itp. Zamiast zmuszać użytkownika do wpisania wartości dla tego rodzaju danych w StringCellEditor lub aby wybrać jedną wartość z listy, cbsCheckboxColumn służy do modyfikowania danych kolumny za pomocą reprezentacji pola wyboru, którą użytkownik może przełączać za pomocą kliknięcia myszą lub wciskając klawisz spacji (jeśli kolumna zawierająca pole wyboru jest zaznaczona i jeśli StringGrid jest edytowalny).
 +
: Pobieranie lub ustawianie wartości pola wyboru w komórce odbywa się w następujący sposób:
 +
<syntaxhighlight lang="pascal">
 +
StringGrid1.Cell[x,y]:='Z';
 +
</syntaxhighlight>
 +
: gdzie Z należy zastąpić przez ''0'' dla niezaznaczonego, ''1'' dla zaznaczonego i pusty ciąg dla wyszarzonego. Zauważ, że każda wartość (string) inna niż ''0'' i ''1'' będzie wyświetlana jako szare pole wyboru.
  
: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.
+
:Jeśli właściwość ButtonStyle kolumny jest ustawiona na cbsAuto, a DBGrid wykryje, że pole powiązane z kolumną jest polem logicznym, wówczas siatka automatycznie użyje tego stylu edytora. Ten automatyczny wybór można wyłączyć lub włączyć za pomocą właściwości OptionsExtra DBGrid; ustawienie elementu dgeCheckboxColumn na wartość False wyłącza tą funkcję.
  
:The values that are used to recognize the checked or unchecked states are set in a column's properties ValueChecked and ValueUnchecked.
+
:Wartości używane do rozpoznawania stanów zaznaczonych lub niezaznaczonych są ustawiane we właściwościach kolumny ValueChecked i 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.
+
:W dowolnym momencie wartość pola może mieć od jednego do trzech stanów: Unchecked (niezaznaczone), Checked (zaznaczone) lub Grayed (wyszarzone). Wewnętrznie te stany są identyfikowane przez następujące wartości typu TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked i gcbpGrayed.
 +
 
 +
:Ten styl edytora nie używa prawdziwych komponentów TCheckbox do obsługi interakcji użytkownika: wizualną reprezentację zapewniają trzy wbudowane obrazy bitmapowe, które odpowiadają możliwym stanom pola wyboru. Użyte mapy bitowe można dostosować, pisząc procedurę obsługi zdarzenia DBGrid OnUserCheckboxBitmap; program obsługi tego zdarzenia otrzymuje stan pola wyboru w parametrze CheckedState typu TDBGridCheckboxState oraz parametr mapy bitowej, którego programista może użyć do określenia niestandardowych map bitowych.
  
: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'''
 
*'''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.
+
:Ten styl edytora służy do wyświetlania przycisku w każdej komórce w kolumnie. Podobnie jak w przypadku cbsCheckboxColumn ten edytor nie używa prawdziwych przycisków, wygląd jest określony przez aktualny motyw zestawu widżetów.
  
: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.
+
:Użytkownik wie, jaki konkretny przycisk został naciśnięty, obsługując OnEditButtonClick w siatce i sprawdzając kolumnę i wiersz siatki. Zauważ, że w tym konkretnym przypadku zmienne col i row siatki nie identyfikują aktualnie zaznaczonej komórki, ale komórkę klikniętego przycisku. Po obsłużeniu zdarzenia OnEditButtonClick i jeśli użytkownik nie zmodyfikował col lub row siatki w tej procedurze obsługi, siatka automatycznie resetuje col i row, aby odzwierciedlić aktualnie wybraną komórkę. Podczas obsługi OnEditButtonClick bieżący wybór siatki jest dostępny w grid.Selection, który jest właściwością typu TRect, Left i Right reprezentują indeksy kolumn, Top i Bottom są indeksami wierszy.
  
:The button's caption is the corresponding cell string.
+
:Podpis przycisku to odpowiedni ciąg string komórki.
  
====Example: How to set a custom cell editor====
+
===Edycja siatek===
 +
Efekt edycji jest różny w zależności od rodzaju użytej siatki, na przykład TStringGrid przechowuje edytowany tekst wewnętrznie, a TDBGrid wpływa na rekordy w zestawie danych (dataset). TDrawGrid nie wie, co zrobić z edytowanym tekstem, jeśli programista nie przejmie nad nim kontroli, jest on po prostu odrzucany.<br>Dla TDrawGrid (chociaż powinno to działać dla wszystkich klas siatki) w celu uzyskania dostępu do edytowanego tekstu, programista może użyć zdarzenia '''OnSetEditText''', które jest wyzwalane za każdym razem, gdy użytkownik zmodyfikuje coś w edytorze, parametry ''aCol'' i ''aRow'' tego zdarzenia identyfikują edytowaną komórkę, parametr ''Value'' przechowuje tekst, jest to zalecana metoda. Innym sposobem dostępu do edytowanego tekstu jest pobranie go bezpośrednio z edytora po zakończeniu procesu edycji za pomocą zdarzenia '''OnEditingDone'''. Można to osiągnąć, uzyskując dostęp do wewnętrznego edytora używanego do edycji, w tym przypadku domyślnego „String Cell Editor” (Komórkowy Edytor String). W tym celu istnieją dwie metody: Pierwsza polega na użyciu zdarzenia '''OnSelectEditor''', gdzie parametr ''Editor'' jest instancją, która będzie używana do edycji komórki, musisz zapisać tę instancję do zmiennej, na przykład TheEditor (typu ''TSringCellEditor''), do późniejszego wykorzystania w '''OnEditingDone'''. Drugą alternatywą jest użycie metody siatki '''EditorByStyle''', ta metoda akceptuje „styl edytora” jako parametr i zwraca instancję edytora odpowiadającego temu stylowi. W naszym przypadku wiedząc, że styl cbsAuto zwraca domyślny edytor komórek, w module obsługi zdarzenia '''OnEditingDone''' możemy bezpośrednio użyć TheEditor := Grid.EditorByStyle(cbsAuto). Następnie możesz pobrać tekst za pomocą ''TheEditor.Text''. Jeśli zastosujesz tę metodę, zauważ, że „z wielką mocą wiąże się wielka odpowiedzialność”.
  
See lazarus/examples/gridcelleditor/gridcelleditor.lpi
+
====Opcje i właściwości wpływające na edycję====
 +
Zachowanie edycyjne jest kontrolowane przez szereg właściwości i opcji, praktycznie każde dostosowanie lub opcje, które wpływają na edycję, wymagają edytowalnej siatki, w przeciwnym razie takie dostosowanie może zostać zignorowane.<br>
 +
Na początku edytowalny stan siatek jest inny, dla TDrawGrid i TStringGrid edycja jest wyłączona, dla TDbGrid jest domyślnie włączona.<br>
 +
Właściwość '''Options''' zawiera kilka pozycji, które zajmują się edycją, są one opisane poniżej:<br>
 +
*'''goEditing, dgEditing (w DbGrid)'''. Ta opcja zmienia edytowalny stan siatki, można ją zmienić w czasie wykonywania, ponieważ jest zaznaczona, gdy edycja ma się rozpocząć.
 +
*'''goAlwaysShowEditor'''. Zwykle edytor jest ukryty i staje się widoczny tylko wtedy, gdy jest potrzebny. Dzięki tej opcji edytor będzie widoczny przez cały czas, jeśli siatka nie jest edytowalna, opcja ta jest ignorowana, a edytor jest zawsze ukryty.
 +
 
 +
===Hint dla komórek===
 +
„Hint” to właściwość wyświetlająca wskazówkę, krótką notatkę lub podpowiedź dla kontrolki, nad którą umieścimy kursor myszy. Inna nazwa tego elementu to „Tooltip”. Nazwy spotykane w języku polskim to także „dymek” lub „chmurka”.
 +
 
 +
Oprócz standardowej właściwości Hint dostępnej dla większości kontrolek, potomkowie TCustomGrid mogą wyświetlać specjalny dymek dla komórki, nad którą znajduje się kursor myszy. Ta funkcja jest kontrolowana przez opcje siatki '''goCellHints''' i '''goTruncCellHints''' (lub '''dgCellHints''' i '''gdTruncCellHints''' odpowiednio dla DBGrid):
 +
 
 +
* '''goCellHints''' aktywuje zdarzenie '''OnGetCellHint''' w celu pobrania wyświetlanego tekstu dymku dla komórki określonej przez przekazane parametry. W przypadku TDrawGrid i TStringGrid podane są indeksy kolumn i wierszy, natomiast w przypadku TDBGrid tekst komórki może zostać wydobyty z pola przypisanego do określonego obiektu kolumny.
 +
* '''goTruncCellHints''' wyświetla dymek tylko wtedy, gdy tekst nie mieści się w komórce i jest obcięty; tekst podpowiedzi to nieobcięty tekst komórki. W Lazarusie 1.9+ ten tekst jest przekazywany do zdarzenia '''OnGetCellHint''' w celu dalszego doprecyzowania (np. wyświetlenie całego tekstu notatki w dymku). W przypadku skróconych tekstów komórek zaleca się również włączenie opcji '''goCellEllipsis''' (lub '''dgCellEllipsis''' dla DBGrid), która dodaje '...' do skróconego tekstu komórki.
 +
 
 +
Jeśli siatka ma wartość w swojej standardowej właściwości '''Hint''', wówczas właściwość '''HintPriority''' może być użyta do kontrolowania, w jaki sposób wszystkie te dymki mogą zostać połączone w jedno wyskakujące okienko:
 +
* '''chpAll''' umieszcza tekst z właściwości Hint w pierwszym wierszu, dymek określony przez zdarzenie OnGetCellHint w drugim wierszu, a wskazówkę dotyczącą obciętych komórek w trzecim wierszu
 +
* '''chpAllNoDefault''' wyświetla tylko dymki dotyczące komórek podane przez zdarzenie OnGetCellHint i obcięte komórki.
 +
* '''chpTruncOnly''' wyświetla tylko dymki dotyczące obciętych komórek; to są ustawienia domyślne.
 +
 
 +
Ponadto standardowa właściwość siatki '''ShowHint''' musi być ustawiona na wartość True, aby wyświetlać dymki dotyczące komórek.
 +
 
 +
== Grids w praktyce i przykłady==
 +
=== Skupienie komórki ===
 +
 
 +
Wykonanie skupienia (focus) dla komórki w TStringGrid jest łatwe. Zwróć uwagę, że liczenie (zwykle w programowaniu) zaczyna się od zera, a nie od 1. Aby zrobić skupienie dla komórki w 10. rzędzie i 9. kolumnie, wykonaj:
 +
 
 +
<syntaxhighlight lang="Pascal">
 +
StringGrid1.Row := 9;
 +
StringGrid1.Col := 8;
 +
</syntaxhighlight>
 +
 
 +
===Przykład: Jak ustawić niestandardowy edytor komórek===
 +
 
 +
Zobacz lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (począwszy od Lazarusa 1.2)
 +
 
 +
====Przykład: Jak dodać edytor przycisków====
 +
<syntaxhighlight lang=pascal>
 +
// Warunkowo pokaż edytor przycisków w kolumnie o indeksie 1 lub 2, jeśli
 +
// komórka w kolumnie o indeksie 1 jest pusta
  
====Example: How to add a button editor====
 
<syntaxhighlight>
 
// 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;  
 
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;  
 
   var Editor: TWinControl);
 
   var Editor: TWinControl);
Line 551: Line 834:
 
   end;
 
   end;
  
// Triggering Action ...
+
// Uruchamianie akcji...
 
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
 
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
 
begin
 
begin
   if StringGrid1.Col = 1 then Showmessage('column 1 editor clicked');
+
   if StringGrid1.Col = 1 then ShowMessage('kliknięto edytor kolumny 1');
   if StringGrid1.Col = 2 then Showmessage('column 2 editor clicked');
+
   if StringGrid1.Col = 2 then ShowMessage('kliknięto edytor kolumny 2');
 +
end;
 +
</syntaxhighlight>
 +
 
 +
===Przykład: Praca z PickList, jak ustawić ją tylko do odczytu i jak wypełnić w czasie wykonywania===
 +
 
 +
Za pomocą zdarzenia OnSelectEditor w siatce można dostosować zachowanie edytora PickList (patrz styl przycisku cbsPickList). W kolejnym przykładzie edytor listy wyboru w kolumnie 1 został zmodyfikowany tak, że w nieparzystych wierszach użytkownik może wprowadzać wartości przez ręczne wpisanie, w parzystych wierszach wartości są ograniczone do tych zawartych na liście wyboru. Ten przykład pokazuje również, jak wypełnić listę różnymi wartościami w zależności od przetwarzanego wiersza.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
 +
  var Editor: TWinControl);
 +
begin
 +
  if aCol=1 then begin
 +
    if (Editor is TCustomComboBox) then
 +
      with Editor as TCustomComboBox do begin
 +
        if (aRow mod 2=0) then
 +
          Style := csDropDown
 +
        else
 +
          Style := csDropDownList;
 +
        case aRow of
 +
          1:
 +
            Items.CommaText := 'ONE,TWO,THREE,FOUR';
 +
          2:
 +
            Items.CommaText := 'A,B,C,D,E';
 +
          3:
 +
            Items.CommaText := 'MX,ES,NL,UK';
 +
          4:
 +
            Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
 +
        end;
 +
      end;
 +
  end;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
===Wyrównywanie tekstu w StringGrids===
 +
Ten kod pokazuje, jak używać różnych sposobów wyrównania tekstu w kolumnach 2 i 3.
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
 +
  aState: TGridDrawState);
 +
var
 +
  MyTextStyle: TTextStyle;
 +
begin
 +
  if (aCol=2) or (aCol=3) then
 +
  begin
 +
    MyTextStyle := StringGrid1.Canvas.TextStyle;
 +
    if aCol=2 then
 +
      MyTextStyle.Alignment := taRightJustify
 +
    else
 +
    if aCol=3 then
 +
      MyTextStyle.Alignment := taCenter;
 +
    StringGrid1.Canvas.TextStyle := MyTextStyle;
 +
  end;
 +
end;</syntaxhighlight>
 +
 
 +
=== Wielowierszowy tekst w siatkach ===
 +
Ten przykład pokazuje, jak utworzyć tekst wielowierszowy w komórce [3,2]. Działa to tak samo dla DBGrid, gdzie OnPrepareCanvas ma parametry do radzenia sobie z TColumns, a stamtąd z TFields.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
 +
  aState: TGridDrawState);
 +
var
 +
  MyTextStyle: TTextStyle;
 +
begin
 +
  if (aRow=2) or (aCol=3) then
 +
  begin
 +
    MyTextStyle := StringGrid1.Canvas.TextStyle;
 +
    MyTextStyle.SingleLine := false;
 +
    StringGrid1.Canvas.TextStyle := MyTextStyle;
 +
  end;
 +
end;</syntaxhighlight>
 +
 
 +
=== Weryfikacja wprowadzonych wartości ===
 +
Lazarus w wersji 0.9.29 wprowadza w StringGrid zdarzenie OnValidateEntry typu TValidateEntryEvent, które ma następującą deklarację:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
type
 +
  TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
 +
</syntaxhighlight>
 +
 
 +
aCol, aRow to współrzędne sprawdzanej komórki.
 +
OldValue to wartość, która znajdowała się w komórce cells[aCol, aRow] przed rozpoczęciem edycji.
 +
NewValue to wartość, która zostanie ostatecznie wstawiona do komórki cells[aCol, aRow].
 +
 
 +
Ze względu na sposób, w jaki działa StringGrid, ustawiając wartość komórki podczas edycji przez użytkownika (patrz zdarzenie OnSetEditText w siatce i metoda SetEditText), gdy wyzwalane jest zdarzenie OnValidateEntry, komórka zawiera już wprowadzoną wartość (prawidłową lub nie); za pomocą argumentów zdarzenia OldValue i NewValue wartość komórki może zostać zweryfikowana i zmieniona w razie potrzeby.
 +
 
 +
Zwykle sprawdzanie poprawności następuje, gdy użytkownik przeniósł się do innej komórki. Jeśli walidacja się nie powiedzie, pożądane jest, aby edytor komórek był widoczny/aktywny, aby wpisana wartość mogła zostać poprawiona przez użytkownika. Aby siatka wiedziała, że ​​walidacja nie powiodła się, należy zgłosić wyjątek. Siatka obsłuży wyjątek poprzez Application.HandleException i każdy ruch zostanie anulowany.
 +
 
 +
Na przykład, załóżmy że komórka cell[1,1] powinna zawierać tylko wartości 'A' lub 'B', walidację można przeprowadzić za pomocą:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
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
 +
      // ustaw nową prawidłową wartość, aby użytkownik mógł na przykład nacisnąć RETURN, aby kontynuować.
 +
      NewValue := 'A';
 +
      // inną opcją jest powrót do poprzedniej wartości komórki (która z założenia jest prawidłowa)
 +
      // NewValue := OldValue;
 +
      // Użyj EAbort zamiast innego przerwania typu Exception, aby uniknąć okna dialogowego
 +
      // z fałszywym komunikatem błędu i odwrotnego debugowania
 +
      raise EAbort.Create('Tylko A lub B są tutaj dozwolone');
 +
    end else begin
 +
      // jeśli nie zostanie zgłoszony żaden wyjątek, przyjmuje się, że wartość jest prawidłowa, ale
 +
      // w razie potrzeby ostateczną wartość można jeszcze zmienić, wypełniając NewValue inną wartością
 +
 
 +
      // komputer wie lepiej :)
 +
      if grid.Cells[1,1]='A' then
 +
        NewValue := 'B'
 +
      else
 +
        NewValue := 'A';
 +
    end;
 +
  end;
 +
end;</syntaxhighlight>
 +
 
 +
=== Sortowanie kolumn lub wierszy ===
 +
Właściwość ColumnClickSorts umożliwia automatyczne sortowanie siatki wtedy, gdy użytkownik kliknie nagłówek kolumny.
 +
Wielokrotne kliknięcie tej samej kolumny zmienia kolejność sortowania.
 +
Wyświetlane są obrazy domyślnego sortowania kolumn, które wskazują, która kolumna została kliknięta.
 +
 
 +
W kodzie możesz użyć metody SortColRow().
 +
 
 +
Pierwszy parametr, IsColumn, to wartość logiczna. Ustaw ją na:
 +
* True  jeśli posortowana ma być kolumna
 +
* False jeśli posortowany ma zostać wiersz.
 +
 
 +
Drugi parametr, index, jest liczbą całkowitą:
 +
* Indeks wskazuje kolumnę lub wiersz do posortowania.
 +
 
 +
Ostatnie dwa parametry są opcjonalne, określają podzakres wierszy lub kolumn do sortowania.
 +
* FromIndex    (od kolumny lub wiersza)
 +
* ToIndex      (do kolumny lub wiersza)
 +
Jeśli ostatnie dwa parametry nie zostaną określone, sortowana jest cała kolumna lub wiersz.
 +
 
 +
Sortowanie wykorzystuje algorytm QuickSort, można go zmienić, jeśli siatka potomna przesłoni metodę sort() i wywoła funkcję doCompareCells w celu porównania komórek.
 +
 
 +
Domyślnie zawartość komórek sortowana jest jako ciągi string w kolejności rosnącej lub malejącej. Kolejność można wybrać za pomocą właściwości SortOrder. Domyślnie używana jest kolejność rosnąca.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
// Sortuj kolumnę 3 w kolejności rosnącej
 +
grid.SortColRow(true, 3);
 +
 
 +
// Sortuj kolumnę 3 w kolejności malejącej, pomiń wiersze stałe (fixed) u góry
 +
grid.SortOrder := soDescending; // lub soAscending
 +
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);</syntaxhighlight>
 +
 
 +
Do niestandardowego sortowania liczb, dat, stanów itp. StringGrid ma zdarzenie OnCompareCells, które użytkownicy mogą wykorzystać na przykład w ten sposób:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
 +
begin
 +
  // Wynik będzie <0, =0 lub >0 dla normalnego sortowania.
 +
  result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
 +
  //W przypadku odwrotnej kolejności, wystarczy zanegować wynik (np. bazując na SortOrder siatki).
 +
  if StringGrid1.SortOrder = soDescending then
 +
    result := -result;
 +
end;</syntaxhighlight>
 +
 
 +
Możesz użyć OnCompareCells również wtedy, gdy automatyczne sortowanie kolumn jest włączone za pomocą właściwości ColumnClickSorts.
 +
 
 +
=== Zastąp sortowanie automatyczne (ColumnClickSorts ustawione na False ). Unikaj niektórych kolumn. Pokaż strzałki sortowania w nagłówku kolumny ===
 +
 
 +
(gdy ColumnClickSorts jest ustawione na False, to w nagłówku kolumny NIE są wyświetlane strzałki sortowania, a więc musimy ustawić strzałki.)
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
 +
Index: Integer);
 +
begin
 +
if isColumn then with StringGrid1 do
 +
  begin
 +
    col:=Index;                                                  // Wybierz kolumnę do sortowania
 +
    if Tag  = 0 then Tag :=-1                                    // Załóżmy, że dane w kolumnie są posortowane rosnąco
 +
                else Tag := Tag  * -1;                          // Używanie StringGrid1.Tag do przechowywania porządku sortowania
 +
                                                                // Pierwsze kliknięcie nagłówka posortuje kolumnę malejąco
 +
 
 +
    // Aktywuj CompareCells (aktywuj sortowanie)
 +
    case Index of
 +
      0: col:=-1;                                                // Nie sortuj kolumny 0 i nie pokazuj strzałki sortowania
 +
      1: SortColRow(True, Index);                                // Sortowanie odbędzie się w CompareCells zaraz po wykonaniu instrukcji
 +
      2: SortColRow(True, Index);
 +
      3: SortColRow(True, Index);
 +
      4: SortColRow(True, Index);
 +
      5: col:=-1;                                                // Nie sortuj kolumny 5 i nie pokazuj strzałki sortowania
 +
    end;
 +
  end;
 +
end;
 +
 
 +
// Compare zwraca jedną z następujących wartości:
 +
// jeśli A<B  zwraca: -1            ( Rosnąco )
 +
// jeśli A=B  zwraca:  0
 +
// jeśli A>B  zwraca:  1            ( Malejąco)
 +
 
 +
// Jak sortować: Text, Integer, Float, DateTime:
 +
 
 +
procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
 +
BRow: Integer; var Result: integer);
 +
 
 +
  function CompareTextAsText: Integer;                        // Text
 +
  begin
 +
    CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
 +
  end;
 +
 
 +
  function CompareTextAsInteger: Integer;
 +
  var i1,i2:Integer;
 +
  begin
 +
    if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and      // Integer
 +
      TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
 +
    Result := CompareValue(i1, i2)
 +
    else Exit(0);
 +
  end; 
 +
 
 +
  function CompareTextAsFloat: Integer;                        // Float
 +
  var f1,f2:Extended;
 +
  begin
 +
    if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
 +
      TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
 +
    Result := CompareValue(f1, f2)
 +
    else Exit(0);
 +
  end;
 +
 
 +
  function CompareTextAsDateTime: Integer;                  // DateTime
 +
  var
 +
  D1, D2 : TDateTime;
 +
  begin
 +
    if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
 +
    begin
 +
      if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
 +
      if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
 +
    end
 +
    else Exit(0);
 +
 
 +
    if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
 +
      TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
 +
    Result := CompareDateTime(D1, D2)
 +
    else Exit(0);
 +
end;
 +
 
 +
begin                                        // Nie rób tutaj nic więcej, sortowanie może być zrobione niepoprawnie
 +
  With    StringGrid1 do
 +
  begin
 +
    case ACol of
 +
      0 : ;                                                                    // Nie sortuj kolumny 0
 +
      1 :  Result := CompareTextAsText;                                        // Text
 +
      2 :  Result := CompareTextAsInteger;                                      // Integer
 +
      3 :  Result := CompareTextAsFloat;                                        // Float
 +
      4 :  Result := CompareTextAsDateTime;                                    // DateTime
 +
    end; {Case}
 +
  end;
 +
  if  StringGrid1.Tag  = -1 then  Result := -Result;                          // Zmień kolejność sortowania
 +
end; 
 +
 
 +
// DrawCell załatwia sprawę, poprzednie obrazy strzałek są usuwane:
 +
procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
 +
  aRect: TRect; aState: TGridDrawState);
 +
 
 +
  procedure DrawArrow(ImgIndex:Integer);                  // Potrzebujesz obrazów strzałek w górę/w dół w ImageList
 +
  begin
 +
    with StringGrid1 do
 +
      TitleImageList.Draw(Canvas,                        // TitleImageList z StringGrid
 +
      ARect.Right - TitleImageList.Width,
 +
      (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2,  // Położenie strzałki można regulować.
 +
      ImgIndex );
 +
  end;
 +
begin
 +
  if Col > 0 then                                    // W tym demo nie ma strzałek w kolumnie 0
 +
    if (ARow = 0) and                                // Załóżmy, że wiersz 0 jest wierszem tytułu
 +
      Assigned(TitleImageList) then                // Potrzebujesz strzałek w górę/w dół w ImageList
 +
        case Columns.Items[aCol].Tag of            // Użyj właściwości Tag kolumny jako indeksu strzałki rosnąco/malejąco
 +
          -1: DrawArrow(1);                        // Zastąp swoim indeksem strzałki w dół
 +
            0: DrawArrow(-1);                        // Usuń poprzedni obraz
 +
            1: DrawArrow(2);                        // Zastąp swoim indeksem strzałki w górę
 +
        end;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
=== Podświetlenie wybranych komórek kolumny i wiersza ===
 +
W rewizji 40276 Lazarusa dodano opcję ('''gdRowHighlight'''), która działa podobnie do '''goRowSelect''', ale używa jaśniejszego koloru do zaznaczania, a prostokąt skupienia wskazuje zaznaczoną komórką, a nie cały rząd. Działa to dobrze w przypadku wierszy, ale co z kolumnami?.
 +
 
 +
Ta sekcja przedstawia sposób podświetlania kolumn, wierszy lub obu (przykład podświetlający tylko nagłówki kolumn i wierszy można znaleźć w lazarus/examples/gridexamples/spreadsheet, ten praktyczny poradnik jest tak naprawdę rozszerzeniem tego przykładu). Wykorzystywane są dwa zdarzenia siatki: '''OnBeforeSelection''' i '''OnPrepareCanvas''', a rysowanie nie jest konieczne.
 +
 
 +
Zdarzenie '''OnBeforeSelection''' jest wyzwalane, gdy zaznaczenie ma ulec zmianie. W tym zdarzeniu możemy się dowiedzieć, która komórka jest aktualnie zaznaczona i jaka komórka zostanie wybrana jako następna. Używamy tych informacji, aby unieważnić cały wiersz lub kolumnę zarówno dla starych, jak i nowych komórek. Gdy rozpocznie się następny cykl malowania, siatka zostanie poinstruowana, aby pomalować komórki należące do obszarów unieważnionych. Jednym z pierwszych kroków podczas malowania jest wywołanie zdarzenia '''OnPrepareCanvas''' (jeśli istnieje) w celu ustawienia domyślnych właściwości płótna. Używamy tego zdarzenia do ustawienia podświetlonego wiersza lub kolumny:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
 +
begin
 +
  // tutaj możemy zdecydować, czy chcemy podświetlać kolumny, wiersze, czy jedno i drugie
 +
  // w tym przykładzie podświetlamy oba
 +
 
 +
  if Grid.Col<>aCol then
 +
  begin
 +
    // wykryto zmianę w bieżącej kolumnie
 +
    grid.InvalidateCol(aCol);      // unieważnia nową wybraną kolumnę komórek
 +
    grid.InvalidateCol(grid.Col);  // unieważnia bieżącą (będzie to „stara”) wybrana kolumna
 +
  end;
 +
  if Grid.Row<>aRow then
 +
  begin
 +
    grid.InvalidateRow(aRow);      // unieważnia nowy wybrany wiersz komórki
 +
    grid.InvalidateRow(grid.Row);  // unieważnia bieżący (będzie to „stary”) wybrany wiersz
 +
  end;
 +
end;
 +
 
 +
procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
 +
  aState: TGridDrawState);
 +
begin
 +
  if gdFixed in aState then
 +
  begin
 +
    if (aCol=grid.Col) or (aRow=grid.Row) then
 +
      grid.Canvas.Brush.Color := clInactiveCaption; // podświetliłoby to również nagłówki kolumn lub wierszy
 +
  end else
 +
  if gdFocused in aState then begin
 +
    // zostawiamy w spokoju aktualnie wybraną/skupioną komórkę
 +
  end else
 +
  if (aCol=Grid.Col) or (aRow=grid.Row) then
 +
    grid.Canvas.Brush.Color := clSkyBlue; // podświetl wiersze i kolumny kolorem clSkyBlue
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
====Example: How to align column text in StringGrids====
+
=== Unikanie pustego miejsca po prawej stronie ostatniej kolumny ===
 +
Aby to osiągnąć, siatka musi być w trybie kolumnowym. Gdy właściwość siatki <tt>AutoFillColumns</tt> ma wartość <tt>True</tt>, puste miejsce jest dzielone przez wszystkie kolumny, które mają niezerowy <tt>SizePriority</tt>. Dlatego powinieneś wykonać następujące kroki, aby wypełnić całą poziomą przestrzeń siatki:
 +
 
 +
* Ustaw <tt>AutoFillColumns</tt> na <tt>True</tt>.
 +
* Korzystając z edytora kolumn, dodaj tyle kolumn, ile potrzebujesz.
 +
* Zaznacz każdą kolumnę w drzewie obiektów nad inspektorem obiektów i ustaw jej <tt>SizePriority</tt> na <tt>0</tt>.
 +
* Teraz zdecyduj, która kolumna zostanie poszerzona tak, że cała pusta przestrzeń zostanie wykorzystana. Ustaw <tt>SizePriority</tt> tej kolumny na <tt>1</tt>.
 +
 
 +
=== Jak przewijać siatkę w czasie rzeczywistym (przesuwanie paska przewijania za pomocą myszy) ===
 +
 
 +
If you scroll a Grid by moving the scrollbar with the mouse then the Grid per default is not moved "in realtime". Only after you release the mouse, the Grid "jumps" to its new position. You can enable a scrolling in "realtime" by adding Option <code>goThumbTracking</code>.
 +
 
 +
Example: <syntaxhighlight lang="pascal"> StringGrid1.Options:=StringGrid1.Options + [goThumbTracking]; </syntaxhighlight>
 +
 
 +
Unfortunately this Option is neither documented in <tt><lazarus-installdir>\lcl\grids.pas</tt> (ver 2.0.6) nor in https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tgridoptions.html
 +
 
 +
 
 +
Jeśli przewijasz siatkę, przesuwając pasek przewijania za pomocą myszy, domyślnie siatka nie jest przesuwana „w czasie rzeczywistym”. Dopiero po zwolnieniu myszy siatka „przeskakuje” do nowej pozycji. Możesz włączyć przewijanie w czasie rzeczywistym, dodając opcję <code>goThumbTracking</code>.
 +
 
 +
Przykład: <syntaxhighlight lang="pascal"> StringGrid1.Options:=StringGrid1.Options + [goThumbTracking]; </syntaxhighlight>
 +
 
 +
Niestety ta opcja nie jest udokumentowana w <tt><lazarus-installdir>\lcl\grids.pas</tt> (wersja 2.0.6) ani w https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tgridoptions.html
 +
 
 +
=== Jak zmienić rozmiar wiersza na podstawie zawartości komórek? ===
 +
Lacak w raporcie o błędzie [https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/38473 #38473] przedstawił następujący kod:
 +
<syntaxhighlight lang="pascal">
 +
{ TStringGridHelper }
 +
  TStringGridHelper = class helper for TStringGrid
 +
    protected
 +
      procedure AutoSizeRow(aRow: integer);
 +
  end;
 +
 
 +
{ TStringGridHelper }
 +
procedure TStringGridHelper.AutoSizeRow(aRow: integer);
 +
var
 +
  aCanvas: TCanvas;
 +
  aCol, maxRowHeight:integer;
 +
  aText: String;
 +
  textRect: TRect;
 +
begin
 +
  aCanvas := GetWorkingCanvas(Canvas);
 +
  maxRowHeight := DefaultRowHeight;
 +
  for aCol:=0 to ColCount-1 do begin
 +
    aText := Cells[aCol,aRow];
 +
    textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
 +
    DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
 +
    if maxRowHeight < textRect.Height then
 +
      maxRowHeight := textRect.Height
 +
  end;
 +
  if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
 +
  RowHeights[aRow] := maxRowHeight+2;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
Dodaj to gdzieś w swoim kodzie źródłowym, a następnie możesz zmieniać rozmiar wiersza tak, jakby procedura AutoSizeRow była częścią siatki, na przykład:
 
<syntaxhighlight>
 
<syntaxhighlight>
   procedure TForm1.StringGrid1PrepareCanvas(sender: TObject; aCol, aRow: Integer;
+
// Zmień rozmiar wiersza o indeksie 2
     aState: TGridDrawState);
+
grid.AutoSizeRow(2);
 +
</syntaxhighlight>
 +
 
 +
== DBGrids w praktyce i przykłady ==
 +
===Przykład: Jak ustawić edytor memo dla dbgrids===
 +
Możesz oczywiście użyć innej kontrolki zamiast TMemo. Dostosuj według gustu.
 +
Zaadaptowane z [http://forum.lazarus.freepascal.org/index.php?topic=3640.0] (uproszczone użycie właściwości SelectedFieldRect, zobacz [http://forum.lazarus.freepascal.org/index.php/topic,8713.msg43288.html#msg43288])
 +
 
 +
* Umieść kontrolkę memo (lub jakąkolwiek kontrolkę, którą chcesz) na formularzu, ustaw dowolne właściwości i ustaw jej widoczność na False. Będzie ona używana podczas edycji komórki siatki. W tym przykładzie użyjemy GridCellMemo.
 +
* W zdarzeniu OnSelectEditor umieść następujący kod - dostosuj go, jeśli nie chcesz edytować kolumny logicznej 3, kolumny fizycznej 4:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  if (Column.DesignIndex = 3) then
 +
  begin
 +
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
 +
      GridCellMemo.Text:=Column.Field.AsString;
 +
      Editor := GridCellMemo;
 +
  end;
 +
</syntaxhighlight>
 +
* Załóżmy, że Twoje źródło danych nazywa się Datasource1, a DBGrid nazywa się ResultsGrid. Następnie w zdarzeniu OnEditingDone dla GridCellMemo umieść następujące elementy:
 +
(Pierwotny post na forum wykorzystywał zdarzenie OnChange, które uruchamia się za każdym razem, gdy zawartość jest zmieniana. Użycie OnEditingDone jest uruchamiane dopiero po zakończeniu edycji przez użytkownika.)
 +
<syntaxhighlight lang="pascal">
 +
  if not(Datasource1.State in [dsEdit, dsInsert]) then
 +
    Datasource1.Edit;
 +
   
 +
  Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;
 +
</syntaxhighlight>
 +
 
 +
''Uwaga''' Używanie normalnej kontrolki jako niestandardowego edytora ma wiele wad: nie zmieni rozmiaru ani nie zmieni położenia podczas zmiany rozmiaru kolumn lub przewijania siatki, musisz zadbać o modyfikację danych w siatce (jak wyjaśniono powyżej), nie działa poprawnie z DbGrids i zakłóca normalną nawigację po siatce za pomocą klawiatury.
 +
Siatka współdziała z edytorem za pomocą specjalnych komunikatów siatki, więc lepiej jest utworzyć podklasę kontrolki w celu zarządzania tymi komunikatami, jak wyjaśniono w rozdziale piątym [http://www.freepascal.org/~michael/articles/grids/grids.pdf tego dokumentu].
 +
 
 +
'''Uwaga''' Jeśli masz więcej niż jedną siatkę na tej samej formie, użyj innego niestandardowego edytora dla każdej z nich: jeśli użyjesz tego samego elementu sterującego, wydarzą się złe rzeczy, ponieważ obie siatki będą próbowały użyć go jednocześnie.
 +
 
 +
===Przykład: Unikaj wyświetlania pól tekstowych jako „(Memo)” dla DBGrids===
 +
When you use SQL statements like "Select * from A" you might see "(Memo)" instead of your content in a grid cell. There are many ways to fix this, but maybe the easiest is to change your statement "Select * from A" to "Select Cast(Column as TEXT) as Column from A".  This is a common problem when you use a DBGrid component.
 +
 
 +
Gdy używasz instrukcji SQL, takich jak „Select * From A”, możesz w komórkach siatki zobaczyć tekst „(Memo)” zamiast rzeczywistej zawartości. Jest wiele sposobów, aby to naprawić, ale być może najłatwiej jest zmienić instrukcję „Select * From A” na „Select Cast(Column as TEXT) as Column From A”. Jest to powszechny problem, gdy używasz komponentu DBGrid.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
    sql : UTF8String;
 +
    Queryu : TSQLQuery;
 +
.....
 +
    sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
 +
    Queryu.SQL.Text:=sql;
 +
</syntaxhighlight>
 +
 
 +
=== Sortowanie kolumn lub wierszy w DBGrid ze strzałkami sortowania w nagłówku kolumny ===
 +
To jest przykład, który sortuje '''DBgrid''' przy użyciu zdarzenia '''OnTitleClick'''
 +
oraz '''TSQLQuery''' i indeksów. Powinno to również działać w przypadku dowolnego zgodnego zestawu danych, takiego jak '''TbufDataset'''.
 +
Funkcja używa właściwości '''Column.Tag''' do przechowywania stanu sortowania dla każdej klikniętej kolumny, więc gdy wrócisz do już posortowanej kolumny, wybierze odpowiednią strzałkę sortowania do wyświetlenia.
 +
 
 +
'''Warunki wstępne:'''
 +
* Będzie potrzebny ImageList do przechowywania strzałek sortowania w górę/w dół. Przypisz swoją kontrolkę ImageList do właściwości '''TitleImageList''' dbgrid.
 +
* Upewnij się, że '''[[doc:fcl/sqldb/tsqlquery.maxindexescount.html|TSQLQuery.MaxIndexesCount]]''' jest wystarczająco duży, aby pomieścić nowe indeksy, które będziesz tworzyć. W tym przykładzie ustawiłem je na 100.
 +
* Będzie także potrzebna prywatna zmienna do przechowywania ostatniej kolumny użytej do sortowania. W tym przykładzie nazwałem ją '''FLastColumn'''.
 +
 
 +
W tym przykładzie użyty '''TSQLQuery''' nazywa się OpenQuery.
 +
 
 +
{{Uwaga|Od 21 marca 2013, '''TSQLQuery''' nie ma możliwości wyczyszczenia indeksów, ale jako obejście można ustawić '''unidirectional''' na True, a następnie ustawić go na False, a to spowoduje wyczyszczenie indeksów.}}
 +
 
 +
Aby ponownie użyć komponentu '''TSQLQuery''' z inną instrukcją SQL, indeksy muszą zostać wyczyszczone po sortowaniu, w przeciwnym razie zostanie zgłoszony wyjątek podczas otwierania '''TSQLQuery''' z inną instrukcją SQL.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
FLastColumn: TColumn; //przechowuj ostatnią kolumnę siatki, według której sortowaliśmy
 +
 
 +
procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
 +
const
 +
  ImageArrowUp=0; //powinien pasować do obrazu na ImageList
 +
  ImageArrowDown=1; //powinien pasować do obrazu na ImageList
 +
var
 +
  ASC_IndexName, DESC_IndexName:string;
 +
   procedure UpdateIndexes;
 +
  begin
 +
    // Upewnij się, że definicje indeksów są aktualne
 +
    OpenQuery.IndexDefs.Updated:=false; {<<<--Ta linia jest krytyczna. IndexDefs.Update nie zostanie
 +
    zaktualizowany, jeśli jest już ustawiony na True, co nastąpi w pierwszej posortowanej kolumnie.}
 +
    Openquery.IndexDefs.Update;
 +
  end;
 +
begin
 +
  ASC_IndexName:='ASC_'+Column.FieldName;
 +
  DESC_IndexName:='DESC_'+Column.FieldName;
 +
  // indeksy nie mogą sortować typów binarnych, takich jak ftMemo, ftBLOB
 +
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
 +
    exit;
 +
  // sprawdź, czy indeks sortowania rosnąco już istnieje dla tej kolumny.
 +
  // jeśli nie, utwórz go
 +
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
 +
  begin
 +
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
 +
    UpdateIndexes; //ensure index defs are up to date
 +
  end;
 +
  // sprawdź, czy indeks sortowania malejąco już istnieje dla tej kolumny.
 +
  // jeśli nie, utwórz go
 +
  if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
 +
  begin
 +
    OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
 +
    UpdateIndexes; //ensure index defs are up to date
 +
  end;
 +
 
 +
  // Użyj właściwości Tag kolumny, aby przełączyć ASC/DESC
 +
  column.tag := not column.tag;
 +
  if boolean(column.tag) then
 +
  begin
 +
    Column.Title.ImageIndex:=ImageArrowUp;
 +
    Openquery.IndexName:=ASC_IndexName;
 +
  end
 +
  else
 +
  begin
 +
    Column.Title.ImageIndex:=ImageArrowDown;
 +
    OpenQuery.IndexName:=DESC_IndexName;
 +
  end;
 +
  // Usuń strzałkę sortowania z poprzedniej kolumny, którą posortowaliśmy
 +
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
 +
    FLastColumn.Title.ImageIndex:=-1;
 +
  FLastColumn:=column;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
=== Wybieranie rekordów w DBGrid za pomocą pól wyboru ===
 +
Celem jest umożliwienie wyboru dowolnych rekordów w dbgrid za pomocą pól wyboru (CheckBox), siatka ma możliwość automatycznego wyświetlania pól wyboru po wykryciu pól logicznych, dla innych typów pól użytkownik może ręcznie wybrać styl przycisku cbsCheckboxColumn dla kolumny. W przypadku tego rodzaju kolumn wystarczy kliknąć pole wyboru, a zawartość pola zostanie odpowiednio zmodyfikowana.
 +
 
 +
Ale co się stanie, jeśli w naszym zbiorze danych nie będzie takiego dostępnego pola? Czy nie chcemy, aby siatka wchodziła w stan edycji po zaznaczeniu pola wyboru? Dodanie kolumny bez pola z ButtonStyle=cbsCheckboxColumn spowoduje wyświetlenie wszystkich pól wyboru wyszarzone i wyłączone, ponieważ nie ma pola połączonego z tą kolumną, a więc nie ma nic do modyfikacji. Ponieważ chcemy sami obsłużyć stan pola wyboru, musimy przechowywać stan gdzieś dla każdego rekordu. W tym celu możemy użyć klasy TBookmarkList (zdefiniowanej w module dbgrids.pas), gdzie właściwość CurrentRowSelected może poinformować, czy bieżący rekord jest zaznaczony, czy nie. Za pomocą zdarzeń dbgrid OnCellClick i OnUserCheckboxState możemy śledzić stan pola wyboru.
 +
 
 +
Zauważ, że ta technika wymaga Lazarusa w rewizji r31148 lub nowszego, który implementuje zdarzenie OnUserCheckboxState.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
...
 +
uses ..., dbgrids, stdctrls, ...
 +
 
 +
type
 +
 
 +
  { TForm1 }
 +
 
 +
  TForm1 = class(TForm)
 +
  ...
 +
    procedure DBGrid1CellClick(Column: TColumn);
 +
    procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
 +
    procedure FormCreate(Sender: TObject);
 +
    procedure FormDestroy(Sender: TObject);
 +
  ...
 +
  private
 +
    RecList: TBookmarklist;
 +
  ...
 +
  end;
 +
 
 +
procedure TForm1.DBGrid1CellClick(Column: TColumn);
 +
begin
 +
  if Column.Index=1 then
 +
    RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
 +
end;
 +
 
 +
procedure TForm1.FormCreate(Sender: TObject);
 +
begin
 +
  RecList := TBookmarkList.Create(DbGrid1);
 +
end;
 +
 
 +
procedure TForm1.FormDestroy(Sender: TObject);
 +
begin
 +
  RecList.Free;
 +
end;
 +
 
 +
procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
 +
begin
 +
  if RecList.CurrentRowSelected then
 +
    AState := cbChecked
 +
  else
 +
    AState := cbUnchecked;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
===Przykład: Jak wyeksportować DBGrid do pliku tekstowego===
 +
Istnieje kilka sposobów na utworzenie pliku tekstowego z zawartości DBGrid. Zwykle ludzie powiedzą ci, że zamiast myśleć o DBGrid jako kontenerze danych, należy spojrzeć na niego jak na połączony zestaw danych. Ale co się stanie, gdy reprezentacja tekstowa, którą chcesz uzyskać, musi pasować do wyglądu siatek, widoczności kolumn, szerokości lub kolejności? Jednym z rozwiązań jest użycie pakietu LazReport, który ma składnik przeznaczony specjalnie do tworzenia raportu z DbGrid, raport ten można następnie wyeksportować do kilku innych formatów, w tym do formatu tekstowego. Tutaj przedstawiono inne proste rozwiązanie, które może być również wykorzystane jako podstawa dla innych eksporterów formatów tekstowych, na przykład prostego eksportera html.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
// aGrid: Siatka do wyeksportowania
 +
// lpp: Ilość linii na stronę. Bez nagłówka tekstu
 +
// pageBreak: Czy wstaw podział strony. Niektóre drukarki mogą użyć tego do rozpoczęcia nowej strony
 +
// wymagania: moduł LazUTF8
 +
function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
 +
const
 +
  PX_PER_CHAR = 7;
 +
 
 +
var
 +
  Ds: TDataset;
 +
  book: TBookMark;
 +
  recCount: Integer;
 +
  line: string;
 +
  column: TColumn;
 +
 
 +
  function WidthToChar(aWidth: Integer): integer;
 +
  begin
 +
     result := trunc(aWidth/PX_PER_CHAR);
 +
  end;
 +
 
 +
  procedure AddNext(theText: string; alignment: TAlignment);
 
   var
 
   var
     MyTextStyle: TTextStyle;
+
     width: Integer;
 
   begin
 
   begin
     if (Col=2) or (Col=3) then
+
     if (line<>'') and (line<>#12) then
     begin
+
      line := line + ' ';
      MyTextStyle := StringGrid1.Canvas.TextStyle;
+
 
      if Col=2 then
+
     width := WidthToChar(Column.Width);
        MyTextStyle.Alignment := taRightJustify
+
 
       else
+
    case Alignment of
       if Col=3 then
+
      taRightJustify: line := line + UTF8PadLeft(theText, width);
        MyTextStyle.Alignment := taCenter;
+
       taCenter:       line := line + UTF8PadCenter(theText, width);
       StringGrid1.Canvas.TextStyle := MyTextStyle;
+
       else            line := line + UTF8PadRight(theText, width);
 
     end;
 
     end;
 
   end;
 
   end;
</syntaxhighlight>
 
  
=== Validating Entered Values ===
+
  procedure CollectHeader;
Lazarus version 0.9.29 introduces StringGrid OnValidateEntry event of type TValidateEntryEvent which has following declaration:
+
  begin
<Delphi>
+
    AddNext(Column.Title.Caption, Column.Title.Alignment);
   TValidateEntryEvent =
+
  end;
    procedure(sender: TObject; aCol, aRow: Integer;
+
 
              const OldValue: string; var NewValue: string) of object;
+
   procedure CollectField;
</Delphi>
+
  var
aCol,aRow are the cell coordinates of cell being validated.
+
    field: TField;
OldValue is the value that was in cells[aCol,aRow] before editing started.
+
  begin
NewValue is the value that will be finally inserted in cells[aCol,aRow].
+
    field := Column.Field;
  
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.
+
    if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
 +
      AddNext(field.AsString, Column.Alignment)
 +
    else if Field.DataType<>ftBlob then
 +
      AddNext(field.DisplayText, Column.Alignment)
 +
    else
 +
      AddNext('(blob)', Column.Alignment);
 +
  end;
  
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 LoopColumns(printingRecord: boolean);
<Delphi>
+
  var
procedure TForm1.GridValidateEntry(sender: TObject; aCol,
+
    c: TCollectionItem;
  aRow: Integer; const OldValue: string; var NewValue: String);
+
  begin
begin
+
    if (not printingRecord) and pageBreak and (result.count>0) then
  if (aCol=1) and (aRow=1) then begin
+
      line := #12
    if grid.Cells[aCol,aRow]<>'A') and grid.Cells[aCol,aRow]<>'B') then begin
+
    else
      // set a new valid value so user can just press RETURN to continue for example.
+
      line := '';
      NewValue := 'A';
+
    for c in aGrid.Columns do begin
      // another option is reverting to previous cell value (which is assumed to be valid)
+
      Column := TColumn(c);
      // NewValue := OldValue;
+
      if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
      raise Exception.Create('Only A or B are allowed here');
+
        if printingRecord then CollectField
    end else begin
+
        else                   CollectHeader;
      // if no exception is raised, is assumed that value is valid, yet if necessary
+
      end;
      // final value can be changed by filling NewValue with a different value
+
    end;
     
+
    result.add(line);
      // computer knows better :)
+
  end;
      if grid.Cells[1,1]='A' then  
 
        NewValue := 'B'
 
      else  
 
        NewValue := 'A';
 
    end;
 
  end;
 
end;
 
</Delphi>
 
=== 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.
+
begin
<Delphi>
+
  result := TStringList.create;
   // sort column 3 in ascending order
+
  Ds := aGrid.DataSource.DataSet;
   grid.SortColRow(true, 3);
+
  Ds.DisableControls;
 +
   book := Ds.GetBookmark;
 +
   try
 +
    Ds.First;
 +
    recCount := 0;
 +
    while not DS.EOF do begin
 +
      if RecCount mod lpp = 0 then
 +
        LoopColumns(false);
 +
      LoopColumns(true);
 +
      inc(recCount);
 +
      Ds.Next;
 +
    end;
 +
  finally
 +
    Ds.GotoBookmark(book);
 +
    Ds.FreeBookmark(book);
 +
    Ds.EnableControls;
 +
  end;
 +
end;
 +
</syntaxhighlight>
  
  // sort column 3 in descending order, skip fixed rows a top
+
Aby skorzystać z tej funkcji, wystarczy wykonać to:
  grid.SortOrder := soDescending; // or soAscending
 
  grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);
 
</Delphi>
 
  
For custom sorting of numbers, dates, states, etc. StringGrid has the OnCompareCells event which users can handle for example this way:
+
<syntaxhighlight lang="pascal">
<Delphi>
+
var
procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol,
+
   L: TStringList;
   BRow: Integer; var Result: integer);
 
 
begin
 
begin
   result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
+
   L := GridTextExporter(grid, 80, true);
   // result will be either <0, =0, or >0 for normal order
+
  L.SaveToFile('grid.txt');
  // for inverse order, just invert the sign of result
+
   L.Free;
  // result := -result;
 
 
end;
 
end;
</Delphi>
+
</syntaxhighlight>
Please notice that SortOrder do not work if OnCompareCells is used.
+
 
 +
----
  
 
'''Tłumaczenie'''
 
'''Tłumaczenie'''
  
Na język polski przełożył: --[[User:Slawek|Sławomir Załęcki]] 21:10, 10 September 2010 (CEST)
+
Na język polski przełożył: --[[User:Slawek|Sławomir Załęcki]] ([[User talk:Slawek|talk]]) 17:13, 2 February 2022 (CET)

Latest revision as of 17:22, 2 February 2022

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

Strona referencyjna siatek Grid

Intencje

Ten tekst będzie próbą pokazania użytkownikowi niektórych aspektów komponentów siatek (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 siatek (ponieważ doświadczeni użytkownicy zazwyczaj potrzebują jedynie informacji dotyczących nowych funkcji). W niniejszym tekście postaram się zatem osiągnąć następujące cele:

  1. Przedstawić komponenty siatki osobom z niewielkim lub żadnym doświadczeniem w Delphi.
  2. Udokumentować różnice w odniesieniu do komponentów siatek Delphi.
  3. Udokumentować nową funkcjonalność w siatkach Lazarusa.
  4. Utworzyć referencje i przykłady dla tych komponentów.

Wprowadzenie

Siatka (Grid) to komponent, który umożliwia wyświetlanie danych w formacie tabelarycznym. Najbardziej oczywistą cechą siatek jest to, że składają się one z komórek tworzących rzędy i kolumny, podobnie jak w arkuszu kalkulacyjnym.

Ilość typów informacji, które można wyświetlić w siatce, jest różna i zależy głównie od tego, co użytkownik chce pokazać. Ogólnie informacje te składają się z tekstów, kolorów, obrazów lub kombinacji tych trzech.

Biorąc pod uwagę ogromną różnorodność informacji, które można przedstawić, istnieje szereg siatek, których celem jest ułatwienie użytkownikowi pokazania określonych rodzajów informacji. Na przykład istnieje siatka przeznaczona do wyświetlania tekstu: StringGrid. Dokumentację dla tej siatki można znaleźć tutaj

Drzewo dziedziczenia

                     TCustomControl
                             |
                             |
                        TCustomGrid
                             |
               +-------------+------------+
               |                          |
         TCustomDrawGrid             TCustomDbGrid
               |                          |
      +--------+--------+                 |
      |                 |              TDBGrid
  TDrawGrid      TCustomStringGrid                
                        |
                        |
                   TStringGrid

Pierwszy przykład

Ponieważ jednym z celów tej strony jest pomoc osobom z niewielką lub żadną wcześniejszą wiedzą o Lazarusie, zróbmy wprowadzający przykład użycia siatek. Dlaczego nie, stwórzmy tradycyjny przykład „Witaj Świecie!” (ang. "Hello World") za pomocą komponentu TStringGrid.

  1. Stwórz nową aplikację.
    • Z menu głównego wybierz: Projekt->Nowy projekt...
    • W oknie dialogowym Utwórz nowy projekt wybierz Aplikacja i naciśnij „OK”
    • Zostanie wyświetlony nowy pusty formularz.
  2. Umieść siatkę (grid) na formularzu
    • Z palety komponentów wybierz zakładkę „Additional”
    • Kliknij ikonę TStringGrid tstringgrid.png
    • Kliknij formularz w lewym górnym rogu. Pojawi się nowa pusta siatka.
  3. Umieść przycisk na formularzu
    • Z palety komponentów wybierz zakładkę „Standard”
    • Kliknij ikonę TButton tbutton.png
    • Kliknij na pusty obszar formularza. Pojawi się nowy przycisk.

Kliknij dwukrotnie przycisk z kroku 3 i zapisz następujący kod w module obsługi przycisku kliknięcia:

    • StringGrid1.Cells[1,1] := 'Witaj Świecie!';
      
  1. Uruchom program, klikając ikonę odtwarzania menu run.png
    • po naciśnięciu przycisku Button1 w komórce w kolumnie 1 i wierszu 1 powinien pojawić się tekst „Witaj Świecie!”. (zwróć uwagę, że indeksy lewej kolumny i górnego wiersza są równe 0)

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

Obecne elementy siatek różnią się od siatek Delphi na kilka sposobów. Jest to spowodowane głównie tym, że kiedy po raz pierwszy opracowano siatki dla Lazarusa, były one tworzone od zera bez żadnych prób uczynienia ich w pełni kompatybilnymi z Delphi.

Na późniejszym etapie kompatybilność z siatkami Delphi stała się pożądanym celem, a siatki Lazarus zaczęły być bardziej zgodne z interfejsem sieci Delphi. Jednak nawet to zostało zrobione bez próby dopasowania każdej właściwości lub metody siatki Lazarusa do swojego odpowiednika w Delphi. Ponadto (ponieważ wewnętrzne elementy siatki Lazarusa bardzo różnią się od wewnętrznych elementów siatki Delphi) niektóre funkcje Delphi nie są dostępne lub muszą być emulowane w inny sposób w siatce Lazarusa niż w przypadku siatki Delphi. Osiągnęliśmy większą kompatybilność z Delphi w miarę ewolucji siatek Lazarusa i jest to pożądane.

Różnice

Znane różnice wymieniono poniżej (kolejność nie ma znaczenia).

  • Edytor komórek (Cell Editor)
  • Zachowanie podczas projektowania
  • Pewne różnice występują podczas rysowania komórek, zobacz sekcję Dostosowywanie siatek.

Nowe funkcje

  • Kolumny
  • Zdarzenia
  • Edytor siatki (Grid Editor)

Sposoby na to, by siatka Lazarus była bardziej zgodna 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.

Możesz sprawić, by siatka Lazarusa wyglądała i zachowywała się tak, jak jej odpowiednik w Delphi. Poniżej wymienione są ustawienia właściwości, które pozwolą to osiągnąć. Te dostosowania są oparte na nowo utworzonej siatce. Wpisy oznaczone jako [Kod] należy ustawić w kodzie, wpisy [Projekt] można zmienić 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 nowsza
  • [Kod] FastEditing := False; //obsługiwane w dbgrid. StringGrid wymaga wersji SVN r10992 lub nowszej
  • [Projekt] AutoAdvance := aaNone;

Charakterystyka siatek

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

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

Właściwość AllowOutboundEvents

Właściwość chroniona w klasie TCustomGrid oraz publiczna w klasie TCustomDrawGrid i klasach potomnych. Zwykle, gdy użytkownik kliknie punkt nad pustą przestrzenią za komórkami (na przykład, jeśli siatka ma trzy wiersze, ale użytkownik kliknie wyimaginowany czwarty wiersz), komórka, w której aktualnie znajduje się fokus, przesunie się do komórki najbliższej punktu, który właśnie kliknął. Nazywamy to wydarzeniem wychodzącym. Wartość domyślna to True, ponieważ tak właśnie działa siatka od samego początku. Ta właściwość została dodana, aby symulować zachowanie Delphi, gdy zdarzenia wychodzące nie są dostępne, więc aby umożliwić zgodność z Delphi, ustaw tę właściwość na False.

Właściwość Columns

Lazarus zawiera właściwość columns w siatkach TStringGrid i TDrawGrid. Ta właściwość dodaje to, co nazywamy kolumnami niestandardowymi. Kolumny niestandardowe to zbiór obiektów, które przechowują właściwości, które mają zastosowanie do całych kolumn, na przykład tytuły kolumn (w przypadku StringGrid zastąpi wartość określoną w odpowiedniej właściwości Cells[ColumnIndex, RowTitle]), wyrównanie tekstu, kolor tła, preferowany edytor itp.

Kolumny niestandardowe dodają dodatkowe właściwości lub zastępują domyślne wartości właściwości w normalnych kolumnach siatki. Ponadto, wartość grid.ColCount może się zwiększyć lub zmniejszyć w celu uwzględnienia liczby niestandardowych kolumn dołączanych do siatki. A to oznacza, że grid.ColCount = grid.FixedCols + grid.Columns.Count.

Na przykład, jeśli mamy siatkę bazową z ColCount := 5 i FixedCols := 2 i wykonamy:

  • Dodaj 3 niestandardowe kolumny, to siatka bazowa będzie dokładnie taka sama, jak poprzednio, 2 stałe kolumny i 3 normalne kolumny.
  • Dodaj 1 niestandardową kolumnę, to siatka bazowa będzie miała ColCount := 3, czyli 2 stałe kolumny i 1 normalną kolumnę.
  • Dodaj 4 niestandardowe kolumny, to siatka bazowa będzie miała ColCount := 6, czyli 2 stałe kolumny i 4 normalne kolumny.

Z tego możemy wywnioskować, że:

  • Stałe właściwości lub liczba kolumn nie są odpowiednio ulepszane ani modyfikowane przez kolumny niestandardowe.
  • grid.ColCount zwykle różni się od grid.Columns.Count ('grid.ColCount=grid.Columns.Count tylko wtedy, gdy ' FixedCols=0)

W czasie projektowania w Inspektorze obiektów użytkownik może uzyskać dostęp do właściwości columns, aby wywołać edytor kolumn. Stamtąd można dodawać, usuwać lub modyfikować niestandardowe kolumny. Edytor wyświetla listę aktualnych kolumn niestandardowych; wybierając elementy z tej listy, inspektor obiektów zostaje wypełniony właściwościami dostępnymi dla każdej kolumny. Lista niestandardowych kolumn jest również dostępna w widoku drzewa komponentów Inspektora obiektów, gdzie kolumny można dodawać, usuwać lub modyfikować. Pojawiają się na niższym poziomie pod siatką kontenerów.

W czasie wykonywania programu, kolumny można modyfikować za pomocą kodu w następujący sposób:

  var
    c: TGridColumn;
  begin
    // dodaj niestandardową kolumnę siatki
    c := Grid.Columns.Add;
    // modyfikuj kolumnę
    c.title.caption := 'Price';       // Ustaw tytuły kolumn
    c.align := taRightJustify;        // Wyrównaj zawartość kolumny do prawej
    c.color := clMoneyGreen;          // Zmień domyślny kolor na clMoneyGreen
    c.Index := 0;                     // Niech to będzie pierwsza kolumna
    // uzyskać dostęp do istniejącej kolumny
    grid.columns[0].Width := 60;      // Zmień szerokość kolumny 0 na 60 pikseli
    // usuń istniejącą kolumnę
    grid.columns.delete(0);           // Usuń kolumnę 0
    ....
  end;

Dodatkowo, podczas korzystania z niestandardowych kolumn, siatki nie pozwalają na bezpośrednią modyfikację grids.colcount; dodawanie lub usuwanie kolumn powinno odbywać się za pomocą właściwości columns. Wyjaśnienie jest takie, że istnieje niespójność w stopniowym usuwaniu niestandardowych kolumn za pomocą ColCount, gdy ColCount osiągnie FixedCols, siatka nie ma więcej niestandardowych kolumn. Jeśli teraz zwiększymy ColCount, nowa kolumna nie będzie kolumną niestandardową, ale normalną.

Obecnie nie ma planów, aby siatki wykorzystywały tylko kolumny niestandardowe.

Właściwość AutoFillColumns

Często siatki są szersze niż pozioma przestrzeń potrzebna kolumnom - pozostawia to nieprzyjemny pusty obszar po prawej stronie ostatniej kolumny. Siatki LCL zapewniają mechanizm rozszerzania szerokości określonych kolumn w taki sposób, że puste miejsce jest wypełniane automatycznie.

W tym celu należy dodać Columns zgodnie z opisem powyżej lub przez ustawienie właściwości ColCount oraz właściwości siatki AutoFillColumns, która musi być ustawiona na True. Każda kolumna ma właściwość SizePriority. Gdy ma wartość 0, szerokość kolumny jest pobierana z właściwości Width kolumny. Ale gdy ma wartość niezerową, szerokość kolumny jest dostosowywana do średniego dostępnego rozmiaru pozostałego dla wszystkich kolumn z niezerowym SizePriority.

Procedura SaveToFile(AFileName: String)

Zapisuje siatkę do pliku XML. Właściwość SaveOptions określa, co dokładnie ma zostać zapisane:

type
  TGridSaveOptions = (
    soDesign,             // Zapisz strukturę siatki (liczba kolumn/wierszy i opcje - Options)
    soAttributes,         // Zapisz atrybuty siatki (czcionka - Font, pędzel - Brush, styl tekstu TextStyle)
    soContent,            // Zapisz zawartość siatki (tekst w StringGrid)
    soPosition            // Zapisz kursor siatki i pozycję zaznaczenia
  );
  TSaveOptions = set of TGridSaveOptions;

Procedura LoadFromFile(AFileName: String)

Ładuje plik XML utworzony przez SaveToFile. Ponownie, SaveOptions określają, co dokładnie ma zostać załadowane z pliku.

TCustomDBGrid

TCustomDBGrid jest podstawą dla TDBGrid.

Nie ujawniają właściwości Col i Row. Aby przejść do określonej kolumny, użyj np. właściwość SelectedIndex.

Ciekawą metodą publiczną jest AutoAdjustColumns.

Procedura AutoAdjustColumns

Ta procedura ustawia szerokość kolumny na rozmiar najszerszego znalezionego tekstu. Można go używać po załadowaniu zestawu danych/ustawieniu go jako Aktywny - Active.

Jednak w przeciwieństwie do TCustomStringGrid.AutoAdjustColumns (patrz poniżej), spowoduje to ustawienie bardzo szerokich kolumn, chyba że masz włączoną właściwość dgAutoSizeColumns.

Procedura InplaceEditor

Zobacz przykład z błędu 23103 - i wstaw wyjaśnienie, co robi/dlaczego jest to potrzebne. Weryfikuje wartości wejściowe? Zmienia to, co jest wyświetlane?

procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: char);
var
  S: String;
begin
  if (Key in [',','.']) then
  begin
    //w przeciwieństwie do Delphi nie wszystkie edytory InPlaceEditors są edytorami typu string, więc sprawdź!
    if (DBGrid1.InplaceEditor is TStringCellEditor) then
    begin
      S := TStringCellEditor(DBGrid1.InplaceEditor).EditText;
      if Pos(',',S) > 0 then
        Key := #0
      else
        Key := ',';
    end;
  end;
end;

TCustomStringGrid

Klasa TCustomStringGrid służy jako podstawa dla klasy TStringGrid. Może być używana dla pochodnych komponentów TStringGrid, które chcą ukryć opublikowane właściwości.

Następujące właściwości lub metody są publiczne i są również dostępne dla TStringGrid.

Zobacz pełną dokumentację TCustomStringGrid

Procedura 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.

Procedura AutoAdjustColumns lub AutoSizeColumns

Automatycznie zmienia szerokość wszystkich kolumn, dopasowując je do najdłuższego tekstu w każdej kolumnie. Jest to szybka metoda zastosowania funkcji AutoSizeColumn() dla każdej kolumny w siatce.

Procedura Clean; overload;

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

Procedura 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]);

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

Wykonuje to samo co 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.

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

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

Procedura SaveToCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);

Zapisuje zawartość siatki w pliku w formacie tekstowym, gdzie wartości są rozdzielone przecinkami (CSV) (dodanym w Lazarus r32179).

Argument AFilename określa nazwę pliku, w którym zostanie zapisana zawartość. Jeśli plik istnieje, zawartość zostanie nadpisana. Jeśli nie istnieje, plik zostanie utworzony.

ADelimiter (argument opcjonalny) jest używany do dostarczenia niestandardowego separatora, jeśli jest to wymagane. Domyślnie tworzony jest format CSV (tzn. ADelimiter:=',';) dla pliku rozdzielanego tabulatorami ADelimiter powinien mieć wartość #9.

Parametr WithHeader służy do określenia, czy „nagłówek wiersza” powinien zostać uwzględniony, czy nie. Nagłówek wiersza to lista nazw pól na początku pliku wyjściowego; jego zawartość pochodzi z ostatniego stałego (Fixed) wiersza w siatce. Istnieje wyjątek od tej reguły: jeśli siatka zawiera niestandardowe kolumny, zawartość nagłówka wiersza pochodzi z niestandardowych tytułów kolumn, a nie ze stałych komórek wiersza.

Jeśli WithHeader ma wartość True, a siatka nie zawiera stałego wiersza ani niestandardowych kolumn, zawartość nagłówka wiersza zostanie pobrana z pierwszego wiersza w siatce.

Normalne wyjście danych CSV rozpocznie się od pierwszego niestałego (Non-Fixed) wiersza w siatce.

Procedura LoadFromCSVFile(AFileName: string; ADelimiter:Char=','; WithHeader:boolean=true);

Wczytuje zawartość siatki z pliku w formacie CSV (dodanym w Lazarus r32179).

Kolumny będą dodawane lub usuwane do lub z siatki w zależności od potrzeb, zgodnie z liczbą pól zawartych w każdym wierszu pliku CSV. Wczytanie pliku CSV nie zmieni liczby stałych (Fixed) wierszy, które już istniały w siatce.

Argument AFileName określa nazwę pliku źródłowego z zawartością CSV.

Opcjonalny parametr ADelimiter może być użyty do określenia innego separatora lub ogranicznika. Przykład: dla pliku rozdzielanego tabulatorami, ADelimiter powinno mieć wartość #9. Innym popularnym formatem pliku jest plik rozdzielany średnikami, gdzie ADelimiter powinien być ;

Parametr WithHeader służy do określenia, czy pierwszy wiersz w pliku CSV powinien być traktowany jako „wiersz nagłówka”, czy nie. Jeśli siatka ma ustalone wiersze i WithHeader ma wartość True, nagłówki kolumn dla ostatniego stałego (Fixed) wiersza zostaną pobrane z wiersza nagłówka. Należy jednak pamiętać, że jeśli siatka zawiera niestandardowe kolumny, wówczas wiersz nagłówka będzie używany jako źródło dla tytułów kolumn, a niestandardowe tytuły kolumn są zawsze wyświetlane w pierwszym stałym wierszu siatki lub ukryte, jeśli w siatce nie ma ustalonych wierszy.

Jeśli procedura LoadFromCSVFile ma trudności z załadowaniem pliku CSV (np. cudzysłowy lub spacje są nieprawidłowo interpretowane), możesz ręcznie załadować siatkę za pomocą np. CsvDocument... i oczywiście zawsze mile widziana jest łatka dla LoadFromCSVFile.

Właściwość 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 wykonywane w tej zwróconej liście ciągów string nie będą miały żadnego wpływu na zawartość siatki lub układu.

Zobacz powyższy przykład pobrania.

Właściwość Rows[index: Integer]: TStrings read GetRows write SetRows;

Pobierz/ustaw listę ciągów string z/do wiersza siatki o danym indeksie, począwszy od indeksu kolumny 0 do kolumny ColCount-1.

Uwagi

Ta właściwość działa inaczej w Lazarusie i Delphi podczas pobierania danych z siatki. W Lazarusie tworzony jest tymczasowy obiekt typu TStringList do pobierania zawartości wiersza. Obowiązkiem użytkownika jest zwolnienie tego obiektu po użyciu.

Oznacza to również, że jakiekolwiek zmiany w tej zwróconej liście ciągów string nie wpłyną na zawartość ani układu, ani siatki.

Przykłady
  • Przykład ustawiania: Ustaw zawartość trzeciego wiersza siatki jako zawartość ListBox:
Grid.Rows[2] := ListBox1.Items;
  • Przykład pobrania: Ustaw zawartość pola listy z 4 indeksu wiersza siatki:
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;
  • Przykład, który nie działa, oraz jego poprawka: Pobrana lista ciągów jest tylko do odczytu
 // te dwa przykłady nie zadziałają i spowodują wycieki pamięci,
 // ponieważ zwrócone listy ciągów nie są zwalniane
 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'; 
 
 // naprawienie pierwszego przypadku
 Lst:=TStringList.Create;
 Lst.CommaText := '1,2,3,4,5';
 Grid.Rows[1] := Lst;
 Lst.Free;

Właściwość UseXORFeatures

Właściwość typu Boolean, wartość domyślna: False;

Ta właściwość kontroluje wygląd kropkowanego prostokąta fokusu w siatce (pokazuje się, gdy klikniemy myszką na jakiejś komórce). Gdy ma wartość True, prostokąt jest malowany przy użyciu operacji rastrowej XOR. Dzięki temu możemy zobaczyć prostokąt fokusu bez względu na kolor tła komórek. Gdy ma wartość False, użytkownik może kontrolować kolor kropkowanego prostokąta fokusu za pomocą właściwości FocusColor

Kontroluje również wygląd zmiany rozmiaru kolumny/wiersza. Gdy ma wartość True, linia pokazuje wizualnie rozmiar kolumny lub wiersza, jeśli użytkownik zakończy operację. Gdy ma wartość False, zmiana rozmiaru kolumny lub wiersza zaczyna obowiązywać, gdy użytkownik przeciąga myszą.

TValueListEditor

TValueListEditor to kontrolka (formant) pochodzący z TCustomStringGrid i służy do edycji par typu klucz-wartość.

Właściwość DisplayOptions

Kontroluje różne aspekty wyglądu TValueListEditor.

Właściwość TitleCaptions

Ustawia wartości dla tytułów kolumn (jeśli właściwość DisplayOptions zawiera wartość doColumnTitles). Jeśli DisplayOptions nie ma wartości doColumnTitles, używane są domyślne tytuły.

Właściwość Strings

Zapewnia dostęp do listy ciągów zawierających pary klucz-wartość.
Pary klucz-wartość muszą mieć format typu:
'NazwaKlucza=Wartość'

Właściwość ItemProps

Możesz użyć tej właściwości, aby kontrolować sposób edytowania elementów w kolumnach „Value” (Wartość).
Jest to kontrolowane przez ustawienie właściwości EditStyle i ReadOnly elementu ItemProp.

Właściwość KeyOptions

KeyOptions to zbiór wartości porządkowych TKeyOptions kontrolujących, czy użytkownik może modyfikować zawartość kolumny „Key” (Klucz).

  • KeyEdit: użytkownik może edytować nazwę klucza
  • KeyAdd: użytkownik może dodawać klucze (przez wciśnięcie klawisza Insert w siatce). KeyAdd wymaga KeyEdit.
  • KeyDelete: użytkownik może usuwać pary klucz-wartość (przez naciśnięcie kombinacji Ctrl+Delete).
  • KeyUnique: jeśli ustawione, klucze muszą mieć unikalne nazwy. Próba wprowadzenia zduplikowanego klucza spowoduje zgłoszenie wyjątku.

Właściwość DropDownRows

Jeśli edytor komórek jest listą wyboru (ValueEdit1.ItemProps['key1'].EditStyle=esPickList), to ta właściwość ustawia DropDownCount, czyli ilość wierszy wyświetlanej listy. Wartość domyślna to 8.

Funkcja DeleteRow

Usuwa parę Klucz-Wartość wiersza o podanym indeksie, całkowicie usuwając wiersz.

Funkcja InsertRow

Wstawia wiersz w siatce i ustawia parę Klucz-Wartość. Zwraca indeks nowo wstawionego wiersza.

Funkcja IsEmptyRow

Zwraca wartość True, jeśli komórki indeksowanego wiersza są puste (Keys[aRow]=; Values[aRow]=).

Funkcja FindRow

Zwraca indeks wiersza, który ma określoną nazwę klucza.

Funkcja RestoreCurrentRow

Cofa edycję w bieżącym wierszu (jeśli edytor jest nadal aktywny). Dzieje się tak, gdy użytkownik naciśnie klawisz Escape.

Zmienione zachowanie niektórych właściwości pochodzących z TCustomStringGrid

Właściwość Options

Ze względu na charakter TValueListEditor jego właściwość Options ma pewne ograniczenia

  • goColMoving nie jest dozwolony w opcjach (nie można go ustawić).
  • goAutoAddRows można ustawić tylko wtedy, gdy KeyAdd jest włączony w KeyOptions. Ustawienie KeyAdd automatycznie ustawi goAutoAddRows.
  • goAutoAddRowsSkipContentCheck jest niedozwolone (na razie powoduje awarię w TValueListeditor: wymaga naprawy).
Właściwość FixedRows

Może mieć wyłącznie wartość równą 1 (pokaż tytuły kolumn) lub 0 (nie pokazuj tytułów kolumn).

Właściwość ColCount

Może mieć wyłącznie wartość równą 2.

Właściwość Objects

Uwaga! Nie zaleca się używania tej właściwości w ogóle.
Kiedy jakiś (wewnętrzny) kod manipuluje właściwością Strings, zmiany dla Objects zostaną utracone.

Ogólne uwagi dotyczące korzystania z TValueListEditor

Podczas manipulowania zawartością ValueListEditor (siatki) można bezpośrednio manipulować podstawową właściwością Strings.
Pamiętaj jednak, że jeśli to zrobisz, musisz ukryć edytor, w przeciwnym razie ciągi mogą nie być zsynchronizowane z zawartością siatki, którą widzisz na ekranie.

Jeśli chcesz wstawić lub usunąć wiersze itp., preferowanym sposobem jest użycie metod publicznych z TValueListEditor: DeleteRow(), InsertRow(), MoveRow(), ExchangeRow() lub różnych metod Sort().

Próba użycia metod z klasy przodka do manipulowania wierszami lub kolumnami (np. Columns.Add) może spowodować awarię.

Zapisywanie i ładowanie zawartości TValueListEditor

Korzystanie z właściwości Strings

Jeśli chcesz tylko zapisać i załadować zawartość TValueListEditor (np. par Klucz/Wartość, a nie układ), możesz użyć metod Strings.SaveToFile i Strings.LoadFromFile.
W przypadku LoadFromFile właściwość RowCount zostanie automatycznie dostosowana.
Również w przypadku LoadFromFile nie są wykonywane żadne kontrole poprawności (więc wiersze, które nie reprezentują pary Klucz/Wartość, zostaną zakończone jako klucze bez wartości).

Korzystanie z TValueList.SaveToFile i TValueList.LoadFromFile

When using SaveToFile and LoadFromFile you get the additional benefits of also being able to save and load the layout, like with other grids.
SaveToFiel will also save information about wether or not the TValueListEditor uses ColumnTitles, and if so, it saves them.

In contrast to it's ancestors TValueList.LoadFromFile performs sanity checks on the file it tries to read. In particular RowCount must be specified and there cannot be a cell with RowIndex > RowCount or ColumnIndex other than 0 or 1.
If the file does not seem to be a valid TValueListEditor grid file, and exception is raised.

TValueList.SaveToFile and TValueList.LoadFromFile do not work correctly in Lazarus versions <= 2.1 r62044.

Korzystając z funkcji SaveToFile i LoadFromFile z TValueList, zyskujesz dodatkowe korzyści związane z możliwością zapisywania i ładowania układu, podobnie jak w przypadku innych siatek.
SaveToFile zapisze również informacje o tym, czy TValueListEditor używa ColumnTitles, a jeśli tak, zapisuje je.

W przeciwieństwie do swoich przodków, TValueList.LoadFromFile sprawdza poprawność pliku, który próbuje odczytać. W szczególności właściwość RowCount musi być określona i nie może istnieć komórka z RowIndex > RowCount lub ColumnIndex innym niż 0 lub 1.
Jeśli plik nie wydaje się być prawidłowym plikiem siatki TValueListEditor, zgłaszany jest wyjątek.

TValueList.SaveToFile i TValueList.LoadFromFile nie działają poprawnie w wersjach Lazarusa <= 2.1 r62044.

Korzystanie z metod SaveToCSVFile i LoadFromCSV

Te metody nie powinny być obecnie używane, ponieważ w przypadku TValueListEditor są one wadliwe.

Praca z siatkami

Dostosowywanie siatek

Komponenty Grid pochodzą z klasy TCustomControl i nie mają skojarzonego z nią natywnego widżetu, co oznacza, że ​​siatki nie są ograniczone przez wygląd aktualnego motywu interfejsu. Może to być zarówno zaletą, jak i wadą: zwykle programiści chcą stworzyć aplikację o jednolitym wyglądzie. Dobrą wiadomością jest to, że siatki Lazarusa są wystarczająco elastyczne, aby uzyskać coś z obu światów; programiści mogą łatwo sprawić, by siatki wyglądały podobnie do innych natywnych kontrolek, lub mogą dostosować siatkę w najdrobniejszych szczegółach, aby uzyskać prawie taki sam wygląd na dowolnej platformie lub interfejsie widżetów (tzn. z wyjątkiem pasków przewijania, ponieważ ich wygląd jest wciąż zdeterminowany aktualnym motywem).

Właściwości i Zdarzenia dostosowujące siatek

Niektóre właściwości mogą wpływać na wygląd siatki, działając, gdy komórka ma zostać pomalowana w zdarzeniu PrepareCanvas/OnPrepareCanvas, zmieniając domyślne właściwości płótna (canvas), takie jak kolor pędzla lub czcionka. Poniżej znajduje się lista takich właściwości:

  • AlternateColor. Dzięki tej właściwości użytkownik może zmienić kolor tła pojawiający się w naprzemiennych wierszach. Ma to na celu umożliwienie łatwiejszego odczytania danych z wierszy siatki przez użytkownika.
  • Color. Ta właściwość ustawia podstawowy kolor używany do rysowania tła niestałych (Non-Fixed) komórek.
  • FixedColor. Jest to kolor używany do rysowania tła dla stałych komórek (Fixed).
  • Flat. Ta właściwość wyłącza trójwymiarowy wygląd stałych komórek.
  • TitleFont. Czcionka używana do rysowania tekstu w stałych komórkach.
  • TitleStyle. Ta właściwość zmienia wygląd 3D stałych komórek, istnieją 3 ustawienia:
    • tsLazarus. To jest domyślny wygląd preferowany przez Lazarusa
    • tsNative. Ta wartość próbuje ustawić wygląd zgodny z bieżącym motywem zestawu widżetów.
    • tsStandard. Ten styl jest bardziej kontrastowy, podobnie jak siatki Delphi.
  • AltColorStartNormal. Wartość logiczna. Jeśli jest True: alternatywny kolor jest zawsze w drugim rzędzie po stałych wierszach, pierwszy wiersz po stałych wierszach będzie zawsze w domyślnym kolorze. Jeśli jest False: domyślny kolor jest ustawiany na pierwszym wierszu, tak jakby nie było żadnych stałych wierszy.
  • BorderColor. Ustawia kolor obramowania siatki używany, gdy Flat:=True i BorderStyle:=bsSingle.
  • EditorBorderStyle. Jeśli ustawione na bsNone w systemie Windows, to edytory komórek nie będą miały obramowania, tak jak w Delphi. Ustaw domyślnie na bsSingle, ponieważ obramowanie może być specyficzne dla motywu w niektórych zestawach widżetów i umożliwiać jednolity wygląd.
  • FocusColor. Kolor używany do rysowania komórki, na której aktualnie ustawione jest skupienie (fokus), jeśli UseXORFeatures nie jest ustawiony, domyślnie jest to clRed.
  • FocusRectVisible. Włącza/wyłącza rysowanie skupionej komórki.
  • GridLineColor. Kolor linii siatki w obszarze niestałych komórek.
  • GridLineStyle. Styl pióra używany do rysowania linii w obszarze niestałych komórek, możliwe wybory to: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame , psPattern, psClear. Domyślnie jest to psSolid.
  • SelectedColor. Kolor używany do rysowania tła komórki na wybranych, zaznaczonych komórkach.
  • UseXORFeatures. Jeśli ustawiony jest na True, to prostokąt skupienia (focus) jest rysowany w trybie XOR, więc powinien być widoczny w połączeniu z dowolnym kolorem tła komórki. Wpływa również na wygląd przesuwanych kolumn.
  • DefaultDrawing. Wartość logiczna. Zwykle siatki przygotowują płótno (canvas) siatki przy użyciu pewnych właściwości zgodnie z rodzajem malowanej komórki. Jeśli użytkownik napisze własną procedurę obsługi zdarzeń OnDrawCell, ustawiony DefaultDrawing również maluje tło komórki. Jeśli użytkownik sam rysuje komórkę, lepiej wyłączyć tę właściwość, aby malowanie się nie duplikowało. W StringGrid ustawiony DefaultDrawing rysuje również tekst w każdej komórce.
  • AutoAdvance. Determinuje to, gdzie ma zostać przeniesiony kursor komórki po naciśnięciu klawisza Enter lub po zakończeniu edycji.
  • TabAdvance. Determinuje to, gdzie ma zostać przeniesiony kursor komórki po naciśnięciu klawisza Tab or Shift-Tab.
  • ExtendedColSizing. Jeśli ma wartość True, użytkownik może zmienić rozmiar kolumn nie tylko w nagłówkach, ale wzdłuż ich wysokości.

Inne właściwości, które również wpływają na wygląd siatek.

Options.

Właściwość Options to zestaw zawierający kilka elementów umożliwiających różnorodną funkcjonalność, ale niektóre są bezpośrednio związane z wyglądem siatki. Te opcje można ustawić w czasie projektowania lub w czasie wykonywania.
  • goFixedVertLine, goFixedHorzLine Rysuje pionową lub poziomą linię, odpowiednio oddzielającą komórki lub kolumny w obszarze stałych komórek, domyślnie aktywne.
  • goVertLine, goHorzLine Tak samo jak poprzednio, ale dla normalnego obszaru do przeglądania. Można stworzyć siatkę, która będzie symulować pole listy (ListBox), wyłączając oba te elementy.
  • goDrawFocusSelected Jeśli ten element jest włączony, tło zaznaczenia jest malowane także w skupionej (fixed) komórce oprócz zaznaczonego kropkowanego prostokąta (pamiętaj, że to nie działa jeszcze, gdy ustawiona jest opcja goRowSelect, w takim przypadku wiersz jest zawsze malowany tak, jakby ustawiono goDrawFocusSelected).
  • goRowSelect Wybierz do zaznaczania cały wiersz zamiast pojedynczych komórek.
  • goFixedRowNumbering Jeśli ustawione, siatka wykona automatyczną numerację wierszy w pierwszej stałej kolumnie.
  • goHeaderHotTracking Jeśli jest ustawione, siatka będzie próbowała pokazać inny wygląd komórki, gdy kursor myszy znajdzie się nad dowolną stałą komórką. Aby to zadziałało, żądana strefa komórki musi być włączona za pomocą właściwości HeaderHotZones. Spróbuj połączyć tę opcję z właściwością TitleStyle:=tsNative, aby uzyskać wygląd, który na bieżąco śledzi wygląd motywu widżetów.
  • goHeaderPushedLook Jeśli jest ustawione, ten element włącza wygląd wciśnięcia po kliknięciu dowolnej stałej komórki. Strefa komórek, które mogą być „wciskane” jest włączana za pomocą właściwości HeaderPusedZones.

(dopisz więcej)

Opis procesu rysowania siatki

Podobnie jak inne niestandardowe kontrolki, siatka jest rysowana metodą malowania. Ogólnie rzecz biorąc, siatkę rysuje się malując wszystkie wiersze, a każdy wiersz malując poszczególne komórki.

Proces ten wygląda następująco:

  • Najpierw określany jest obszar widocznych komórek: każdy wiersz jest testowany, aby sprawdzić, czy krzyżuje się z granicą płótna (canvas); jeśli wszystko jest w porządku, widoczny obszar jest malowany przez rysowanie kolumn każdego wiersza.
  • Wartości kolumn i wierszy służą do identyfikacji komórki, która ma zostać pomalowana, a każda kolumna jest ponownie testowana pod kątem krzyżowania się z granicą płótna; jeśli wszystko jest w porządku, niektóre dodatkowe właściwości, takie jak prostokątny zasięg komórki i stan wizualny, są przekazywane jako argumenty do metody DrawCell.
  • W trakcie procesu rysowania stan wizualny każdej komórki jest dostosowywany zgodnie z opcjami siatki i położeniem w siatce. Stan wizualny zachowany jest w zmiennej typu TGridDrawState, która jest zbiorem składającym się z następujących elementów:
    • gdSelected Komórka będzie miała wygląd wybranej, zaznaczonej.
    • gdFocused Komórka będzie miała wygląd skupionej (focus).
    • gdFixed Komórka musi być pomalowana jak komórka stała.
    • gdHot Mysz jest nad tą komórką, więc pomaluj ją z bieżącym podświetleniem
    • gdPushed Komórka jest klikana, więc pomaluj ją jako wciśniętą
  • DrawCell. Metoda DrawCell jest wirtualna i może zostać zastąpiona w siatkach potomnych w celu wykonania niestandardowego rysowania. Informacje przekazywane do DrawCell pomagają zidentyfikować konkretną komórkę, która jest malowana, fizyczny obszar zajmowany na ekranie i widoczny status. Zobacz odwołanie do DrawCell, aby uzyskać szczegółowe informacje. Dla każdej komórki wykonywane są następujące operacje:
  • PrepareCanvas. W tej metodzie, jeśli ustawiona jest właściwość DefaultDrawing, płótno siatki jest skonfigurowane z domyślnymi właściwościami pędzla i czcionki na podstawie bieżącego stanu wizualnego. W przypadku kilku właściwości wyglądu i środowiska wykonawczego wyrównanie tekstu jest ustawiane tak, aby było zgodne z wyborem programisty w niestandardowych kolumnach, jeśli takie istnieją. Jeśli DefaultDrawing ma wartość False, kolor pędzla jest ustawiony na clWindow, a kolor czcionki na clWindowText, wyrównanie tekstu jest ustawiane za pomocą wartości właściwości siatek defaultTextStyle.
  • OnPrepareCanvas. Jeśli programista napisał obsługę zdarzenia dla zdarzenia OnPrepareCanvas, jest on wywoływany w tym momencie. To zdarzenie może służyć do wykonywania prostych dostosowań, takich jak zmiana koloru tła komórki, właściwości czcionki, takie jak kolor, czcionka i styl, układ tekstu, na przykład różne kombinacje wyrównania do lewej, do środka, do góry, do dołu, do prawej itp. Wszelkie zmiany wprowadzone na płótnie w tym zdarzeniu zostałoby utracone, ponieważ następujące potem rysowanie komórki ponownie zresetuje obszar roboczy do stanu domyślnego. Więc bezpieczniej jest robić zmiany tylko dla konkretnej komórki lub komórek i zapomnieć o pozostałych. Użycie tego zdarzenia czasami pomaga uniknąć użycia zdarzenia siatki OnDrawCell, w którym użytkownicy byliby zmuszeni do powielenia kodu rysowania siatki. Do zrobienia: informacje o tym, co można zrobić tutaj, a co zostawić dla zdarzenia OnDrawCell?...
  • OnDrawCell. Następnie, jeśli nie określono obsługi zdarzenia OnDrawCell, siatka wywołuje metodę DefaultDrawCell, która po prostu maluje tło komórki przy użyciu bieżącego koloru i stylu pędzla płótna. Jeśli istnieje procedura obsługi OnDrawCell, siatka najpierw maluje tło komórki, ale tylko wtedy, gdy ustawiono właściwość DefaultDrawing, a następnie wywołuje zdarzenie OnDrawCell w celu wykonania niestandardowego malowania komórek. Zwykle programiści chcą wykonywać niestandardowe rysowanie tylko dla określonych komórek, ale standardowe rysowanie dla innych; w tym przypadku mogą ograniczyć niestandardową operację do określonej komórki lub komórek, zaglądając do argumentów ACol, Arow i AState, a dla innych komórek po prostu wywołując metodę DefaultDrawCell, aby pozwolić zająć się tym siatce.
  • Text. W tym momencie (tylko dla TStringGrid), jeśli właściwość DefaultDrawing ma wartość True, malowana jest zawartość tekstowa komórki.
  • Grid lines. Ostatnim krokiem dla każdej komórki jest malowanie linii siatki: jeśli określono opcje siatki goVertLine, goHorzLine, goFixedVertLine i goFixedHorzLine, siatka komórek jest rysowana w tym miejscu. Siatki zawierające tylko wiersze lub tylko kolumny można uzyskać, zmieniając te opcje. Jeśli programista wybrał wygląd „motywu” (tsNative), jest to również robione w tym momencie (zobacz właściwość TitleStyle).
  • FocusRect. Po pomalowaniu wszystkich kolumn bieżącego wiersza nadszedł czas na narysowanie prostokąta skupienia (focus) dla aktualnie zaznaczonej komórki lub dla całego wiersza, jeśli ustawiono opcję goRowSelect.

Różnice w porównaniu z Delphi

  • W Lazarusie metoda TCustomGrid.DrawCell nie jest abstrakcyjna i jej domyślna implementacja wykonuje podstawowe wypełnianie tła komórki.
  • W Delphi tekst komórki jest rysowany przed wejściem do zdarzenia OnDrawCell (patrz raport o błędzie #9619).

Wybór komórki w siatce

Lokalizację bieżącej (skupionej (focused)) komórki (lub wiersza) siatki można zmienić za pomocą klawiatury, myszy lub kodu. Aby skutecznie zmienić skupienie z jednej komórki na inną, musimy przetestować pozycję docelową, aby sprawdzić, czy może ona otrzymać skupienie na komórce. W przypadku korzystania z klawiatury właściwość część procesu wykonuje AutoAdvance, znajdując następną komórkę, na której ma wykonać skupienie. Podczas korzystania z kliknięć myszą lub poruszania się po kodzie, skupienie nie zostanie przeniesione z bieżącej komórki, chyba że komórka docelowa może takie skupienie otrzymać.

Siatka wywołuje funkcję SelectCell, aby sprawdzić, czy na komórce można wykonać skupienie: jeśli ta funkcja zwraca wartość True, to znaczy, że na komórce docelowej, zidentyfikowanej za pomocą argumentów aCol i aRow jest możliwe wykonanie skupienia (bieżąca implementacja TCustomGrid po prostu zwraca True). TCustomDrawGrid, a zatem także TDrawGrid i TStringGrid przesłaniają tę metodę, aby najpierw sprawdzić, czy komórka jest szersza niż 0; normalnie nie chcesz, aby wybrana została komórka o szerokości 0, więc komórka z tymi cechami jest automatycznie pomijana w procesie wyszukiwania odpowiedniej komórki. Inną rzeczą, jaką robi zastąpiona metoda SelectCell, jest wywołanie konfigurowanego przez użytkownika zdarzenia OnSelectCell: to zdarzenie odbiera współrzędne komórki jako argumenty i zawsze zwraca w wyniku domyślną wartość True.

Gdy wiadomo, że komórka może uzyskać skupienie i jesteśmy pewni, że nastąpi ruch, najpierw wywoływana jest metoda BeforeMoveSelection; to z kolei wyzwala zdarzenie OnBeforeSelection. Argumentami tej metody są współrzędne nowej komórki ze skupieniem. W tym momencie każdy widoczny edytor jest również ukryty. Słowo „before” (przed) oznacza, że ​​zaznaczenie nie zostało jeszcze zmienione, a dostęp do aktualnych współrzędnych komórki skupionej można uzyskać za pomocą właściwości grid.Col i grid.Row.

Następnie wewnętrzne współrzędne skupionej komórki są zmieniane, a potem wywoływana jest metoda MoveSelection; celem tej metody jest wyzwolenie zdarzenia OnSelection, jeśli jest ustawione (jest to powiadomienie, że skupiona komórka już się zmieniła, a współrzędne komórki są teraz dostępne we właściwościach grid.row i grid.col).

Należy zauważyć, że nie jest dobrze używać zdarzenia OnSelectCell do wykrywania zmian skupienia komórki, ponieważ to zdarzenie zostanie wyzwolone kilka razy nawet dla tej samej komórki w procesie wyszukiwania odpowiedniej komórki. Lepiej jest użyć do tego celu zdarzeń OnBeforeSelection lub OnSelection.

Różnice w porównaniu z Delphi

  • Zachowanie SelectCell i OnSelectCell jest prawdopodobnie inne — nie można tak naprawdę komentować różnic. W Lazarusie są one używane w funkcjonalności takiej jak AutoAdvance, która, o ile wiem, nie istnieje w Delphi.

Gdy powyższe dostosowanie nie wystarczy, siatki pochodne

Siatki pochodne zazwyczaj muszą zastąpić następujące metody:
DrawAllRows: Rysuje wszystkie widoczne wiersze.
DrawRow: Rysuje wszystkie komórki w rzędzie.
DrawRow rysuje wszystkie komórki w rzędzie, najpierw sprawdzając, czy komórka znajduje się w obszarze przycinania, i rysuje tylko komórkę, jeśli tak jest.
DrawCell:
DrawCellGrid:
DrawCellText:
DrawFocusRect:
(napisz do mnie).

Co dzieje się w metodzie TCustomGrid.Paint?

Poniższa lista pokazuje wewnętrzną kolejność wywołań metod dla malowania TCustomGrid (lub potomka). Każda pozycja na listach reprezentuje wywołania metod podczas operacji malowania. Powinno to pomóc w znalezieniu właściwego punktu do zmiany zachowania, jeśli chodzi o tworzenie klas potomnych z TCustomGrid.

  • DrawEdges: Rysuje zewnętrzną granicę siatki.
  • DrawAllRows (virtual): Rysuje wszystkie wiersze w widocznej części siatki. Jest to jedyna metoda wywoływana w procesie malowania, która jest zadeklarowana jako virtual (wirtualna).
    • DrawRow (virtual): Jest wywoływana dla każdego wiersza wewnątrz obecnego widoku.
      • DrawCell (virtual): Jest najpierw wywoływana dla każdej „normalnej” (tj. niestałej) komórki w rzędzie.
        • PrepareCanvas (virtual): Ustawia style rysowania na płótnie zgodnie z wizualnymi właściwościami bieżącej komórki.
        • DrawFillRect: Rysuje tło komórki ze stylami ustawionymi w PrepareCanvas.
        • DrawCellGrid (virtual): Rysuje linie obramowania komórki.
      • DrawFocusRect (virtual): W TCustomGrid ta metoda nic nie robi. W siatkach potomnych ta metoda jest używana do rysowania prostokąta skupienia wewnątrz aktywnej komórki.
      • DrawCell (virtual): (patrz powyżej) Jest wywoływana dla każdej stałej (fixed) komórki w widocznym obszarze wiersza.
  • DrawColRowMoving: Aktywna tylko podczas przenoszenia kolumny lub wiersza. Ta metoda rysuje linię wskazującą nową pozycję wiersza/kolumny.
  • DrawBorder: W razie potrzeby (Flat=TRUE i BorderStyle=bsSingle) rysuje wewnętrzną linię obramowania.

Dodatkowe metody rysowania w TCustomGrid

Te metody są zadeklarowane i (częściowo) zaimplementowane w TCustomGrid, ale nie są wywoływane bezpośrednio z tego miejsca. Są używane przez klasy potomne do rysowania zawartości komórki.

  • DrawCellText (virtual): Zapisuje/rysuje tekst, który jest przekazywany jako parametr do komórki. Tekst jest formatowany przy użyciu stylów aktywnych w Canvas.TextStyle (zobacz także PrepareCanvas).
  • DrawThemedCell (virtual): Jest używany tylko dla stałych komórek i tylko jeśli TitleStyle=tsNative. Rysuje tło komórki za pomocą ThemeServices.
  • DrawColumnText (virtual): Jest używany tylko dla komórek nagłówka kolumny.
    • DrawColumnTitleImage: Jeśli siatka ma przypisany TitleImageList i Columns.Title[x].Title.ImageIndex zawiera prawidłową wartość, to ten wybrany obraz jest rysowany w komórce.
    • DrawCellText : (patrz wyżej)
  • DrawTextInCell (virtual): Jest używany do „normalnych” (tj. niestałych) komórek. W TCustomGrid ta metoda nic nie robi. W siatkach potomnych ta metoda służy do rysowania zawartości komórek. W TSringGrid metoda ta wywołuje DrawCellText (patrz wyżej).

Metody rysowania wprowadzone przez TCustomDrawGrid

Jak widać, klasa bazowa TCustomGrid nie rysuje żadnej zawartości w komórkach. Odbywa się to w TCustomDrawGrid. TCustomDrawGrid nadpisuje metodę DrawCell w następujący sposób:

  • DrawCell (override):
    • PrepareCanvas: wywołuje odziedziczoną metodę z TCustomGrid.
    • DefaultDrawCell (virtual): Ta metoda jest wywoływana tylko wtedy, gdy procedura obsługi zdarzeń dla OnDrawCell NIE jest przypisana.
      • DrawFillRect / DrawThemedCell (od TCustomGrid): Rysuje tło komórki. Jeśli TitleStyle=tsNative, wówczas tło stałych komórek jest rysowane za pomocą DrawThemedCell.
      • DrawCellAutonumbering (virtual): Kiedy goFixedAutonumbering jest włączona w Options, to numery wierszy są rysowane w pierwszej ustalonej kolumnie przy użyciu TCustomGrid.DrawCellText.
      • DrawColumnText (z TCustomGrid): Jest wywoływana tylko dla komórek nagłówka kolumny (zobacz Dodatkowe metody rysowania w TCustomGrid).
      • DrawTextInCell (z TCustomGrid): Jest wywoływana tylko dla 'normalnych' (tj. niestałych) komórek (zobacz Dodatkowe metody rysowania w TCustomGrid).
    • DrawCellGrid (virtual): Rysuje obramowanie komórki.

Zapisz i Odzyskaj zawartość siatki

Metody SaveToFile i LoadFromFile umożliwiają siatce zapisywanie i pobieranie układu i danych do/z pliku w formacie XML. TStringGrid, odziedziczony z TCustomStringGrid, ma również możliwość „eksportowania” i „importowania” swojej zawartości do/z pliku w formacie wartości oddzielonych przecinkami, lepiej znanego jako format pliku CSV. Jest to opisane w dokumentacji do metod SaveToCSVFile i LoadFromCSVFile (do zrobienia: utworzyć łącza).

Rodzaj informacji, które można zapisać, a następnie pobrać podczas używania SaveToFile i LoadFromFile, jest określony przez właściwość SaveOptions (typu TSaveOptions), która jest zestawem opcji opisanych w następujący sposób:

soDesign:     Zapisz i wczytaj ColCount,RowCount,FixedCols,FixedRows,
              ColWidths, RowHeights i Options (TCustomGrid)
soPosition:   Zapisz i wczytaj wartości pozycji Scroll, Row, Col i Selection (TCustomGrid)
soAttributes: Zapisz i wczytaj kolory, wyrównanie tekstu i układ itp. (TCustomDrawGrid)
soContent:    Zapisz i wczytaj tekst (TCustomStringGrid)
soAll:        Ustaw wszystkie opcje (soAll:=[soDesign,soPosition,soAttributes,soContent];)

Nie wszystkie opcje mają zastosowanie do wszystkich rodzajów siatek, na przykład soContent nie mają zastosowania do siatek pochodnych TCustomGrid, takich jak TDrawGrid, ponieważ ten rodzaj siatki nie zawiera pojęcia „treści”. TStringGrid jest specjalnym rodzajem siatki, która „wie”, jak obsługiwać łańcuchy i może użyć opcji soContent, jeśli jest określona.

Opcja soAttributes nie jest używana w standardowych siatkach Lazarusa, jest dostępna w przypadku siatek potomnych.

Podczas używania LoadFromFile siatka używa również właściwości SaveOptions, aby wiedzieć, jakie informacje należy pobrać z pliku, więc np. jest całkowicie możliwe określenie SaveOptions:=[soDesign,soContent] przy zapisywaniu i tylko SaveOptions :=[soContent] podczas ładowania.

Dla TStringGrid domyślną wartością SaveOptions jest [soContent], dla innego rodzaju siatek SaveOptions jest pustym zbiorem.

Uwaga: Jeden typowy problem podczas zapisywania i pobierania danych siatki występuje, gdy użytkownik określi właściwość SaveOptions przed SaveToFile, ale nie przed LoadFromFile. W przypadku użycia LoadFromFile jakiś czas po użyciu SaveToFile właściwość SaveOptions jest ustawiona prawidłowo, ale jeśli LoadFromFile zostanie wykonane przy następnym uruchomieniu programu, właściwość SaveOptions może nie zostać poprawnie skonfigurowane, z tego powodu zaleca się, aby zawsze określać właściwość SaveOptions tuż przed LoadFromFile lub robić to globalnie podczas uruchamiania programu, jak w poniższym przykładzie:


Przykład:

  1. Najpierw przejdź do menu "Plik -> Nowy -> Aplikacja";
  2. Umieść pusty TStringGrid w formularzu;
  3. Umieść na formularzu TButton i TOpenDialog;
  4. Dodaj zdarzenie OnCreate do formularza;
  5. Dodaj zdarzenie OnClick dla przycisku.
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
 //ustawia SaveOptions w momencie tworzenia formularza
 stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
 //Pytaj, czy uruchomiono metodę Execute w OpenDialog,
 //a gdy to nastąpi, użytkownik wybiera plik XML do załadowania,
 //którego nazwa była przechowywana we właściwości FileName.

 if opendialog1.Execute then
 begin
   //Wyczyść siatkę
   StringGrid1.Clear;
   //Załaduj plik XML
   StringGrid1.LoadFromFile(OpenDialog1.FileName);
   //Odśwież siatkę
   StringGrid1.Refresh;
 end;
end;

initialization
  {$I unit1.lrs}

end.

Przykładowy plik xml: (Skopiuj poniższy tekst do pliku txt. Nie zapomnij umieścić nagłówka xml :-))

<?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>

Edytory komórek siatki

Siatka używa edytorów komórek do zmiany zawartości komórek.

W przypadku wyspecjalizowanej siatki, takiej jak TStringGrid, edytor jest zwykłą jednowierszową kontrolką edytora tekstu, ale czasami pożądane jest posiadanie innych sposobów wprowadzania informacji. Na przykład:

  1. pokaż okno dialogowe otwierania pliku, po to by znaleźć lokalizację pliku, żeby użytkownik nie musiał ręcznie wpisywać pełnej ścieżki
  2. jeśli tekst w komórce reprezentuje datę, wyświetl kalendarz, abyśmy mogli łatwo wybrać konkretną datę.

Czasami informacje, które użytkownik powinien wprowadzić w komórce, są ograniczone do ograniczonej listy słów; w takim przypadku bezpośrednie wpisywanie informacji może wprowadzać błędy i może zaistnieć potrzeba zaimplementowania procedur sprawdzania poprawności. Możemy tego uniknąć, używając edytora komórek, który przedstawia użytkownikowi listę zawierającą tylko prawidłowe wartości.

Dotyczy to również ogólnych siatek, takich jak TDrawGrid, w których użytkownik potrzebuje jakiejś struktury do przechowywania danych, które będą wyświetlane w siatce. W takiej sytuacji informacje wprowadzone w edytorze komórek aktualizują wewnętrzną strukturę, aby odzwierciedlić zmiany w siatce.

Wbudowane edytory komórek

Moduł grids.pas zawiera już niektóre z najczęściej używanych edytorów komórek gotowych do użycia w siatkach. Możliwe jest również tworzenie nowych edytorów komórek (niestandardowe edytory komórek), jeśli wbudowane edytory nie są odpowiednie do określonego zadania.

Wbudowane edytory komórek to Button, Edit i Picklist.

Korzystanie z edytorów komórek

Użytkownicy mogą określić, jaki edytor będzie używany dla danej komórki, korzystając z jednej z dwóch metod.

  1. Korzystanie z niestandardowej kolumny i wybranie właściwości ButtonStyle kolumny. W pierwszej metodzie użytkownik może wybrać styl edytora, który będzie wyświetlany. Dostępne wartości to: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn.
  2. Używanie zdarzenia siatki OnSelectEditor. W drugiej metodzie, w parametrze Editor użytkownik określa, którego edytora użyć dla komórki zidentyfikowanej dla kolumny aCol i wiersza ARow w siatce pochodnej TCustomDrawGrid lub TColumn w TCustomDBGrid. Do tego celu służy publiczna funkcja siatek EditorByStyle(), która jako parametr przyjmuje jedną z następujących wartości: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn, cbsButtonColumn. Ta metoda ma pierwszeństwo przed metodą pierwszą, gdy używane są kolumny niestandardowe. W tym miejscu można określić niestandardowy edytor komórek (patrz rozdział 5 tego dokumentu autorstwa Michaëla Van Canneyta, który wyjaśnia jak poprawnie zaimplementować niestandardowy edytor). To zdarzenie jest również miejscem do zamontowania edytora z wartościami specyficznymi dla komórki, wiersza lub kolumny (np. niestandardowe menu kontekstowe dla edytora komórek).

Ustawienie właściwości ButtonStyle działa tylko wtedy, gdy kolumna jest tworzona za pomocą polecenia StringGrid1.Columns.Add;. Użycie wyrażenia takiego jak StringGrid1.ColCount:=X; spowoduje wyjątek. Ustawienie właściwości ButtonStyle można wykonać za pomocą podobnego kodu:

if ColCB< StringGrid1.Columns.Count
   then StringGrid1.Columns.Items[ColCB].ButtonStyle:=cbsCheckboxColumn;

Opis stylów edytora

Poniżej znajduje się opis stylów edytora. Są to wartości wyliczeniowe typu TColumnButtonStyle, dlatego są poprzedzone przedrostkiem „cbs”. Ten typ został użyty, aby zachować kompatybilność z DBGrid firmy Delphi.

  • cbsAuto
To jest domyślny styl edytora dla siatek potomnych TCustomGrid. Rzeczywista klasa edytora, która będzie używana do edycji zawartości komórki, zależy od kilku czynników. W przypadku TCustomGrids używana jest klasa TStringCellEditor pochodząca z TCustomMaskEdit. Ten edytor wyspecjalizowany jest do edycji jednowierszowych ciągów string. Jest on następnie domyślnie używany w TStringGrid i TDrawGrid. W przypadku korzystania z kolumn niestandardowych, jeśli programista wypełnił właściwość PickList kolumny, działa to tak, jakby ustawiono styl edytora cbsPickList. W przypadku TCustomDBGrid, który ma pole typu boolean, zachowuje się on tak, jakby określono styl edytora cbsCheckBoxColumn. Jest to zalecana wartość dla niestandardowych edytorów komórek. Do zrobienia: odniesienie do OnEditingDone.
  • cbsEllipsis
Ten styl edytora jest najbardziej ogólny. Gdy jest używany, w komórce edycji pojawia się przycisk, a programiści mogą użyć zdarzenia siatki OnEditButtonClick, aby wykryć, kiedy użytkownik nacisnął przycisk i podjąć dowolną akcję zaprogramowaną dla takiej komórki. Na przykład programista może użyć tego stylu edytora, aby wyświetlić okno dialogowe kalendarza, aby umożliwić użytkownikowi łatwe wybranie określonej daty. Innymi możliwościami może być wyświetlenie okna dialogowego otwierania pliku w celu znalezienia plików, kalkulatora, aby użytkownik mógł wprowadzić liczbowy wynik obliczeń itp.
OnEditButtonClick to tylko powiadomienie, aby dowiedzieć się, w której komórce został kliknięty przycisk, sprawdź właściwości grid.Row i grid.Col.
A DBGrid ma specyficzne właściwości do pobierania aktywnej kolumny lub pola, a ponieważ to zdarzenie występuje w aktywnym rekordzie, może aktualizować informacje w aktywnym polu.
Ten styl edytora jest zaimplementowany przy użyciu TButtonCellEditor, bezpośredniego potomka TButton.
  • cbsNone
Ten styl edytora instruuje siatkę, aby nie używała żadnego edytora dla określonej komórki lub kolumny; zachowuje się wtedy tak, jakby siatka była tylko do odczytu dla takiej komórki lub kolumny.
  • cbsPickList
Służy do przedstawienia użytkownikowi listy wartości, które można wprowadzić. Ten styl edytora jest zaimplementowany przy użyciu TPickListCellEditor, komponentu wywodzącego się z TCustomComboBox. Lista wyświetlanych wartości jest wypełniana na jeden z dwóch sposobów, w zależności od metody użytej do wyboru stylu edytora.
  1. W przypadku używania kolumn niestandardowych programiści mogą wprowadzić listę wartości za pomocą właściwości PickList kolumny. [DLA POCZĄTKUJĄCYCH: TODO: dokładna procedura edycji listy]
  2. W OnSelectEditor programiści pobierają instancję TPickListCellEditor za pomocą funkcji EditorByStyle(cbsPickList). Zobacz tutaj przykład
Wartość w siatce TStringGrid automatycznie odzwierciedla wybraną wartość. W razie potrzeby programista może wykryć moment wybrania wartości, zapisując procedurę obsługi zdarzenia dla zdarzenia OnPickListSelect siatki, dzięki czemu można podjąć dodatkowe kroki (na przykład w celu przetworzenia nowej wartości). TODO: powiązanie z OnEditingDone.
  • cbsCheckboxColumn
Może to być przydatne, gdy zawartość danych powiązana z kolumną jest ograniczona do pary wartości, na przykład tak-nie, prawda-fałsz, włącz-wyłącz, 1-0 itp. Zamiast zmuszać użytkownika do wpisania wartości dla tego rodzaju danych w StringCellEditor lub aby wybrać jedną wartość z listy, cbsCheckboxColumn służy do modyfikowania danych kolumny za pomocą reprezentacji pola wyboru, którą użytkownik może przełączać za pomocą kliknięcia myszą lub wciskając klawisz spacji (jeśli kolumna zawierająca pole wyboru jest zaznaczona i jeśli StringGrid jest edytowalny).
Pobieranie lub ustawianie wartości pola wyboru w komórce odbywa się w następujący sposób:
StringGrid1.Cell[x,y]:='Z';
gdzie Z należy zastąpić przez 0 dla niezaznaczonego, 1 dla zaznaczonego i pusty ciąg dla wyszarzonego. Zauważ, że każda wartość (string) inna niż 0 i 1 będzie wyświetlana jako szare pole wyboru.
Jeśli właściwość ButtonStyle kolumny jest ustawiona na cbsAuto, a DBGrid wykryje, że pole powiązane z kolumną jest polem logicznym, wówczas siatka automatycznie użyje tego stylu edytora. Ten automatyczny wybór można wyłączyć lub włączyć za pomocą właściwości OptionsExtra DBGrid; ustawienie elementu dgeCheckboxColumn na wartość False wyłącza tą funkcję.
Wartości używane do rozpoznawania stanów zaznaczonych lub niezaznaczonych są ustawiane we właściwościach kolumny ValueChecked i ValueUnchecked.
W dowolnym momencie wartość pola może mieć od jednego do trzech stanów: Unchecked (niezaznaczone), Checked (zaznaczone) lub Grayed (wyszarzone). Wewnętrznie te stany są identyfikowane przez następujące wartości typu TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked i gcbpGrayed.
Ten styl edytora nie używa prawdziwych komponentów TCheckbox do obsługi interakcji użytkownika: wizualną reprezentację zapewniają trzy wbudowane obrazy bitmapowe, które odpowiadają możliwym stanom pola wyboru. Użyte mapy bitowe można dostosować, pisząc procedurę obsługi zdarzenia DBGrid OnUserCheckboxBitmap; program obsługi tego zdarzenia otrzymuje stan pola wyboru w parametrze CheckedState typu TDBGridCheckboxState oraz parametr mapy bitowej, którego programista może użyć do określenia niestandardowych map bitowych.
  • cbsButtonColumn
Ten styl edytora służy do wyświetlania przycisku w każdej komórce w kolumnie. Podobnie jak w przypadku cbsCheckboxColumn ten edytor nie używa prawdziwych przycisków, wygląd jest określony przez aktualny motyw zestawu widżetów.
Użytkownik wie, jaki konkretny przycisk został naciśnięty, obsługując OnEditButtonClick w siatce i sprawdzając kolumnę i wiersz siatki. Zauważ, że w tym konkretnym przypadku zmienne col i row siatki nie identyfikują aktualnie zaznaczonej komórki, ale komórkę klikniętego przycisku. Po obsłużeniu zdarzenia OnEditButtonClick i jeśli użytkownik nie zmodyfikował col lub row siatki w tej procedurze obsługi, siatka automatycznie resetuje col i row, aby odzwierciedlić aktualnie wybraną komórkę. Podczas obsługi OnEditButtonClick bieżący wybór siatki jest dostępny w grid.Selection, który jest właściwością typu TRect, Left i Right reprezentują indeksy kolumn, Top i Bottom są indeksami wierszy.
Podpis przycisku to odpowiedni ciąg string komórki.

Edycja siatek

Efekt edycji jest różny w zależności od rodzaju użytej siatki, na przykład TStringGrid przechowuje edytowany tekst wewnętrznie, a TDBGrid wpływa na rekordy w zestawie danych (dataset). TDrawGrid nie wie, co zrobić z edytowanym tekstem, jeśli programista nie przejmie nad nim kontroli, jest on po prostu odrzucany.
Dla TDrawGrid (chociaż powinno to działać dla wszystkich klas siatki) w celu uzyskania dostępu do edytowanego tekstu, programista może użyć zdarzenia OnSetEditText, które jest wyzwalane za każdym razem, gdy użytkownik zmodyfikuje coś w edytorze, parametry aCol i aRow tego zdarzenia identyfikują edytowaną komórkę, parametr Value przechowuje tekst, jest to zalecana metoda. Innym sposobem dostępu do edytowanego tekstu jest pobranie go bezpośrednio z edytora po zakończeniu procesu edycji za pomocą zdarzenia OnEditingDone. Można to osiągnąć, uzyskując dostęp do wewnętrznego edytora używanego do edycji, w tym przypadku domyślnego „String Cell Editor” (Komórkowy Edytor String). W tym celu istnieją dwie metody: Pierwsza polega na użyciu zdarzenia OnSelectEditor, gdzie parametr Editor jest instancją, która będzie używana do edycji komórki, musisz zapisać tę instancję do zmiennej, na przykład TheEditor (typu TSringCellEditor), do późniejszego wykorzystania w OnEditingDone. Drugą alternatywą jest użycie metody siatki EditorByStyle, ta metoda akceptuje „styl edytora” jako parametr i zwraca instancję edytora odpowiadającego temu stylowi. W naszym przypadku wiedząc, że styl cbsAuto zwraca domyślny edytor komórek, w module obsługi zdarzenia OnEditingDone możemy bezpośrednio użyć TheEditor := Grid.EditorByStyle(cbsAuto). Następnie możesz pobrać tekst za pomocą TheEditor.Text. Jeśli zastosujesz tę metodę, zauważ, że „z wielką mocą wiąże się wielka odpowiedzialność”.

Opcje i właściwości wpływające na edycję

Zachowanie edycyjne jest kontrolowane przez szereg właściwości i opcji, praktycznie każde dostosowanie lub opcje, które wpływają na edycję, wymagają edytowalnej siatki, w przeciwnym razie takie dostosowanie może zostać zignorowane.
Na początku edytowalny stan siatek jest inny, dla TDrawGrid i TStringGrid edycja jest wyłączona, dla TDbGrid jest domyślnie włączona.
Właściwość Options zawiera kilka pozycji, które zajmują się edycją, są one opisane poniżej:

  • goEditing, dgEditing (w DbGrid). Ta opcja zmienia edytowalny stan siatki, można ją zmienić w czasie wykonywania, ponieważ jest zaznaczona, gdy edycja ma się rozpocząć.
  • goAlwaysShowEditor. Zwykle edytor jest ukryty i staje się widoczny tylko wtedy, gdy jest potrzebny. Dzięki tej opcji edytor będzie widoczny przez cały czas, jeśli siatka nie jest edytowalna, opcja ta jest ignorowana, a edytor jest zawsze ukryty.

Hint dla komórek

„Hint” to właściwość wyświetlająca wskazówkę, krótką notatkę lub podpowiedź dla kontrolki, nad którą umieścimy kursor myszy. Inna nazwa tego elementu to „Tooltip”. Nazwy spotykane w języku polskim to także „dymek” lub „chmurka”.

Oprócz standardowej właściwości Hint dostępnej dla większości kontrolek, potomkowie TCustomGrid mogą wyświetlać specjalny dymek dla komórki, nad którą znajduje się kursor myszy. Ta funkcja jest kontrolowana przez opcje siatki goCellHints i goTruncCellHints (lub dgCellHints i gdTruncCellHints odpowiednio dla DBGrid):

  • goCellHints aktywuje zdarzenie OnGetCellHint w celu pobrania wyświetlanego tekstu dymku dla komórki określonej przez przekazane parametry. W przypadku TDrawGrid i TStringGrid podane są indeksy kolumn i wierszy, natomiast w przypadku TDBGrid tekst komórki może zostać wydobyty z pola przypisanego do określonego obiektu kolumny.
  • goTruncCellHints wyświetla dymek tylko wtedy, gdy tekst nie mieści się w komórce i jest obcięty; tekst podpowiedzi to nieobcięty tekst komórki. W Lazarusie 1.9+ ten tekst jest przekazywany do zdarzenia OnGetCellHint w celu dalszego doprecyzowania (np. wyświetlenie całego tekstu notatki w dymku). W przypadku skróconych tekstów komórek zaleca się również włączenie opcji goCellEllipsis (lub dgCellEllipsis dla DBGrid), która dodaje '...' do skróconego tekstu komórki.

Jeśli siatka ma wartość w swojej standardowej właściwości Hint, wówczas właściwość HintPriority może być użyta do kontrolowania, w jaki sposób wszystkie te dymki mogą zostać połączone w jedno wyskakujące okienko:

  • chpAll umieszcza tekst z właściwości Hint w pierwszym wierszu, dymek określony przez zdarzenie OnGetCellHint w drugim wierszu, a wskazówkę dotyczącą obciętych komórek w trzecim wierszu
  • chpAllNoDefault wyświetla tylko dymki dotyczące komórek podane przez zdarzenie OnGetCellHint i obcięte komórki.
  • chpTruncOnly wyświetla tylko dymki dotyczące obciętych komórek; to są ustawienia domyślne.

Ponadto standardowa właściwość siatki ShowHint musi być ustawiona na wartość True, aby wyświetlać dymki dotyczące komórek.

Grids w praktyce i przykłady

Skupienie komórki

Wykonanie skupienia (focus) dla komórki w TStringGrid jest łatwe. Zwróć uwagę, że liczenie (zwykle w programowaniu) zaczyna się od zera, a nie od 1. Aby zrobić skupienie dla komórki w 10. rzędzie i 9. kolumnie, wykonaj:

StringGrid1.Row := 9;
StringGrid1.Col := 8;

Przykład: Jak ustawić niestandardowy edytor komórek

Zobacz lazarus/examples/gridexamples/gridcelleditor/gridcelleditor.lpi (począwszy od Lazarusa 1.2)

Przykład: Jak dodać edytor przycisków

// Warunkowo pokaż edytor przycisków w kolumnie o indeksie 1 lub 2, jeśli
// komórka w kolumnie o indeksie 1 jest pusta

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;

// Uruchamianie akcji...
procedure TForm1.StringGrid1EditButtonClick(Sender: TObject);
begin
  if StringGrid1.Col = 1 then ShowMessage('kliknięto edytor kolumny 1');
  if StringGrid1.Col = 2 then ShowMessage('kliknięto edytor kolumny 2');
end;

Przykład: Praca z PickList, jak ustawić ją tylko do odczytu i jak wypełnić w czasie wykonywania

Za pomocą zdarzenia OnSelectEditor w siatce można dostosować zachowanie edytora PickList (patrz styl przycisku cbsPickList). W kolejnym przykładzie edytor listy wyboru w kolumnie 1 został zmodyfikowany tak, że w nieparzystych wierszach użytkownik może wprowadzać wartości przez ręczne wpisanie, w parzystych wierszach wartości są ograniczone do tych zawartych na liście wyboru. Ten przykład pokazuje również, jak wypełnić listę różnymi wartościami w zależności od przetwarzanego wiersza.

procedure TForm1.gridSelectEditor(Sender: TObject; aCol, aRow: Integer;
  var Editor: TWinControl);
begin
  if aCol=1 then begin
    if (Editor is TCustomComboBox) then
      with Editor as TCustomComboBox do begin
        if (aRow mod 2=0) then
          Style := csDropDown
        else
          Style := csDropDownList;
        case aRow of
          1:
            Items.CommaText := 'ONE,TWO,THREE,FOUR';
          2:
            Items.CommaText := 'A,B,C,D,E';
          3:
            Items.CommaText := 'MX,ES,NL,UK';
          4:
            Items.CommaText := 'RED,GREEN,BLUE,YELLOW';
        end;
      end;
  end;
end;

Wyrównywanie tekstu w StringGrids

Ten kod pokazuje, jak używać różnych sposobów wyrównania tekstu w kolumnach 2 i 3.

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

Wielowierszowy tekst w siatkach

Ten przykład pokazuje, jak utworzyć tekst wielowierszowy w komórce [3,2]. Działa to tak samo dla DBGrid, gdzie OnPrepareCanvas ma parametry do radzenia sobie z TColumns, a stamtąd z TFields.

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

Weryfikacja wprowadzonych wartości

Lazarus w wersji 0.9.29 wprowadza w StringGrid zdarzenie OnValidateEntry typu TValidateEntryEvent, które ma następującą deklarację:

type
  TValidateEntryEvent = procedure(sender: TObject; aCol, aRow: Integer; const OldValue: string; var NewValue: string) of object;
aCol, aRow to współrzędne sprawdzanej komórki.
OldValue to wartość, która znajdowała się w komórce cells[aCol, aRow] przed rozpoczęciem edycji.
NewValue to wartość, która zostanie ostatecznie wstawiona do komórki cells[aCol, aRow].

Ze względu na sposób, w jaki działa StringGrid, ustawiając wartość komórki podczas edycji przez użytkownika (patrz zdarzenie OnSetEditText w siatce i metoda SetEditText), gdy wyzwalane jest zdarzenie OnValidateEntry, komórka zawiera już wprowadzoną wartość (prawidłową lub nie); za pomocą argumentów zdarzenia OldValue i NewValue wartość komórki może zostać zweryfikowana i zmieniona w razie potrzeby.

Zwykle sprawdzanie poprawności następuje, gdy użytkownik przeniósł się do innej komórki. Jeśli walidacja się nie powiedzie, pożądane jest, aby edytor komórek był widoczny/aktywny, aby wpisana wartość mogła zostać poprawiona przez użytkownika. Aby siatka wiedziała, że ​​walidacja nie powiodła się, należy zgłosić wyjątek. Siatka obsłuży wyjątek poprzez Application.HandleException i każdy ruch zostanie anulowany.

Na przykład, załóżmy że komórka cell[1,1] powinna zawierać tylko wartości 'A' lub 'B', walidację można przeprowadzić za pomocą:

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
      // ustaw nową prawidłową wartość, aby użytkownik mógł na przykład nacisnąć RETURN, aby kontynuować.
      NewValue := 'A';
      // inną opcją jest powrót do poprzedniej wartości komórki (która z założenia jest prawidłowa)
      // NewValue := OldValue;
      // Użyj EAbort zamiast innego przerwania typu Exception, aby uniknąć okna dialogowego
      // z fałszywym komunikatem błędu i odwrotnego debugowania
      raise EAbort.Create('Tylko A lub B są tutaj dozwolone');
    end else begin
      // jeśli nie zostanie zgłoszony żaden wyjątek, przyjmuje się, że wartość jest prawidłowa, ale
      // w razie potrzeby ostateczną wartość można jeszcze zmienić, wypełniając NewValue inną wartością

      // komputer wie lepiej :)
      if grid.Cells[1,1]='A' then 
        NewValue := 'B' 
      else 
        NewValue := 'A';
    end;
  end;
end;

Sortowanie kolumn lub wierszy

Właściwość ColumnClickSorts umożliwia automatyczne sortowanie siatki wtedy, gdy użytkownik kliknie nagłówek kolumny. Wielokrotne kliknięcie tej samej kolumny zmienia kolejność sortowania. Wyświetlane są obrazy domyślnego sortowania kolumn, które wskazują, która kolumna została kliknięta.

W kodzie możesz użyć metody SortColRow().

Pierwszy parametr, IsColumn, to wartość logiczna. Ustaw ją na:

  • True jeśli posortowana ma być kolumna
  • False jeśli posortowany ma zostać wiersz.

Drugi parametr, index, jest liczbą całkowitą:

  • Indeks wskazuje kolumnę lub wiersz do posortowania.

Ostatnie dwa parametry są opcjonalne, określają podzakres wierszy lub kolumn do sortowania.

  • FromIndex (od kolumny lub wiersza)
  • ToIndex (do kolumny lub wiersza)

Jeśli ostatnie dwa parametry nie zostaną określone, sortowana jest cała kolumna lub wiersz.

Sortowanie wykorzystuje algorytm QuickSort, można go zmienić, jeśli siatka potomna przesłoni metodę sort() i wywoła funkcję doCompareCells w celu porównania komórek.

Domyślnie zawartość komórek sortowana jest jako ciągi string w kolejności rosnącej lub malejącej. Kolejność można wybrać za pomocą właściwości SortOrder. Domyślnie używana jest kolejność rosnąca.

// Sortuj kolumnę 3 w kolejności rosnącej
grid.SortColRow(true, 3);

// Sortuj kolumnę 3 w kolejności malejącej, pomiń wiersze stałe (fixed) u góry
grid.SortOrder := soDescending; // lub soAscending
grid.SortColRow(true, 3, grid.FixedRows, grid.RowCount-1);

Do niestandardowego sortowania liczb, dat, stanów itp. StringGrid ma zdarzenie OnCompareCells, które użytkownicy mogą wykorzystać na przykład w ten sposób:

procedure TForm1.GridCompareCells(Sender: TObject; ACol, ARow, BCol, BRow: Integer; var Result: integer);
begin
  // Wynik będzie <0, =0 lub >0 dla normalnego sortowania.
  result := StrToIntDef(Grid.Cells[ACol,ARow],0)-StrToIntDef(Grid.Cells[BCol,BRow],0);
  //W przypadku odwrotnej kolejności, wystarczy zanegować wynik (np. bazując na SortOrder siatki).
  if StringGrid1.SortOrder = soDescending then
    result := -result;
end;

Możesz użyć OnCompareCells również wtedy, gdy automatyczne sortowanie kolumn jest włączone za pomocą właściwości ColumnClickSorts.

Zastąp sortowanie automatyczne (ColumnClickSorts ustawione na False ). Unikaj niektórych kolumn. Pokaż strzałki sortowania w nagłówku kolumny

(gdy ColumnClickSorts jest ustawione na False, to w nagłówku kolumny NIE są wyświetlane strzałki sortowania, a więc musimy ustawić strzałki.)

procedure TForm1.StringGrid1HeaderClick(Sender: TObject; IsColumn: Boolean;
 Index: Integer);
begin
 if isColumn then with StringGrid1 do
  begin
    col:=Index;                                                  // Wybierz kolumnę do sortowania
    if Tag  = 0 then Tag :=-1                                    // Załóżmy, że dane w kolumnie są posortowane rosnąco
                else Tag := Tag  * -1;                           // Używanie StringGrid1.Tag do przechowywania porządku sortowania
                                                                 // Pierwsze kliknięcie nagłówka posortuje kolumnę malejąco

     // Aktywuj CompareCells (aktywuj sortowanie)
    case Index of
      0: col:=-1;                                                // Nie sortuj kolumny 0 i nie pokazuj strzałki sortowania
      1: SortColRow(True, Index);                                // Sortowanie odbędzie się w CompareCells zaraz po wykonaniu instrukcji
      2: SortColRow(True, Index);
      3: SortColRow(True, Index);
      4: SortColRow(True, Index);
      5: col:=-1;                                                // Nie sortuj kolumny 5 i nie pokazuj strzałki sortowania
    end;
  end;
end;

// Compare zwraca jedną z następujących wartości:
// jeśli A<B  zwraca: -1            ( Rosnąco )
// jeśli A=B  zwraca:  0
// jeśli A>B  zwraca:  1            ( Malejąco)

// Jak sortować: Text, Integer, Float, DateTime:

procedure TForm1.StringGrid1CompareCells(Sender: TObject; ACol, ARow, BCol,
 BRow: Integer; var Result: integer);

  function CompareTextAsText: Integer;                         // Text
  begin
    CompareText(Cells[ACol,ARow], Cells[BCol,BRow]);
  end;

  function CompareTextAsInteger: Integer;
  var i1,i2:Integer;
  begin
    if TryStrToInt(StringGrid1.Cells[ACol,ARow],i1) and       // Integer
       TryStrToInt(StringGrid1.Cells[BCol,BRow],i2) then
    Result := CompareValue(i1, i2)
    else Exit(0);
  end;  

  function CompareTextAsFloat: Integer;                        // Float
  var f1,f2:Extended;
  begin
    if TryStrToFloat(StringGrid1.Cells[ACol,ARow],f1) and
       TryStrToFloat(StringGrid1.Cells[BCol,BRow],f2) then
    Result := CompareValue(f1, f2)
    else Exit(0);
  end;

  function CompareTextAsDateTime: Integer;                  // DateTime
  var
  D1, D2 : TDateTime;
  begin
    if Trim(StringGrid1.Cells[ACol,ARow]) <> Trim(StringGrid1.Cells[BCol,BRow]) then
    begin
      if trim(StringGrid1.Cells[ACol,ARow]) = '' then Exit(1);
      if Trim(StringGrid1.Cells[BCol,BRow]) = '' then Exit(-1);
    end
    else Exit(0);

    if TryStrToDate(StringGrid1.Cells[ACol,ARow], D1, 'yy/mm/dd') and
       TryStrToDate(StringGrid1.Cells[BCol,BRow], D2, 'yy/mm/dd') then
    Result := CompareDateTime(D1, D2)
    else Exit(0);
 end;

begin                                        // Nie rób tutaj nic więcej, sortowanie może być zrobione niepoprawnie
  With    StringGrid1 do
  begin
    case ACol of
      0 : ;                                                                     // Nie sortuj kolumny 0
      1 :  Result := CompareTextAsText;                                         // Text
      2 :  Result := CompareTextAsInteger;                                      // Integer
      3 :  Result := CompareTextAsFloat;                                        // Float
      4 :  Result := CompareTextAsDateTime;                                     // DateTime
    end; {Case}
  end;
  if  StringGrid1.Tag  = -1 then   Result := -Result;                           // Zmień kolejność sortowania
end;  

// DrawCell załatwia sprawę, poprzednie obrazy strzałek są usuwane: 
procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  aRect: TRect; aState: TGridDrawState);
   
   procedure DrawArrow(ImgIndex:Integer);                  // Potrzebujesz obrazów strzałek w górę/w dół w ImageList
   begin
     with StringGrid1 do
       TitleImageList.Draw(Canvas,                         // TitleImageList z StringGrid
       ARect.Right - TitleImageList.Width,
      (ARect.Bottom - ARect.Top - TitleImageList.Height) div 2 + 2,  // Położenie strzałki można regulować.
       ImgIndex );
   end;
begin
  if Col > 0 then                                    // W tym demo nie ma strzałek w kolumnie 0
    if (ARow = 0) and                                // Załóżmy, że wiersz 0 jest wierszem tytułu
       Assigned(TitleImageList) then                 // Potrzebujesz strzałek w górę/w dół w ImageList
         case Columns.Items[aCol].Tag of             // Użyj właściwości Tag kolumny jako indeksu strzałki rosnąco/malejąco
           -1: DrawArrow(1);                         // Zastąp swoim indeksem strzałki w dół
            0: DrawArrow(-1);                        // Usuń poprzedni obraz
            1: DrawArrow(2);                         // Zastąp swoim indeksem strzałki w górę
         end;
end;

Podświetlenie wybranych komórek kolumny i wiersza

W rewizji 40276 Lazarusa dodano opcję (gdRowHighlight), która działa podobnie do goRowSelect, ale używa jaśniejszego koloru do zaznaczania, a prostokąt skupienia wskazuje zaznaczoną komórką, a nie cały rząd. Działa to dobrze w przypadku wierszy, ale co z kolumnami?.

Ta sekcja przedstawia sposób podświetlania kolumn, wierszy lub obu (przykład podświetlający tylko nagłówki kolumn i wierszy można znaleźć w lazarus/examples/gridexamples/spreadsheet, ten praktyczny poradnik jest tak naprawdę rozszerzeniem tego przykładu). Wykorzystywane są dwa zdarzenia siatki: OnBeforeSelection i OnPrepareCanvas, a rysowanie nie jest konieczne.

Zdarzenie OnBeforeSelection jest wyzwalane, gdy zaznaczenie ma ulec zmianie. W tym zdarzeniu możemy się dowiedzieć, która komórka jest aktualnie zaznaczona i jaka komórka zostanie wybrana jako następna. Używamy tych informacji, aby unieważnić cały wiersz lub kolumnę zarówno dla starych, jak i nowych komórek. Gdy rozpocznie się następny cykl malowania, siatka zostanie poinstruowana, aby pomalować komórki należące do obszarów unieważnionych. Jednym z pierwszych kroków podczas malowania jest wywołanie zdarzenia OnPrepareCanvas (jeśli istnieje) w celu ustawienia domyślnych właściwości płótna. Używamy tego zdarzenia do ustawienia podświetlonego wiersza lub kolumny:

procedure TForm1.gridBeforeSelection(Sender: TObject; aCol, aRow: Integer);
begin
  // tutaj możemy zdecydować, czy chcemy podświetlać kolumny, wiersze, czy jedno i drugie
  // w tym przykładzie podświetlamy oba

  if Grid.Col<>aCol then
  begin
    // wykryto zmianę w bieżącej kolumnie
    grid.InvalidateCol(aCol);      // unieważnia nową wybraną kolumnę komórek
    grid.InvalidateCol(grid.Col);  // unieważnia bieżącą (będzie to „stara”) wybrana kolumna
  end;
  if Grid.Row<>aRow then
  begin
    grid.InvalidateRow(aRow);      // unieważnia nowy wybrany wiersz komórki
    grid.InvalidateRow(grid.Row);  // unieważnia bieżący (będzie to „stary”) wybrany wiersz
  end;
end; 

procedure TForm1.gridPrepareCanvas(sender: TObject; aCol, aRow: Integer;
  aState: TGridDrawState);
begin
  if gdFixed in aState then
  begin
    if (aCol=grid.Col) or (aRow=grid.Row) then
      grid.Canvas.Brush.Color := clInactiveCaption; // podświetliłoby to również nagłówki kolumn lub wierszy
  end else
  if gdFocused in aState then begin
    // zostawiamy w spokoju aktualnie wybraną/skupioną komórkę
  end else
  if (aCol=Grid.Col) or (aRow=grid.Row) then
    grid.Canvas.Brush.Color := clSkyBlue; // podświetl wiersze i kolumny kolorem clSkyBlue
end;

Unikanie pustego miejsca po prawej stronie ostatniej kolumny

Aby to osiągnąć, siatka musi być w trybie kolumnowym. Gdy właściwość siatki AutoFillColumns ma wartość True, puste miejsce jest dzielone przez wszystkie kolumny, które mają niezerowy SizePriority. Dlatego powinieneś wykonać następujące kroki, aby wypełnić całą poziomą przestrzeń siatki:

  • Ustaw AutoFillColumns na True.
  • Korzystając z edytora kolumn, dodaj tyle kolumn, ile potrzebujesz.
  • Zaznacz każdą kolumnę w drzewie obiektów nad inspektorem obiektów i ustaw jej SizePriority na 0.
  • Teraz zdecyduj, która kolumna zostanie poszerzona tak, że cała pusta przestrzeń zostanie wykorzystana. Ustaw SizePriority tej kolumny na 1.

Jak przewijać siatkę w czasie rzeczywistym (przesuwanie paska przewijania za pomocą myszy)

If you scroll a Grid by moving the scrollbar with the mouse then the Grid per default is not moved "in realtime". Only after you release the mouse, the Grid "jumps" to its new position. You can enable a scrolling in "realtime" by adding Option goThumbTracking.

Example:

 StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];

Unfortunately this Option is neither documented in <lazarus-installdir>\lcl\grids.pas (ver 2.0.6) nor in https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tgridoptions.html


Jeśli przewijasz siatkę, przesuwając pasek przewijania za pomocą myszy, domyślnie siatka nie jest przesuwana „w czasie rzeczywistym”. Dopiero po zwolnieniu myszy siatka „przeskakuje” do nowej pozycji. Możesz włączyć przewijanie w czasie rzeczywistym, dodając opcję goThumbTracking.

Przykład:

 StringGrid1.Options:=StringGrid1.Options + [goThumbTracking];

Niestety ta opcja nie jest udokumentowana w <lazarus-installdir>\lcl\grids.pas (wersja 2.0.6) ani w https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tgridoptions.html

Jak zmienić rozmiar wiersza na podstawie zawartości komórek?

Lacak w raporcie o błędzie #38473 przedstawił następujący kod:

 
{ TStringGridHelper }
  TStringGridHelper = class helper for TStringGrid
    protected
      procedure AutoSizeRow(aRow: integer);
  end;

{ TStringGridHelper }
procedure TStringGridHelper.AutoSizeRow(aRow: integer);
var
  aCanvas: TCanvas;
  aCol, maxRowHeight:integer;
  aText: String;
  textRect: TRect;
begin
  aCanvas := GetWorkingCanvas(Canvas);
  maxRowHeight := DefaultRowHeight;
  for aCol:=0 to ColCount-1 do begin
    aText := Cells[aCol,aRow];
    textRect := Rect(0, 0, ColWidths[aCol], MaxInt);
    DrawText(aCanvas.Handle, PChar(aText), Length(aText), textRect, DT_CALCRECT or DT_WORDBREAK);
    if maxRowHeight < textRect.Height then
      maxRowHeight := textRect.Height
  end;
  if aCanvas<>Canvas then FreeWorkingCanvas(aCanvas);
  RowHeights[aRow] := maxRowHeight+2;
end;

Dodaj to gdzieś w swoim kodzie źródłowym, a następnie możesz zmieniać rozmiar wiersza tak, jakby procedura AutoSizeRow była częścią siatki, na przykład:

// Zmień rozmiar wiersza o indeksie 2
grid.AutoSizeRow(2);

DBGrids w praktyce i przykłady

Przykład: Jak ustawić edytor memo dla dbgrids

Możesz oczywiście użyć innej kontrolki zamiast TMemo. Dostosuj według gustu. Zaadaptowane z [1] (uproszczone użycie właściwości SelectedFieldRect, zobacz [2])

  • Umieść kontrolkę memo (lub jakąkolwiek kontrolkę, którą chcesz) na formularzu, ustaw dowolne właściwości i ustaw jej widoczność na False. Będzie ona używana podczas edycji komórki siatki. W tym przykładzie użyjemy GridCellMemo.
  • W zdarzeniu OnSelectEditor umieść następujący kod - dostosuj go, jeśli nie chcesz edytować kolumny logicznej 3, kolumny fizycznej 4:
  if (Column.DesignIndex = 3) then
  begin
      GridCellMemo.BoundsRect := DbGrid.SelectedFieldRect;
      GridCellMemo.Text:=Column.Field.AsString;
      Editor := GridCellMemo;
  end;
  • Załóżmy, że Twoje źródło danych nazywa się Datasource1, a DBGrid nazywa się ResultsGrid. Następnie w zdarzeniu OnEditingDone dla GridCellMemo umieść następujące elementy:

(Pierwotny post na forum wykorzystywał zdarzenie OnChange, które uruchamia się za każdym razem, gdy zawartość jest zmieniana. Użycie OnEditingDone jest uruchamiane dopiero po zakończeniu edycji przez użytkownika.)

  if not(Datasource1.State in [dsEdit, dsInsert]) then
    Datasource1.Edit;
    
  Datasource1.DataSet.FieldByName(ResultsGrid.SelectedField.FieldName).AsString:=GridCellMemo.Text;

Uwaga' Używanie normalnej kontrolki jako niestandardowego edytora ma wiele wad: nie zmieni rozmiaru ani nie zmieni położenia podczas zmiany rozmiaru kolumn lub przewijania siatki, musisz zadbać o modyfikację danych w siatce (jak wyjaśniono powyżej), nie działa poprawnie z DbGrids i zakłóca normalną nawigację po siatce za pomocą klawiatury. Siatka współdziała z edytorem za pomocą specjalnych komunikatów siatki, więc lepiej jest utworzyć podklasę kontrolki w celu zarządzania tymi komunikatami, jak wyjaśniono w rozdziale piątym tego dokumentu.

Uwaga Jeśli masz więcej niż jedną siatkę na tej samej formie, użyj innego niestandardowego edytora dla każdej z nich: jeśli użyjesz tego samego elementu sterującego, wydarzą się złe rzeczy, ponieważ obie siatki będą próbowały użyć go jednocześnie.

Przykład: Unikaj wyświetlania pól tekstowych jako „(Memo)” dla DBGrids

When you use SQL statements like "Select * from A" you might see "(Memo)" instead of your content in a grid cell. There are many ways to fix this, but maybe the easiest is to change your statement "Select * from A" to "Select Cast(Column as TEXT) as Column from A". This is a common problem when you use a DBGrid component.

Gdy używasz instrukcji SQL, takich jak „Select * From A”, możesz w komórkach siatki zobaczyć tekst „(Memo)” zamiast rzeczywistej zawartości. Jest wiele sposobów, aby to naprawić, ale być może najłatwiej jest zmienić instrukcję „Select * From A” na „Select Cast(Column as TEXT) as Column From A”. Jest to powszechny problem, gdy używasz komponentu DBGrid.

 var
    sql : UTF8String;
    Queryu : TSQLQuery;
.....
    sql := 'SELECT cast(Name as TEXT) as Name FROM Client';
    Queryu.SQL.Text:=sql;

Sortowanie kolumn lub wierszy w DBGrid ze strzałkami sortowania w nagłówku kolumny

To jest przykład, który sortuje DBgrid przy użyciu zdarzenia OnTitleClick oraz TSQLQuery i indeksów. Powinno to również działać w przypadku dowolnego zgodnego zestawu danych, takiego jak TbufDataset. Funkcja używa właściwości Column.Tag do przechowywania stanu sortowania dla każdej klikniętej kolumny, więc gdy wrócisz do już posortowanej kolumny, wybierze odpowiednią strzałkę sortowania do wyświetlenia.

Warunki wstępne:

  • Będzie potrzebny ImageList do przechowywania strzałek sortowania w górę/w dół. Przypisz swoją kontrolkę ImageList do właściwości TitleImageList dbgrid.
  • Upewnij się, że TSQLQuery.MaxIndexesCount jest wystarczająco duży, aby pomieścić nowe indeksy, które będziesz tworzyć. W tym przykładzie ustawiłem je na 100.
  • Będzie także potrzebna prywatna zmienna do przechowywania ostatniej kolumny użytej do sortowania. W tym przykładzie nazwałem ją FLastColumn.

W tym przykładzie użyty TSQLQuery nazywa się OpenQuery.

Template:Uwaga

Aby ponownie użyć komponentu TSQLQuery z inną instrukcją SQL, indeksy muszą zostać wyczyszczone po sortowaniu, w przeciwnym razie zostanie zgłoszony wyjątek podczas otwierania TSQLQuery z inną instrukcją SQL.

FLastColumn: TColumn; //przechowuj ostatnią kolumnę siatki, według której sortowaliśmy

procedure TSQLForm.ResultsGridTitleClick(Column: TColumn);
const
  ImageArrowUp=0; //powinien pasować do obrazu na ImageList
  ImageArrowDown=1; //powinien pasować do obrazu na ImageList
var
  ASC_IndexName, DESC_IndexName:string;
  procedure UpdateIndexes;
  begin
    // Upewnij się, że definicje indeksów są aktualne
    OpenQuery.IndexDefs.Updated:=false; {<<<--Ta linia jest krytyczna. IndexDefs.Update nie zostanie
    zaktualizowany, jeśli jest już ustawiony na True, co nastąpi w pierwszej posortowanej kolumnie.}
    Openquery.IndexDefs.Update;
  end;
begin
  ASC_IndexName:='ASC_'+Column.FieldName;
  DESC_IndexName:='DESC_'+Column.FieldName;
  // indeksy nie mogą sortować typów binarnych, takich jak ftMemo, ftBLOB
  if (Column.Field.DataType in [ftBLOB,ftMemo,ftWideMemo]) then
    exit;
  // sprawdź, czy indeks sortowania rosnąco już istnieje dla tej kolumny.
  // jeśli nie, utwórz go
  if OpenQuery.IndexDefs.IndexOf(ASC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(ASC_IndexName,column.FieldName,[]);
    UpdateIndexes; //ensure index defs are up to date
  end;
  // sprawdź, czy indeks sortowania malejąco już istnieje dla tej kolumny.
  // jeśli nie, utwórz go
  if OpenQuery.IndexDefs.IndexOf(DESC_IndexName) = -1 then
  begin
    OpenQuery.AddIndex(DESC_IndexName,column.FieldName,[ixDescending]);
    UpdateIndexes; //ensure index defs are up to date
  end;

  // Użyj właściwości Tag kolumny, aby przełączyć ASC/DESC
  column.tag := not column.tag;
  if boolean(column.tag) then
  begin
    Column.Title.ImageIndex:=ImageArrowUp;
    Openquery.IndexName:=ASC_IndexName;
  end 
  else
  begin
    Column.Title.ImageIndex:=ImageArrowDown;
    OpenQuery.IndexName:=DESC_IndexName;
  end;
  // Usuń strzałkę sortowania z poprzedniej kolumny, którą posortowaliśmy
  if (FLastColumn <> nil) and (FlastColumn <> Column) then
    FLastColumn.Title.ImageIndex:=-1;
  FLastColumn:=column;
end;

Wybieranie rekordów w DBGrid za pomocą pól wyboru

Celem jest umożliwienie wyboru dowolnych rekordów w dbgrid za pomocą pól wyboru (CheckBox), siatka ma możliwość automatycznego wyświetlania pól wyboru po wykryciu pól logicznych, dla innych typów pól użytkownik może ręcznie wybrać styl przycisku cbsCheckboxColumn dla kolumny. W przypadku tego rodzaju kolumn wystarczy kliknąć pole wyboru, a zawartość pola zostanie odpowiednio zmodyfikowana.

Ale co się stanie, jeśli w naszym zbiorze danych nie będzie takiego dostępnego pola? Czy nie chcemy, aby siatka wchodziła w stan edycji po zaznaczeniu pola wyboru? Dodanie kolumny bez pola z ButtonStyle=cbsCheckboxColumn spowoduje wyświetlenie wszystkich pól wyboru wyszarzone i wyłączone, ponieważ nie ma pola połączonego z tą kolumną, a więc nie ma nic do modyfikacji. Ponieważ chcemy sami obsłużyć stan pola wyboru, musimy przechowywać stan gdzieś dla każdego rekordu. W tym celu możemy użyć klasy TBookmarkList (zdefiniowanej w module dbgrids.pas), gdzie właściwość CurrentRowSelected może poinformować, czy bieżący rekord jest zaznaczony, czy nie. Za pomocą zdarzeń dbgrid OnCellClick i OnUserCheckboxState możemy śledzić stan pola wyboru.

Zauważ, że ta technika wymaga Lazarusa w rewizji r31148 lub nowszego, który implementuje zdarzenie OnUserCheckboxState.

...
uses ..., dbgrids, stdctrls, ...

type

  { TForm1 }

  TForm1 = class(TForm)
  ...
    procedure DBGrid1CellClick(Column: TColumn);
    procedure DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  ...
  private
    RecList: TBookmarklist;
  ...
  end;

procedure TForm1.DBGrid1CellClick(Column: TColumn);
begin
  if Column.Index=1 then
    RecList.CurrentRowSelected := not RecList.CurrentRowSelected;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RecList := TBookmarkList.Create(DbGrid1);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  RecList.Free;
end;

procedure TForm1.DBGrid1UserCheckboxState(sender: TObject; column: TColumn; var AState: TCheckboxState);
begin
  if RecList.CurrentRowSelected then
    AState := cbChecked
  else
    AState := cbUnchecked;
end;

Przykład: Jak wyeksportować DBGrid do pliku tekstowego

Istnieje kilka sposobów na utworzenie pliku tekstowego z zawartości DBGrid. Zwykle ludzie powiedzą ci, że zamiast myśleć o DBGrid jako kontenerze danych, należy spojrzeć na niego jak na połączony zestaw danych. Ale co się stanie, gdy reprezentacja tekstowa, którą chcesz uzyskać, musi pasować do wyglądu siatek, widoczności kolumn, szerokości lub kolejności? Jednym z rozwiązań jest użycie pakietu LazReport, który ma składnik przeznaczony specjalnie do tworzenia raportu z DbGrid, raport ten można następnie wyeksportować do kilku innych formatów, w tym do formatu tekstowego. Tutaj przedstawiono inne proste rozwiązanie, które może być również wykorzystane jako podstawa dla innych eksporterów formatów tekstowych, na przykład prostego eksportera html.

// aGrid: Siatka do wyeksportowania
// lpp: Ilość linii na stronę. Bez nagłówka tekstu
// pageBreak: Czy wstaw podział strony. Niektóre drukarki mogą użyć tego do rozpoczęcia nowej strony
// wymagania: moduł LazUTF8
function GridTextExporter(aGrid: TDbGrid; lpp: Integer; pageBreak:boolean): TStringList;
const
  PX_PER_CHAR = 7;

var
  Ds: TDataset;
  book: TBookMark;
  recCount: Integer;
  line: string;
  column: TColumn;

  function WidthToChar(aWidth: Integer): integer;
  begin
    result := trunc(aWidth/PX_PER_CHAR);
  end;

  procedure AddNext(theText: string; alignment: TAlignment);
  var
    width: Integer;
  begin
    if (line<>'') and (line<>#12) then
      line := line + ' ';

    width := WidthToChar(Column.Width);

    case Alignment of
      taRightJustify: line := line + UTF8PadLeft(theText, width);
      taCenter:       line := line + UTF8PadCenter(theText, width);
      else            line := line + UTF8PadRight(theText, width);
    end;
  end;

  procedure CollectHeader;
  begin
    AddNext(Column.Title.Caption, Column.Title.Alignment);
  end;

  procedure CollectField;
  var
    field: TField;
  begin
    field := Column.Field;

    if (field.DataType=ftMemo) and (dgDisplayMemoText in aGrid.Options) then
      AddNext(field.AsString, Column.Alignment)
    else if Field.DataType<>ftBlob then
      AddNext(field.DisplayText, Column.Alignment)
    else
      AddNext('(blob)', Column.Alignment);
  end;

  procedure LoopColumns(printingRecord: boolean);
  var
    c: TCollectionItem;
  begin
    if (not printingRecord) and pageBreak and (result.count>0) then
      line := #12
    else
      line := '';
    for c in aGrid.Columns do begin
      Column := TColumn(c);
      if Column.Visible and (Column.Width>=PX_PER_CHAR) then begin
        if printingRecord then CollectField
        else                   CollectHeader;
      end;
    end;
    result.add(line);
  end;

begin
  result := TStringList.create;
  Ds := aGrid.DataSource.DataSet;
  Ds.DisableControls;
  book := Ds.GetBookmark;
  try
    Ds.First;
    recCount := 0;
    while not DS.EOF do begin
      if RecCount mod lpp = 0 then
        LoopColumns(false);
      LoopColumns(true);
      inc(recCount);
      Ds.Next;
    end;
  finally
    Ds.GotoBookmark(book);
    Ds.FreeBookmark(book);
    Ds.EnableControls;
  end;
end;

Aby skorzystać z tej funkcji, wystarczy wykonać to:

var
  L: TStringList;
begin
  L := GridTextExporter(grid, 80, true);
  L.SaveToFile('grid.txt');
  L.Free;
end;

Tłumaczenie

Na język polski przełożył: --Sławomir Załęcki (talk) 17:13, 2 February 2022 (CET)