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

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


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

При синхронизации действий будем считать, что тот грид, который инициирует это действие — ведущий, а второй в этой ситуации — ведомый. Чтобы не зациклить синхронизацию, введем дополнительную переменную: 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; //----------------------------------------------------------------------------------------
Ну а теперь, пробуйте! :о)

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



Содержание раздела