Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Pascal и Delphi /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


НеОбычный TDBGrid - Программирование от RIN.RU
НеОбычный TDBGrid



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


А теперь снова вернемся к заголовкам и пойдем по дорожке, только что проложенной в самом начале статьи. Если мы умеем рисовать в заголовках, то мы можем очень многое, практически все :о)


На рис.2 изображен грид со сложными заголовками. Разберем один из возможных способов достижения такого результата.


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


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





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





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





Реализация описанной методики в нашем наследнике 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 привязывать не ко всему гриду, а к каждой его колонке.





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


Запрет перемещения колонок с разрешением менять их ширину


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


В опциях грида объединены запрет/разрешение на передвижение колонок и на изменение их ширины (dbColumnResize). Если запретить перемещать колонки, тогда нельзя будет менять их ширину. В нашем случае это неудачное сочетание будет крайне неудобно с точки зрения пользователя. Введем еще одно поле, которое будет отдельно запрещать перемещение колонок:



TexDBGrid = class(TDBGrid)
private
...
FAllowColumnMoved: Boolean;
...
public
Property AllowColumnMoved : Boolean read FAllowColumnMoved write SetAllowColumnMoved;




Изучив исходные коды DBGrids.pas, обратим внимание на метод BeginColumnDrag (см. help). Этот метод вызывается тогда, когда начинается перетаскивание колонок.


Переопределим его в нашем наследнике:



function TexDBGrid.BeginColumnDrag(var Origin, Destination: Integer; const MousePt: TPoint): Boolean;
Begin
Result:=FAllowColumnMoved;


// Разрешить передвигать колонки только если это разрешено в настройках: AllowColumnMoved
IF Result Then Result:= Inherited BeginColumnDrag(Origin,Destination,MousePt);
End;




Так как мы контролируем непосредственно начало процесса перемещения, то возможность менять ширину колонок остается у пользователя.


<<<  НазадВперед  >>>
 1  2  3  4  5  6 


 8  Комментарии к статье  8 8  Обсудить в чате

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь