Задача состоит в том, чтобы заставить два TDBGrid, расположенных один под другим, полностью синхронизировать свою работу с колонками: изменение размеров колонок и их перемещение должно происходить в обоих гридах отдновременно. Самое распространенное применение этой задачи в отображении грида с данными и грида с итогами (см. рис. 3). Верхний грид содержит список всех стран с данными по площади и населению(MainGrid), нижний — список, где эти же данные сгруппированы по континентам(TotalGrid).
рис. 3
При синхронизации действий будем считать, что тот грид, который инициирует это действие — ведущий, а второй в этой ситуации — ведомый. Чтобы не зациклить синхронизацию, введем дополнительную переменную: SynchProccesed : Boolean; Для синхронизации необходимо обработать три события:
//--------------------------------------------------------------------------------------------------
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; //-------------------------------------------------------------------------------------------------- |
Этот метод автоматически вызывается всякий раз, когда изменяются настройки колонок, в том числе их ширина. Мы нашли то, что нам нужно! По аналогии с 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; //---------------------------------------------------------------------------------------- |
Для того, чтобы расслабиться перед следующим "броском", пристроим к нашему гриду несколько простых, но приятных бантиков :о)