Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / 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



Имитация внутренних группировок и метки колонок


Работая с заголовками мы не один раз их перерисовывали, вписывая текст и добавляя 3D-окантовку. Это умение можно использовать в любом месте сетки грида, а не только в заголовках.


Рисуем ячейку в стиле заголовка в любом месте TDBGrid


Добавим нашему гриду еще один метод - DrawCellButton, который будет рисовать в любой ячейке 3D-окантовку, то есть делать имитацию заголовка. Передавать в нее будем прямоугольник этой ячейки, текст, выравнивание текста, шрифт, которым текст будет выведен и состояние (State) грида. Состояние нам понадобится для нормальной работы с фиксированными колонками.



procedure TexDBGrid.DrawCellButton(Rect: TRect; Text: String;
Style: TFontStyles; State: TGridDrawState; Alignment: TAlignment);
Var Shift : Integer;
begin
//Очищаем ячейку
Canvas.Brush.Color:=clBtnFace;
Canvas.Font.Color:=clBtnText;
Canvas.Font.Style:=Style;
Canvas.FillRect(Rect);


// Если ячейка фиксирована, то мы получим TRect меньшего размера,
// чем для обычной ячейки. Это нужно учесть
Shift:=-2 + ORD(gdFixed In State);


// вписываем текст
InflateRect(Rect,Shift,0);
WriteText(Canvas, Rect, Text , Alignment );
InflateRect(Rect,(-1)*Shift,0);


// рисуем по размеру ячейки button
// только если это не фиксированная ячейка, так как для нее окантовка уже нарисована
IF NOT (gdFixed in State) Then
Begin
// Рисуем аналог разделительных линий между фиксированными ячейками грида
// (они рисуются черным цветом, в отличие от серых линий между ячейками
// данных (grids.pas))
InflateRect(Rect, 1, 1);
Rect.Top:=Rect.Top + 1;
FrameRect(Canvas.Handle, Rect, GetStockObject(BLACK_BRUSH));


Rect.Top:=Rect.Top - 1;
// Закончили имитацию линий между фиксированными ячейками.
InflateRect(Rect, -2, -2);
Paint3dRect(Canvas.Handle, Rect);
End;


end;




Такой, на первый взгляд экзотический, вариант ячейки поможет нам создать видимость внутренних группировок в гриде (рис. 4).





Имитация внутренних группировок


Для создания внутренних группировок необходимо подготовить не только TDBGrid, но и набор данных, которые он будет отображать. Ведь TDBGrid не умеет показывать строк, которых нет в его источнике данных (TDataSource). Подготовим данные по такому запросу: выберем всю информацию по странам и добавим список континентов с суммами полей "население" и "площадь". Обычный UNION-запрос:



Select 1 as TypeRecord , Continent , Name, Area , Population
From country
Union
Select 0 as TypeRecord,Continent ,Continent as Name, Sum(Area) as Area , Sum(Population) as Population
From country
Group By Continent
Order by 2,1




Итак, мы получим для каждого континента список его стран и еще одну запись, которую мы будем использовать как служебную запись для группировки, суммы по континенту эта строка уже содержит. Идентифицировать служебную запись можно по значению служебного поля TypeRecord (именно для этого оно и введено). Добавим в обработку события OnDrawColumnCell рисование группировочной строки:



procedure TfExDBG.__GridFixDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
Var Alignment : TAlignment;
begin
// рисуем на строке итогов имитацию заголовка грида
IF Column.Field.DataSet.FieldByName('TypeRecord').AsInteger = 0
Then TexDBGrid(Sender).DrawCellButton(Rect,Column.Field.DisplayText,[fsBold],State,Alignment)
end;




Вуаля! :о)


А вот еще один вариант группировок - без итогов по каждой колонке, только отделение групп данных друг от друга (рис. 5).





Для его реализации добавим метод, аналогичный DrawCellButton, вернее создадим новый на его основе. Метод DrawRowButton делает тоже самое, что и DrawCellButton, но только всегда растягивает картинку на всю видимую строку грида.



