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



Синхронизация гридов


Синхронизация размеров и положения колонок двух гридов


Задача состоит в том, чтобы заставить два TDBGrid, расположенных один под другим, полностью синхронизировать свою работу с колонками: изменение размеров колонок и их перемещение должно происходить в обоих гридах отдновременно. Самое распространенное применение этой задачи в отображении грида с данными и грида с итогами (см. рис. 3). Верхний грид содержит список всех стран с данными по площади и населению(MainGrid), нижний - список, где эти же данные сгруппированы по континентам(TotalGrid).





При синхронизации действий будем считать, что тот грид, который инициирует это действие - ведущий, а второй в этой ситуации - ведомый. Чтобы не зациклить синхронизацию, введем дополнительную переменную:


SynchProccesed : Boolean;


Для синхронизации необходимо обработать три события:


  • Изменение позиции колонки;

  • Горизонтальный скролинг(изменение колонки, которая оказывается первой видимой в гриде);

  • Изменение ширины колонки.




Для отслеживания перемещения колонок воспользуемся событием OnColumnMoved. Синхронизацию проведем незатейливо: полностью перепишем колонки ведомого грида, взяв за основу колонки ведущего:



procedure TfExDBG.mainGridColumnMoved(Sender: TObject; FromIndex,
ToIndex: Integer);
Var Grid : TDBGrid;
begin
// TDBGrid(Sender) инициирует перемещение колонок, он - ведущий грид
// определяем "ведомый" грид
IF TDBGrid(Sender).Name = 'TotalGrid' Then Grid:=MainGrid
Else Grid:=TotalGrid;


// Сейчас ведомому гриду не нужно реагировать на изменение его колонок,
// инициируя в свою очередь синхронизацию с другим гридом
SynchProccesed:=True;


Grid.Columns.Assign(TDBGrid(Sender).Columns);


// Синхронизация завершена
SynchProccesed:=False;


end;




Для отслеживания горизонтального скролинга как нельзя лучше подходит метод TCustomDBGrid.TopLeftChanged. К сожалению, в стандартном TDBGrid этот метод не доступен (protected). Поэтому, лучшим вариантом будет не мучить стандартный грид, а создать собственного наследника. Положительные стороны этого способа уже описывались в начале статьи.



TexDBGrid = class(TDBGrid)
private
FOnTopLeftChanged : TNotifyEvent;
...
public
Procedure TopLeftChanged; override;
...
published
Property OnTopLeftChanged : TNotifyEvent read FOnTopLeftChanged write FOnTopLeftChanged;
...
End;
...
//--------------------------------------------------------------------------------------------------
Procedure TexDBGrid.TopLeftChanged;
Begin
Inherited;
IF Assigned(FOnTopLeftChanged) then FOnTopLeftChanged(Self);
End;




Теперь нам доступно событие OnTopLeftChanged. Синхронизация заключается в том, чтобы сделать первой видимой колонкой ведомого грида ту же колонку, что и у ведущего. Для этого нам понадобится свойство TCustomGrid.LeftCol (см. help). Это свойство protected, но так как мы создаем собственного наследника от TDBGrid, то повысить его видимость нам не составит труда.



procedure TfExDBG.GridTopLeftChanged(Sender: TObject);
Var Grid : TexDBGrid;
begin


IF NOT SynchProccesed Then
Begin


// TDBGrid(Sender) инициирует скролинг, он - ведущий грид
// определяем "ведомый" грид
IF TDBGrid(Sender).Name = 'TotalGrid' Then Grid:=MainGrid
Else Grid:=TotalGrid;


SynchProccesed:=True;
Grid.LeftCol:=TexDBGrid(Sender).LeftCol;
SynchProccesed:=False;
End;


end;




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



Procedure TfExDBG.SynchronizeGrids( MasterGrid , SlaveGrid : TDBGrid );
Var i : Integer;
Begin


IF NOT SynchProccesed Then
Begin
SynchProccesed:=True;
For i:=0 To MasterGrid.Columns.Count - 1 Do
SlaveGrid.Columns[i].Width:=MasterGrid.Columns[i].Width ;
SynchProccesed:=False;
End;


End;




А вот в какой момент применить этот метод? Ведь у грида нет события OnResizeColumn... Внимательно изучив help, обратим внимание на метод SetColumnAttributes:



Sets the column widths and disables tabbing to cells that can't be edited.


procedure SetColumnAttributes; virtual;


Description


Applications cannot call this protected method. It is called automatically when the
Columns property is recomputed, to adjust the column widths and ensure that
the user can only tab to fields that can be edited.




Этот метод автоматически вызывается всякий раз, когда изменяются настройки колонок, в том числе их ширина. Мы нашли то, что нам нужно! По аналогии с OnTopLeftChanged создадим в нашем гриде событие OnSetColumnAttr:



TexDBGrid = class(TDBGrid)
private
FOnTopLeftChanged,
FOnSetColumnAttr : TNotifyEvent;
...
protected
Procedure SetColumnAttributes; override;
public
Procedure TopLeftChanged; override;
...
published
Property OnTopLeftChanged : TNotifyEvent read FOnTopLeftChanged write FOnTopLeftChanged;
Property OnSetColumnAttr : TNotifyEvent read FOnSetColumnAttr write FOnSetColumnAttr;
...
End;
...


