TArrayGrid v1.3 (September 1999). Delphi4 Component.

ã 1999 Alex Konshin

mailto:alexk@mtgroup.ru

http://www.mtgroup.ru/~alexk

 

Краткое описание

Очень кратко о том, что такое ArrayGrid.

 

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

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

 

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

 

Реализован multiselect для строк (отметка прямоугольных регионов IMHO вообще мало когда нужна), логика отметки позволяет раз/отмечать несколько участков (в отличии от логики explorer, где нельзя это сделать с помощью Shift, только через Ctrl, что иногда бывает очень утомительно).

 

Совсем по-другому сделан скроллинг по горизонтали - он плавный, хотя с помощью Ctrl+стрелка можно скакать по колонкам почти как в обычном гриде.

 

Пока есть только пять типов ячеек: выровненная влево строка, выровненная вправо строка, целое число, деньги и дата/время (кстати, числа редактируются довольно забавно - они не прыгают влево при редактировании и грамотно реагируют на десятичную точку). При большом желании можно сделать собственную отрисовку ячеек, про собственное редактирование, pickuplist и т.п. думаем, может сделаю.

 

Пока есть только два варианта подключения данных, но оба требуют написания наследников от абстрактных классов.

 

Первый способ - наиболее общий: написать наследника от TAbstractDataConnector. Для этого достаточно переопределить методы GetAs*/SetAs* и, если необходим multiselect, то и Get/SetSelected. Есть также возможность перекрытия других методов для оптимизации.

 

Второй способ - упрощение для наиболее частой ситуации: обычно есть какой-то массив данных, которые хочется смотреть/редактировать в гриде. Для этого реализуем соответствующий динамический массив объектов на основе абстрактных классов TGridDataArray и TGridDataArrayItem. Далее либо перекрываем GetAs/SetAs (перекрывать Get/SetSelected не надо - уже реализовано), либо объявляем published  свойства и в дизайнере для соответствующих колонок грида прописываем имена этих свойств. В обработчике события OnCreate формы создаем этот массив и, когда приходит время загрузить и отрисовать данные (например, в обработчике события OnShow формы), присваиваем его свойству грида DataObject. Простейшие примеры, находящиеся в директории ArrayGridSample и ArrayGridRTTI, демонстрирует, как это должно выглядеть. Естественно, использование published более просто, но это несколько медленнее.

 

Рекомендуется также перекрыть методы RowHandle и RowByHandle – это позволит гриду более правильно позиционировать текущую строку при обновлении данных (например, пользователь изменил условия отбора данных из базы) и изменении порядка сортировки. Метод RowHandle должен выдавать некое уникальное LongInt значение для каждой строки (например, идентификатор записи в соответствующей таблице базы данных). Если Вы собираетесь использовать динамические массивы объектов и вы можете предоставить уникальный идентификатор для каждой записи, то посмотрите на классы TGridDataIdArray и TGridDataIdArrayItem, они специально для этого случая и Вам не потребуется самостоятельно переопределять упомянутые методы – это уже сделано.

 

Register-процедура находится в файле ArrayGrids.pas

 

Классы из модуля Arrays.pas используются в ArrayGrids, кстати, сам этот модуль тоже достаточно интересный.


Свойства и методы класса TArrayGrid:

 

property Options: TArrayGridOptions;

TArrayGridOption = (goMultiSelect, goFocusSelected, goAlwaysShowFocus, goRowFocus, goColSizing, goEditing, goAlwaysShowEditor, goAutoHideEditor, goVertThumbTracking, goHorzThumbTracking, goTabs, goSmoothScroll, goTrackMouse, goTrackEditorChanging, goSortOnTitleClick);