procedure TexDBGrid.DrawRowButton(Rect: TRect; Text: String; Style: TFontStyles; Alignment: TAlignment);
Var FullRect : TRect;
Col : TColumn;
begin
FullRect:=Rect;
FullRect.Left:=IndicatorWidth + 1;
FullRect.Right:=CalcTitleRect(Columns[Columns.Count-1],0,Col).Right;


DrawCellButton(FullRect,Text,Style,[],Alignment);
end;




Метки колонок: рисуем в заголовке TCheckBox или TRadioButton


Вновь вернемся к заголовкам. Допустим нам надо реализовать возможность как-то отметить колонку. В принципе для таких целей может служить два контрола TCheckBox и TRadioButton. Для рисования в заголовках воспользуемся специальным событием нашего нового грида: OnDrawTitleRect



procedure TfExDBG.OnDrawTitleRect(Sender: TObject; ACol: Integer; Column: TColumn; ARect: TRect);
Var Style, TypeButton : Word;
FRect : TRect;
begin
IF ACol >= TexDBGrid(Sender).FixedCols Then
Begin
InflateRect(ARect, -1, -1);


TDBGrid(Sender).Canvas.FillRect(ARect);


// Ширина прямоугольника для рисования контрола - 20 пикселей
FRect:=ARect;
IF RectWidth(FRect) > 20 Then FRect.Right:=FRect.Left + 20;


// Определяем отмечено или нет текущее поле
IF Column.Field.Tag = 1
Then Style:=DFCS_CHECKED
Else Style:=0;


// Выбираем тип контрола для отметки колонки
IF FTitleIsCheckBox
Then TypeButton:=DFCS_BUTTONCHECK
Else TypeButton:=DFCS_BUTTONRADIO;


// Рисуем отметку
DrawFrameControl(TDBGrid(Sender).Canvas.Handle, FRect, DFC_BUTTON, TypeButton OR Style);


FRect.Left:=FRect.Right + 1;
FRect.Right:=ARect.Right;


// Текст заголовка
WriteText(TDBGrid(Sender).Canvas,FRect,Column.Title.Caption,Column.Title.Alignment);
End;
end;




Обработку нажатия на метку колонки проводим в обработчике события OnMouseUp. В приведенном примере для хранения отметки столбца используется свойство TField.Tag. Естественно, это только один из возможных вариантов.



procedure TfExDBG.GridFixMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Const MinX = 2;
MaxX = 20;
Var Row, Col ,
i : Integer;
Grid : TexDBGrid;
Begin
Grid:=TexDBGrid(Sender);


// Получим номер строки и столбца грида, над которыми произошел клик мышкой
Grid.MouseToCell(X,Y,Col,Row);


IF Button = mbLeft
Then Begin
// Левая кнопка мыши - проверяем попадание в заголовок
// и обязательное попадание на сам крыжик
IF (Row = 0) AND (Col > Grid.FixedCols ) AND
(Grid.Columns[Col - 1].Field <> nil)
Then Begin
Dec(X, Grid.TitleRect(Col-1).Left);


// Проверяем попадание в область крыжика
IF (X > MinX) and (X < MaxX) Then
Begin
Tag:=Grid.Columns[Col - 1].Field.Tag;


// Снимаем отметку со всех колонок (если это TRadioButton)
IF NOT FTitleIsCheckBox
Then For i:=0 To Grid.Columns.Count - 1 Do Grid.Columns[i].Field.Tag:=0;


// И отмечаем текущую
Grid.Columns[Col - 1].Field.Tag:=1 - Tag;


// Перерисовываем только заголовки, а не весь грид
Grid.RefreshTitles;
RefreshSelect;
End;
End;
End;
End;




Использование фиксированных колонок


И последнее, что мы сотворим с нашим гридом :о), это снабдим его свойством FixedCols, которого так не хватает в стандартном TDBGrid'е.


