Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 181. – úprava StringGridu

Jan Šindelář 20.4.2005

Vylepšování vlastností standardních komponent je jedním z našich častých témat a dnes tomu nebude jinak. Na řadě je StringGrid, který si o vylepšení přímo říká. Opravíme si jednu nepříjemnou vlastnost této komponenty a přidáme i několik dalších vizuálních vylepšení.

Začneme od té nepříjemné vlastnosti, kterou komponenta má. Těžko ji označit přímo za chybu, prostě je to jedna z vlastností, se kterou se musíme nějak vypořádat. Oč se tedy jedná? Problém je v počtu řádků. Nastavíme-li si první řádek jako pevný (Fixed) kvůli umístění hlavičky, ale poté klesne počet řádků se samotnými daty na nula (tedy tabulka obsahuje řádek jen jeden, právě ten první s hlavičkou), dojde k tomu, že první řádek změní svoji vizuální podobu z původního hlavičkového 3D formátu na vzhled obyčejné buňky. Pokud poté opět data do tabulky vložíme, formát se už však nevrátí do původního stavu a první řádek bude stále zobrazován obyčejným způsobem a nebude "fixed". Je to prostě dané tím, že celkový počet fixních řádků musí být nižší než celkový počet řádků.

Opravíme si tedy tento problém, aby se při opětovném zvýšení počtu řádků vrátila i hlavička. To zařídíme velmi snadno. Vytvoříme si pro tento účel zvláštní proceduru UpdateGrid, kterou budeme volat vždy po aktualizaci tabulky. Procedura nám zjistí počet řádků tabulky (resp. budeme pro jednoduchost tento počet předávat parametrem) a pokud se jejich počet opět zvětší (tzn. celkový počet bude větší než 1), nastavíme první řádek opět jako fixní. Kód této jednoduché procedury může vypadat třeba takto:

procedure TForm1.UpdateGrid(rowCount: integer);
begin
  StringGrid1.RowCount := rowCount;
  if StringGrid1.RowCount > 1 then StringGrid1.FixedRows := 1;
  StringGrid1.Invalidate;
end;

Toto ovšem není nic úžasného, ale přesto to může někomu stačit. Pořád nám však zůstává problém, že při snížení počtu řádků na jeden se změní hlavička, tedy zobrazí se jako běžné buňky. Zde už musíme zasáhnout poněkud více a použít opět vlastní způsob vykreslování objektu. Naše pozornost se tedy musí zaměřit na událost OnDrawCell.

V této proceduře nejprve budeme zjišťovat, který řádek vykreslujeme. Nás dnes bude zajímat pouze řádek první (který má index nula). Připravíme si prostor pro vykreslení textu, přičemž budeme vycházet ze souřadnic původního obdélníku buňky (který získáme z parametrů volané události). Přidáme však menší odsazení od pomyslného okraje buňky, přičemž toto odsazení je definováno konstantou TXT_MARG. Dále nastavíme základní vlastnosti písma a pak následuje samotné vykreslení textu. K tomu použijeme API funkci DrawText, v jejíchž parametrech ještě můžeme upravit některé vlastnosti jako je například zarovnání textu. Podrobný popis této funkce včetně parametrů najdete v nápovědě Delphi a nebo online na MSDN. Poslední řádky kódu pak obstarají vykreslení obdélníku kolem buňky, čímž dosáhneme jednoduchého trojrozměrného efektu.

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
  function GetText(ACol, ARow: integer): string;
  begin
    if ARow = 0 then result := format(`Sloupec č. %d`, [ACol])
  end;
var
  txtRect: TRect;
  str: string;
begin
  if ARow = 0 then
  begin
    txtRect := Rect;
    txtRect.Left := Rect.Left + TXT_MARG.x;
    txtRect.Top := Rect.Top + TXT_MARG.y;
    with StringGrid1.Canvas do
    begin
      Brush.Color := clBtnFace;
      Font.Style := [fsbold];
      Font.Color := clWindowText;
      Font.Name := StringGrid1.Font.Name;
      Font.Size := StringGrid1.Font.Size;
      FillRect(rect);
    end;
    str := GetText(ACol, ARow);
    DrawText(StringGrid1.canvas.Handle, PChar(str), length(str), txtRect, DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_END_ELLIPSIS);
    with StringGrid1.Canvas do
    begin
      Pen.Style := psSolid;
      Pen.Width := 1;
      Pen.Color := clWhite;
      Polyline([point(rect.left, rect.bottom), rect.TopLeft, point(rect.Right, rect.top)]);
      Pen.Color := clBtnShadow;
      Polyline([point(rect.left + 1, rect.bottom - 1), point(rect.right - 1, rect.bottom – 1), point(rect.Right - 1, rect.Top + 1)]);
    end;
  end;
end;

Tím jsme docílili požadovaného efektu, kdy hlavička je zobrazena i v případě, že tabulka neobsahuje žádné další řádky. Pro dnešek tedy končíme a příště si náš příklad rozšíříme ještě o další vylepšení jako například malá tlačítka či zatržítka přímo uvnitř buněk tabulky a podobně.

Obě funkce najdete opět v ukázkovém projektu, který můžete stahovat zde.

Zdroj: Vegar Vikan