goMultiSelect - Разрешение множественной отметки строк. Пользователь может ставить/снимать отметку при помощи клавиатуры и мыши. Из приложения отметка устанавливается методом DataConnector.SetSelected, получить значение можно с помощью DataConnector.GetSelected, а если класс присоединенного объекта данных является наследником от класса TGridDataArray, то к отметке можно обращаться через свойство Selected. Цвет ячеек, принадлежащих отмеченным строкам, задается через SelColor, SelTextColor, SelFocusColor, SelFocusTextColor.

goFocusSelected - Если True, то цвета *Focus*Color относятся к ячейке, на которую указывает Row и Col, иначе они описывают цвета строки Row(есть у меня подозрение, что здесь что-то не так).

goAlwaysShowFocus - Если True, то текущая ячейка/строка всегда будет выделяться даже если грид не в фокусе.

goRowFocus - В фокусе вся строка, а не отдельная ячейка. Режим не рекомендуется, если отдельные колонки доступны для редактирования и Вы используете встроенные редакторы ячеек – считается, что в этом режиме должна редактироваться строка целиком.

goColSizing - Если True, то разрешено менять ширину колонок.

goEditing - Если True, разрешено редактирование ячеек в колонках, для которых property ReadOnly = False.

goAlwaysShowEditor - Если разрешено редактирование ячейки, находящейся в фокусе, то она будет в режиме            редактирования.

goAutoHideEditor – Если True, то при выходе из редактирования ячейки редактор будет отключаться. В противном случае, если редактирование новой текущей ячейки разрешено, то она сразу будет переведена в режим редактирования.

goVertThumbTracking, goHorzThumbTracking - Если тащить за бегунок полосы прокрутки, то синхронно будет изменяться положение ячейки в фокусе. При отключенных этих режимах оно изменяется только при отпускании бегунка.

goSmoothScroll - (Win2K only) гладкая прокрутка (я не уверен, что она работает так, как об этом пишет MS).

goTabs - Если True, то клавиша Tab будет изменять фокус.

goTrackMouse - Если в этом режиме нажать на левую кнопку мыши и двигать за пределы грида, то автоматически будет производится прокрутка. Если же при этом еще и нажата клавиша Shift, то попутно будет ставиться/сниматься отметка строк.

goTrackEditorChanging - В этом режиме при редактировании ячейки на каждое изменение будет вызываться метод DataConnector.SetAs...(т.е. сохранять), иначе это действие будет происходить только при выходе из редактирования.

goSortOnTitleClick - Пока этот режим имеет смысл, только если класс присоединенного объекта с данными является наследником от TGridDataArray. В этом случае при клике на заголовок колонки будет переключаться направление сортировки записей (три состояния: по возрастанию, по убыванию и в первоначальном порядке).

 

property DefaultColWidth: Integer;

Задает ширину колонки по умолчанию.

property TitleHeight : Integer;

Задает высоту заголовка колонки.

 

property RowHeight: Integer;

Задает высоту строк. Все строки данных имеют одинаковую высоту.

 

property LeftCol: LongInt;

Номер (от нуля) левой отображаемой колонки.

 

property MaxLeftCol: LongInt;

Максимальный номер левой отображаемой (хотя бы частично) колонки. Грид стремится не оставлять пустое место справа.

 

property RightCol: LongInt;

Номер правой отображаемой колонки.

 

property TopRow: LongInt;

Номер (от нуля) верхней строки.

property MaxTopRow: LongInt;

Максимальный номер верхней строки (от нуля). Грид стремится не оставлять пустое место снизу.

 

property BottomRow: LongInt;

Номер (от нуля) последней отображаемой (хотя бы частично) строки.

 

property Col: LongInt;

Номер колонки для ячейки в фокусе.

 

property Row: LongInt;

Номер текущей строки - номер строки, которой находится ячейка в фокусе.

 

property VisibleRowCount: Integer;

Количество отображаемых строк.

 

property GridWidth: LongInt;

Ширина области данных грида без учета пустого пространства справа.

 

property GridHeight: LongInt;

Высота области данных и заголовка.

 