Для тех, кто может быть не знает, отметим, что у стандартного TDBGrid есть фиксированный столбец, он используется гридом для внутренних нужд. Это тот самый индикатор слева, в котором рисуется треугольник, указывая на текущую строку. Добавляя свое свойство FixedCols, необходимо это учитывать



TexDBGrid = class(TDBGrid)
private
...
FFixedCols : Integer;
...
public
Property FixedCols : Integer read GetFixedCols write SetFixedCols;
...


//**************************************************************************************************
procedure TexDBGrid.SetFixedCols(const Value: Integer);
Var FixedCount,i : Integer;
begin


// Следует учесть индикатор грида
IF Value <= 0 Then FixedCount:=IndicatorOffset
Else FixedCount := Value + IndicatorOffset;


IF DataLink.Active AND NOT (csDesigning in ComponentState) AND (ColCount > IndicatorOffset + 1) Then
Begin
IF FixedCount >= ColCount Then FixedCount:=ColCount - 1;


Inherited FixedCols := FixedCount;


// На фиксированных колонках нельзя останавливаться по табуляции
For i := 1 To FixedCols Do
TabStops[I] := False;
End;


FFixedCols := FixedCount - IndicatorOffset;
end;
//**************************************************************************************************
function TexDBGrid.GetFixedCols: Integer;
begin
IF DataLink.Active Then Result := Inherited FixedCols - IndicatorOffset
Else Result := FFixedCols;
end;
//**************************************************************************************************




Необходимо восстанавливать данные о фиксированных колонках каждый раз, когда параметры колонок будут пересчитываться. Смотрите в иллюстрирующем проекте процедуры TexDBGrid.LayoutChanged; и TexDBGrid.SetColumnAttributes.


Для того, чтобы в нашем гриде фиксированные колонки вели себя также, как ведут они себя, например, в TStringGrid, нужно обработать реакцию на мышь и клавиатуру.


//**************************************************************************************************
Procedure TexDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
Var KeyDownEvent: TKeyEvent;
Begin
KeyDownEvent := OnKeyDown;
IF Assigned(KeyDownEvent) Then KeyDownEvent(Self, Key, Shift);


IF NOT Datalink.Active OR NOT CanGridAcceptKey(Key, Shift) Then Exit;


// наша задача - не пустить в область фиксированных колонок,
// то есть SelectedIndex не может быть меньше, чем FFixedCols
IF ssCtrl IN Shift Then
Begin
IF (Key = VK_LEFT) AND (FixedCols > 0) Then
Begin
SelectedIndex := FixedCols;
Exit;
End;
End
Else Case Key Of
VK_LEFT: IF (FixedCols > 0) AND NOT (dgRowSelect in Options)
Then IF SelectedIndex <= FFixedCols Then Exit;


VK_HOME: IF (FixedCols > 0) AND (ColCount <> IndicatorOffset + 1)
AND NOT (dgRowSelect IN Options) Then
Begin
SelectedIndex := FixedCols;
Exit;
End;
End;


OnKeyDown := Nil;
Try
Inherited KeyDown(Key, Shift);
Finally
OnKeyDown := KeyDownEvent;
End;


end;
//**************************************************************************************************
procedure TexDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var Cell : TGridCoord;
begin


Cell:=MouseCoord(X,Y);


//При скроллировании данных фиксированные колонки должны оставаться на месте
IF (Cell.X >= 0) AND (Cell.X < FixedCols + IndicatorOffset) AND Datalink.Active Then
Begin
IF (dgIndicator IN Options)
Then Inherited MouseDown(Button, Shift, 1, Y)
Else IF (Cell.Y >= 1) AND (Cell.Y - Row <> 0)
Then Datalink.Dataset.MoveBy(Cell.Y - Row);
End
Else inherited MouseDown(Button, Shift, X, Y);


end;
//**************************************************************************************************


Вот, собственно и все, что мы хотели рассказать.


Игорь Шевченко, Елена Филиппова, Королевство Дельфи


<<<  Назад
 1  2  3  4  5  6 


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

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