Delphi - сбориник статей


Сложные заголовки


Изначально наш грид выглядит вот так:



Для того, чтобы добавить объединяющие заголовки для существующих, совершенно явно следует увеличить по высоте область заголовков грида.



И в нужном месте дорисовать самим объединяющую часть заголовка.



Реализация описанной методики в нашем наследнике TexDBGrid:
  1. Введем свойство, которое будет включать/выключать режим сложных заголовков.

    TexDBGrid = class(TDBGrid) private FSubHeader : Boolean; // подзаголовки ... published Property SubHeader : Boolean read FSubHeader write SetSubHeader;
    Именно это свойство будет регулировать высоту области заголовков.

    ... Const TITLE_SUBHEADER = 2; TITLE_DEFAULT = 1; ... //******************************************************* procedure TexDBGrid.CalcTitle; begin RowHeights[0] := 19 * FTitleLines ; end; //******************************************************* procedure TexDBGrid.SetSubHeader(const Value: Boolean); begin FSubHeader := Value; IF FSubHeader Then FTitleLines:=TITLE_SUBHEADER Else FTitleLines:=TITLE_DEFAULT; CalcTitle; end;

  2. В метод TexDBGrid.DrawCell добавляем обработку

    IF FSubHeader Then Begin // Рисуем объединяющий заголовок Header к мелким заголовкам Title DrawSubHeader(DataCol, Canvas); // Рисуем заголовки Title FRect:=ARect; FRect.Top:=RectHeight(ARect) div FTitleLines; DrawTitleCell(FRect,Columns[DataCol]); End Else DrawTitleCell(FRect,Columns[DataCol]);
    Здесь рисование заголовка разбито на две процедуры: DrawSubHeader и DrawTitleCell. Где DrawTitleCell рисует в прямоугольнике 3D-окантовку, заливает его цветом FixedCols и вписывает текст. То есть имитирует обычный заголовок колонки. А вот на процедуре DrawSubHeader остановимся поподробнее.

  3. Для того, чтобы нарисовать объединяющий заголовок для нескольких колонок, нужно получить прямоугольник (TRect), который объединяет эти колонки и текст, который следует писать в объединяющем заголовке. Для обеспечения гибкой настройки создадим два свойства:

    published Property OnGetHeaderText : TOnGetHeaderText read FOnGetHeaderText write FOnGetHeaderText; Property OnGetHeaderRect : TOnGetHeaderRect read FOnGetHeaderRect write FOnGetHeaderRect;
    С помощью этих свойств можно будет настраивать обработчики соответствующих событий.

    Procedure DrawSubHeader(ACol : Integer; Canvas : TCanvas); Var HRect : TRect; Begin // Получаем прямоугольник, объединяющий несколько колонок, // для которых рисуем сложный заголовок HRect:=GetHeaderRect(ACol); // По высоте берем только часть прямоугольника // так как вторая часть — обычный заголовок HRect.Bottom:=RectHeight(HRect) div TITLE_SUBHEADER; Canvas.FillRect(HRect); // Вписываем текст, // который получаем методом GetHeaderText InflateRect(HRect,-1,-1); WriteText(Canvas, HRect, GetHeaderText(ACol) , taCenter); // Рисуем 3D-окантовку Paint3dRect(Canvas.Handle,HRect); End;
    Внутри методов GetHeaderRect и GetHeaderText будут вызываться обработчики событий FOnGetHeaderRect и FOnGetHeaderText.

    При этом, следует помнить, что в каждый момент могут быть видны не все колонки из объединенных в блок. Воспользуемся функцией TCustomDBGrid.CalcTitleRec, которая возвращает прямоугольник для определенной колонки и строки. Если в данный момент эта колонка не видна, то будет возвращен нулевой прямоугольник.

    Function TexDBGrid.GetHeaderRect(ACol : Integer) : TRect; Var MasterCol : TColumn; Index,Shift , Count,i : Integer; Begin // Если в опциях отключен показ сетки, это нужно учесть при расчете // общего прямоугольника IF [dgColLines] * Options = [dgColLines] Then Shift:=1 Else Shift:=0; Index:=ACol; Count:=1; // получаем информацию для текущей колонки грида: // в какой объединяющий блок она входит // Index — с какой колонки начинается объединяющий блок // Count — сколько колонок он включает IF Assigned(FOnGetHeaderRect) Then FOnGetHeaderRect(ACol, Index, Count); IF Index+Count-1 > Columns.Count-1 Then Begin Index:=ACol; Count:=1; End; // В результате нужно получить прямоугольник, состоящий из // всех, включенных в объединенный блок колонок Result:=CalcTitleRect(Columns[Index],0,MasterCol); For i:=Index+1 To Index + Count -1 Do Result.Right:=Result.Right + RectWidth(CalcTitleRect(Columns[i] ,0,MasterCol)) + Shift; End;

И для примера покажем, как именно могут использоваться обработчики событий получения объединяющего прямоугольника и текста при использовании сложных заголовков:

Const GeoColumns = 3; ParamColumns = 2; ... //---------------------------------------------------------------------------------------- // Получить для текущей колонки информацию о том, в какое объеденение колонок она попадает //---------------------------------------------------------------------------------------- procedure TfExDBG.GetHeaderRect(ACol: Integer; var IndexStart, Count: Integer); begin IF ACol < GeoColumns Then Begin IndexStart:=0; Count:=GeoColumns; End Else Begin IndexStart:=GeoColumns; Count:=ParamColumns; End end; //---------------------------------------------------------------------------------------- // Получить для текущей колонки текст заголовка объеденени //---------------------------------------------------------------------------------------- procedure TfExDBG.GetHeaderText(ACol: Integer; var Text: String); begin IF ACol < GeoColumns Then Text:='География' Else Text:='Параметры'; end; //----------------------------------------------------------------------------------------

Предложенный способ просто один из возможных, он не позволяет настраивать параметры объединяющих заголовков в design-time, рассчитан на использование двухуровневых заголовков и предполагает наличие сложных заголовков у всех колонок грида.

Например, для того, чтобы сделать так, как показано на рисунке ниже, следует свойство SubHeader привязывать не ко всему гриду, а к каждой его колонке.

Рассказать о реализации всех вариантов сложных заголовков не представляется возможным. Изучив наши примеры, Вы можете сами совершенствовать новый грид, по собственному усмотрению.




Начало  Назад  Вперед