property Offset: LongInt;

Смещение левого края левой видимой колонки от левого края клиентской части окна.

 

property Color: TColor default clWindow;

Цвет фона.

 

property SelColor: TColor;

Цвет фона ячейки из отмеченной строки.

 

property SelTextColor: TColor;

Цвет текса ячейки из отмеченной строки.

 

property SelFocusColor: TColor;

Цвет фона ячейки из отмеченной строки, если она в фокусе.

 

property SelFocusTextColor: TColor;

Цвет текста ячейки из отмеченной строки, если она в фокусе.

 

property FocusColor: TColor;

Цвет фона ячейки в фокусе.

 

property FocusTextColor: TColor;

Цвет текста ячейки в фокусе.

 

property ScrollBars: TArrayGridScrollBars default [sbHorz,sbVert];

TArrayGridScrollBars = set of (sbHorz,sbVert);

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

 

property DefaultDrawing: Boolean default True;

Если False, то закрашивание фона и отрисовка линий не будет производиться.

 

property EditorMode: Boolean;

Если True, то ячейка в фокусе сейчас редактируется.

 

property DataConnector: TAbstractDataConnector;

Объект для доступа к данным. Если данные в объекте класса-наследника от TGridDataArray, то проще воспользоваться свойством DataObject, в противном случае нужно написать наследника от TAbstractDataConnector, создать объект и присвоить его описываемому свойству грида.

 

property ColCount: LongInt;

Количество колонок. Всегда больше нуля.

 

property Columns: TArrayGridCols;

Массив описателей колонок.

 

property RowCount: LongInt;

Количество строк.

 

property DataObject: TObject;

Присоединенный объект данных. То же, что и DataConnector.DataObject.

Если присвоить объект класса-наследника TGridDataArray, то автоматически будет создан объект TGridDataArrayConnector и проставлены необходимые связи, что приведет к отрисовке данных в гриде.

 

property OnDrawTitle: TArrayGridDrawTitleEvent;

TArrayGridDrawTitleEvent = procedure(Sender: TObject; ARect: TRect) of object;

В обработчике этого события нужно полностью отрисовывать строку заголовков. Стандартная отрисовка может быть выполнена методом DrawTitle.

 

property OnDrawRow: TArrayGridDrawRowEvent;

TArrayGridDrawRowEvent = function(Sender: TObject; const ARow, ALeftCol: LongInt; ARect: TRect; State: TArrayGridDrawState): Boolean of object;

Обработчик этого события вызывается для отрисовки строки. Если он возвратит False, то должен полностью отрисовать указанную строку данных.

 

property OnDrawCell: TArrayGridDrawCellEvent;

TArrayGridDrawCellEvent = function(Sender: TObject; const ACol, ARow: LongInt; ARect: TRect; AState: TarrayGridDrawState): Boolean of object;

Обработчик этого события должен либо полностью отрисовать указанную ячейку, либо возвратить False.для стандартной отрисовки. Стандартная отрисовка может быть выполнена с помощью метода DrawCell (см.также DrawStringCell, DrawRightStrCell, DrawIntegerCell, DrawCurrencyCell ).

 

property OnGetCellColor: TArrayGridGetCellColorEvent;

TArrayGridGetCellColorEvent = procedure (Sender: TObject; сonst ACol, ARow: LongInt; var AColor, ATextColor: TColor; AState: TArrayGridDrawState) of object;

Обработчик этого события может изменить цвет фона и текста ячеек.

 

property OnFocusMoved: TArrayGridDataChangedEvent;

TArrayGridDataChangedEvent = procedure(Sender: TObject; const ACol, ARow: LongInt) of object;

Обработчик этого события вызывается при изменении индекса текущей строки или текущей ячейки. Параметры ACol и ARow задают координаты фокуса до перемещения.

 

property OnDataChanged: TArrayGridDataChangedEvent;