procedure TexDBGrid.SetColumnAttributes;
begin
inherited;
IF Assigned(FOnSetColumnAttr) Then FOnSetColumnAttr(Self);
end;




Обработаем это событие для обоих гридов:



// Так как определять ведомый грид приходится не один раз, правильно выделить это в отдельный метод
Function TfExDBG.GetSlaveGrid( MasterGrid : TexDBGrid) : TexDBGrid;
Begin
// MasterGrid инициирует синхронизацию, он - ведущий грид
// определяем "ведомый" грид
IF MasterGrid.Name = 'TotalGrid' Then Result:=MainGrid
Else Result:=TotalGrid;
End;


Procedure TfExDBG.OnSetColumnAttr(Sender: TObject);
Begin
IF NOT SynchProccesed
Then SynchronizeGrids( TexDBGrid(Sender) ,GetSlaveGrid(TexDBGrid(Sender)) );
End;




Ну а теперь, пробуйте! :о)


Для того, чтобы расслабиться перед следующим "броском", пристроим к нашему гриду несколько простых, но приятных бантиков :о)


Вызываем разные меню для заголовков и области данных


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



procedure TexDBGrid.MouseToCell(X, Y: Integer; var ACol, ARow: Integer);
Var Coord: TGridCoord;
Begin
Coord := MouseCoord(X, Y);
ACol := Coord.X;
ARow := Coord.Y;
End;




И теперь обработаем событие OnMouseUp:



procedure TfExDBG.GridMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var Row, Col : Integer;
APoint : TPoint;
Grid : TexDBGrid;
begin
Grid:=TexDBGrid(Sender);


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


IF Button = mbRight
Then // Если мышка не попала на незаполненную область грида
IF (Col >= 0) AND (Row >=0 ) Then
Begin
// Нажатие правой кнопки мыши, проверяем какое меню требуется вызвать
IF Row = 0 Then Grid.PopUpMenu:=pmTitle
Else Grid.PopUpMenu:=pmData;


// Получаем из координат мыши(относительно грида - клиентские координаты)
// экранные координаты для всплывающего меню
APoint := Grid.ClientToScreen(Point(X,Y));
Grid.PopUpMenu.Popup(APoint.X,APoint.Y);
End;
end;




Выделение цветом текущей строки


При установке в опциях грида свойства dgRowSelect, текущая строка всегда выделяется полностью, но нельзя редактировать поля. Как выделить цветом строку при условии, что любой поле можно редактировать? Основной проблемой здесь является вопрос, как понять, что строка, которая рисуется и есть текущая. Смотрим свойство TDataLink.ActiveRecord



Specifies the index of the current record within the internal
set of records buffer maintained by the dataset
for the Owner of the TDataLink object.


property ActiveRecord: Integer;


Description


Use ActiveRecord to discover or set the current record in the set
of one or more records managed by the dataset.
The set of records managed by the dataset corresponds to the number
of records from the dataset visible at one time.
For example, when the TDataLink object is owned by a data-aware grid,
the set of records managed by the dataset
corresponds to the number of rows shown by the grid,
and the ActiveRecord represents the current row.




Очень полезное свойство.



Property ActiveRecord : Integer read GetActiveRecord;
...


function TexDBGrid.GetActiveRecord: Integer;
begin
Result:=DataLink.ActiveRecord;
end;




И внесем необходимые изменения в обработку рисования строк грида:



procedure TfExDBG.mainGridDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
// Выделяем текущую строку
IF TexDBGrid(Sender).ActiveRecord = TexDBGrid(Sender).Row-1 Then
TDBGrid(Sender).Canvas.Brush.Color:=RGB($CC,$CC,$99);


IF (gdSelected IN State)
Then Begin
TDBGrid(Sender).Canvas.Brush.Color:= clHighLight;
TDBGrid(Sender).Canvas.Font.Color := clHighLightText;
End;
// А теперь пусть он рисует сам !
TDBGrid(Sender).DefaultDrawColumnCell(Rect,DataCol,Column,State);
end;




А вот еще один способ выделять строку как в RowSelect (из Demo к TDBGridEh Дмитрия Большакова):



procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
Grid : THSDBGrid;
begin
Grid := THSDBGrid(Sender);
if (Rect.Top = Grid.CellRect(Grid.Col, Grid.Row).Top) and
//В данном случае проверяется, что мы рисуем текущую строку
(not (gdFocused in State) or not Grid.Focused) then
//И фокус находится не на текущем столбце или фокус вообще не на гриде
Grid.Canvas.Brush.Color := TColor($D86A10);
Grid.DefaultDrawColumnCell(Rect,DataCol,Column,State);
end;




Для этого надо в нашем наследнике объявить в секцию public процедуру CellRect, как



function CellRect(ACol, ARow: Longint): TRect;




а ее реализацию выполнить:



function TexDBGrid.CellRect(ACol, ARow: Integer): TRect;
begin
Result := inherited CellRect (ACol, ARow);
end;




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


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

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