TArrayGridDataChangedEvent = procedure(Sender: TObject; const ACol, ARow: LongInt) of object;

Вызывается при изменении данных. Параметры ACol и/или ARow могут равняться -1, что означает, что изменены данные всех ячеек строки и/или колонки соответственно. Этот метод также будет вызван при изменении отметки, если OnSelectionChanged или OnRowsChanged не заданы.

 

property OnRowsChanged: TArrayGridRowsChangedEvent;

TArrayGridRowsChangedEvent = procedure(Sender: TObject; const AFrom, ATo: LongInt) of object;

Изменилось состояние или данные строки, либо вызван метод DataConnector.RowsChanged. Если изменилась только состояние отметки и задан OnSelectionChanged, то описываемый метод вызван не будет. Если этот обработчик не задан, то будет вызван OnDataChanged с параметром ACol=-1.

 

property OnRowsDeleted: TArrayGridRowsChangedEvent;

TArrayGridRowsChangedEvent = procedure(Sender: TObject; const AFrom, ATo: LongInt) of object;

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

 

property OnSelectionChanged: TArrayGridRowsChangedEvent;

TArrayGridRowsChangedEvent = procedure(Sender: TObject; const AFrom, ATo: LongInt) of object;

Обработчик вызывается при смене отметки. Если этот обработчик не задан, то будет вызван обработчик OnRowsChanged, если и он не задан, то OnDataChanged с параметром ACol=-1.

 

property OnTitleClick : TArrayGridTitleClickEvent;

TArrayGridTitleClickEvent = procedure(Sender: TObject; const ACol : LongInt) of object;

Обработчик вызывается при левом щелчке на заголовке колонки.

 

property OnShowHint: TArrayGridShowHintEvent;

TArrayGridShowHintEvent = procedure (AArray: TCustomArrayGrid; AHintInfo: PHintInfo; AColumn: TArrayGridCol; const ACol, ARow: Integer ) of object;

PHintInfo = ^THintInfo;

THintInfo = record

HintControl: TControl;

HintWindowClass: THintWindowClass;

HintPos: TPoint;

HintMaxWidth: Integer;

HintColor: TColor;

CursorRect: TRect;

CursorPos: TPoint;

ReshowTimeout: Integer;

HideTimeout: Integer;

HintStr: string;

HintData: Pointer;

end;

Обработчик этого события вызывается перед выводом окна подсказки (Hint) в случае, если  указатель мыши находится на областью данных или над заголовком колонки. Если ARow=iTitle, то это значит, что указатель мыши находится над заголовком колонки ACol. Вы можете изменить текст подсказки, для этого впишите желаемое значение в поле AHintInfo^.HintStr, а для подавления присвойте этому полю пустую строку.

 

function AddColumn(const ACaption: String; const AWidth: Integer): ArrayGridCol; virtual;

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

 

function InsertColumn(const APosition: Integer; const ACaption: String; const AWidth: Integer): ArrayGridCol; virtual;

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

 

procedure DeleteColumn(const ACol: LongInt); virtual;

Удаляет колонку. Последняя колонка не может быть удалена.

 

procedure InvalidateCell(const ACol, ARow: LongInt);

Указывает гриду, что указанная ячейка должна быть перерисована. Параметр ARow может равняться константе iTitle, тогда будет перерисован заголовок соответствующей колонки.

 

procedure InvalidateCol(const ACol: LongInt);

Указывает гриду, что указанная колонка должна быть перерисована.

 

procedure InvalidateRow(const ARow: LongInt);

Указывает гриду, что указанная строка должна быть перерисована. Параметр ARow может равняться константе iTitle, тогда будет перерисована строка заголовков.

 

procedure InvalidateRows(const AFrom, ATo: LongInt);

Указывает гриду, что указанный диапазон строк должен быть перерисован. Параметры также могут равнятся iTitle (заголовок) и iBlank (пустая область).

 

procedure InvalidateRect(ARect : TArrayGridRect);

Указывает гриду, что указанный прямоугольник ячеек должен быть перерисован.

 

procedure DrawTitle; virtual;

Стандартная отрисовка заголовка.

 

procedure DrawColumnTitle(const ACol: Integer; const APushed: Boolean); virtual;

Стандартная отрисовка заголовка указанной колонки.

 

procedure DrawCell(const ACol, ARow: LongInt; ARect: TRect; AState: TArrayGridDrawState);

Стандартная отрисовка ячейки.

 

procedure DrawStringCell(const AValue: String; AColumn: TArrayGridCol; const ARow: LongInt; ARect: TRect); virtual;

Рисует строку в указанной области.

 

procedure DrawRightStrCell(const AValue: String; AColumn: TArrayGridCol; const ARow: LongInt; ARect: TRect); virtual;

Рисует в указанной области строку, выровненную вправо.

 

procedure DrawIntegerCell(const AValue: LongInt; AColumn: TArrayGridCol; const ARow: LongInt; ARect: TRect); virtual;

Рисует целое число в указанной области.

 

procedure DrawCurrencyCell(const AValue: Currency; AColumn: TArrayGridCol; const ARow: LongInt; ARect: TRect); virtual;

Рисует денежное значение в указанной области.

 

procedure DrawDateTimeCell(const AValue: TDateTime; AColumn: TArrayGridCol; const ARow: LongInt; ARect: TRect); virtual;

Рисует дату и/или время в указанной области.

 

function BoxRect(ALeft, ATop, ARight, ABottom: LongInt): TRect;

Параметры - номера колонок и строк, задающие область, координаты которой нужно получить.

 

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

Результат - область, занимаемая указанной ячейкой.

 

procedure DeleteRow(const ARow: LongInt);

Удалить указанную строку.

 

procedure DeleteRows(const AFrom, ATo: Integer);

Удалить указанные строки.

 

procedure DeleteSelected;

Удалить отмеченные строки.

 

procedure DeleteAll;

Удалить все строки.

 

procedure ToggleSelection(const ARow: LongInt);

Инвертировать отметку указанной строки.

 

procedure ChangeSelection(AFrom, ATo: LongInt);

Изменить отметку указанной области.

 

procedure InvertSelection;

Инвертировать все отметки.

 

procedure SelectAll; virtual;

Отметить все строки.

 

procedure UnselectAll; virtual;

Снять все отметки.

 

procedure CopySelectedToClipboard(const AIndexes: Array of Integer);

Скопировать отмеченное в буфер обмена (clipboard). Параметр - массив номеров колонок, если же он задан как [], то будут скопированы все колонки. Ячейки разделяются символами табуляции, строки - CRLF. Под NT в буфер будет записана строка в UNICODE, под Win9x записывается номер кодовой страницы, поэтому скопированное прекрасно вставляется в MS Excel 97 и MS Word 97.

 

function LockLayout : LongInt;

Блокирует обработку и отрисовку изменений, связанных с изменением количества строк, количества колонок, ширины колонок и т.п.. Результат – уровень вложенности блокировки.

 

procedure UnlockLayout;

Уменьшает счетчик уровня блокировок и, если он обнулился, выполняет пересчет параметров отрисовки, полос прокрутки и т.п..

 

procedure UpdateLayout;

Если не заблокировано, то выполняется пересчет параметров отрисовки и полос прокрутки.

 

function LocateString(const AKey: String; const ACol, AFrom: Integer; AOptions: TStringArrayLocateOptions): Integer;

TStringArrayLocateOption = (saloCaseInsensitive, saloPartialKey, saloBackward);

Поиск подходящего значения в колонке строкового типа (ackString или ackRight). Параметр AFrom задает индекс строки, начиная с которой будет производиться поиск, если AFrom=-1, то поиск стартует с начала (конца в случае saloBackward).