Вы находитесь на странице: 1из 12

http://www.delphi-manual.ru/stringgrid.

php

 Компонент StringGrid находится на


странице Additional палитры
компонентов. Там находятся
"дополнительные" компоненты, но
StringGrid Delphi, на мой взгляд,
достоин большего уважения, лично я
разместил бы его на странице Standart!

   StringGrid - компонент для


отображения различных данных в
табличной форме. Как следует из
названия, ячейки компонента StringGrid
Delphi могут содержать данные,
имеющие тип String, а также отображать
графику.

   Таблица StringGrid состоит из выделенных серым FixedCols и FixedRows -


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

   Итак, компонент StringGrid имеет возможность адресации каждой отдельной ячейки по


номеру столбца и строки. Содержимое ячейки (i, j), где где i - номер столбца, j - номер
строки, имеет вид

StringGrid1.Cells[i, j]

и доступно как для чтения, так и для записи. Здесь, как и всегда, номера столбцов ( i ) и
строк ( j ) отсчитываются от 0.

   Выделенная ячейка таблицы имеет

номер столбца:    StringGrid1.Col


номер строки:    StringGrid1.Row

поэтому содержимое выделенной ячейки будет адресоваться так:

   S:=StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row];

Не правда ли, написание такой строки - утомительный процесс. Поэтому пользуйтесь


оператором присоединения with:

  with StringGrid1 do
    S:=Cells[Col, Row];

А лучше сразу задать в свойстве Name имя покороче, например SG.

   За многие свойства компонента Delphi StringGrid отвечает свойство Options. В


Инспекторе Объектов Options - это раскрывающийся список, представляющий собой
элементы данного множества. Если значение элемента равно True, то он присутствует в
множестве, если False - то нет.

Свойство Значение
Наличие
вертикальных
разделительных
goFixedVertLine
линий между
"фиксированными"
ячейками
Наличие
горизонтальных
разделительных
goFixedHorzLine
линий между
"фиксированными"
ячейками
Наличие
вертикальных
goVertLine разделительных
линий между
"обычными" ячейками
Наличие
горизонтальных
goHorzLine разделительных
линий между
"обычными" ячейками
Возможность
goRangeSelect выделить диапазон
ячеек
Закрашивание ячейки
goDrawFocusSelected 
с фокусом ввода
Возможность менять
goRowSizing
высоту строк мышкой
Возможность менять
goColSizing ширину столбцов
мышкой
Возможность менять
номер строки, то есть
goRowMoving
перемещать её,
мышкой
Возможность менять
номер столбца, то
goColMoving
есть перемещать его,
мышкой
goEditing Возможность
редактировать
содержимое ячейки с
клавиатуры
При значении True
фокус смещается на
следующую ячейку в
goTabs
таблице, False - на
следующий
компонент
Выделяется вся
строка с
goRowSelect
"фокусированной"
ячейкой
При значении True
содержимое ячейки
при получении
фокуса сразу
доступно
редактированию,
goAlwaysShowEditor False - сначала
необходимо щёлкнуть
по ней мышкой, либо
нажать Enter или F2
(прим.: не действует
при
goRowSelect=True)
При значении True
перемещение
"бегунка" прокрутки
мышкой вызывает
немедленное
goThumbTracking
перемещение ячеек,
False - ячейки
перемещаются только
при отпускании
"бегунка"

   Как следует из таблицы, за возможность редактировать содержимое ячеек с клавиатуры


отвечает элемент goEditing свойства-множества Options. В Инспекторе Объектов
установите его значение в True. Чтобы управлять этой возможностью программно, нужно
включить или исключить из множества данный элемент:

StringGrid1.Options:=StringGrid1.Options+[goEditing];  //Включаем редактирование, другие


элементы не трогаем

StringGrid1.Options:=StringGrid1.Options-[goEditing];  //Выключаем редактирование,
другие элементы не трогаем

StringGrid1.Options:=[goEditing, goFixedVertLine, goFixedHorzLine, goVertLine,


goHorzLine, goRowSelect];  //Задаём список необходимых элементов
   Если элементы заданы списком, это аналогично присвоению в Инспекторе Объектов
этим элементам значения True, остальным - False.

   Ячеек в таблице, как правило, много, и в рамках компонента видна только часть из них.
В программе доступна информация как об общем количестве строк и столбцов, так и
номерах и количестве строк и столбцов, видимых в рамках таблицы.

   Количество строк в Delphi StringGrid равно StringGrid1.RowCount.


   Количество столбцов в Delphi StringGrid равно StringGrid1.ColCount.

   Если ячейки не помещаются в таблице, появляются полосы прокрутки. При прокручивании

StringGrid1.LeftCol Номер столбца, видимого самым левым


StringGrid1.TopRow Номер строки, видимой самой верхней
StringGrid1.VisibleColCount
Количество столбцов, видимых в
рамках таблицы
StringGrid1.VisibleRowCount
Количество строк, видимых в рамках
таблицы

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

DefaultRowHeight - высота строк по умолчанию


DefaultColWidth - ширина столбцов по умолчанию

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

RowHeights - массив, содержащий высоты строк. То есть, например, RowHeights[5] -


высота строки с индексом 5
ColWidths - массив, содержащий ширины столбцов. То есть, например, ColWidths[5] -
ширина строки с номером 5

   Все эти свойства настраиваем в обработчике события OnCreate Формы, так же как и
надписи заголовков, располагающиеся в строках и столбцах "фиксированной" зоны
таблицы. В результате таблица появляется уже в "настроенном" виде!

   Поскольку ячейки компонента StringGrid можно редактировать, точно так же как и


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

type TGridCracker = class(TStringGrid);

procedure SetCaretPosition(Grid: TStringGrid; col, row, x_pos: Integer);


begin
  Grid.Col := Col;
  Grid.Row := Row;
  with TGridCracker(Grid) do
  InplaceEditor.SelStart := x_pos;
end;

   Теперь можно установить желаемую позицию курсора в ячейке, например, по нажатию


кнопки:

procedure TForm1.Button1Click(Sender: TObject);


begin
StringGrid1.SetFocus;
with StringGrid1 do
SetCaretPosition(StringGrid1, Col, Row, 2);
end;

   Правда, ещё один момент! Чтобы код сработал, нужно установить в Инспекторе
Объектов значение параметра goAlwaysShoweEditor свойства Options в True. Можно это
сделать также и программно, в той же процедуре нажатия кнопки:

StringGrid.Options:=StringGrid.Options+[goAlwaysShoweEditor];

   Отдельно требуется осветить вопрос очистки содержимого таблицы StringGrid. Так как
таблица StringGrid, в отличие от, например, компонента Memo, не имеет метода для
очистки содержимого сразу всех ячеек, то для удаления внесённых в таблицу ранее
данных приходится очищать каждую ячейку отдельно. Делается это двумя вложенными
циклами for, пробегающими по столбцам и строкам:

var i, j: Integer;
begin
  with StringGRid1 do
    for i:=1 to RowCount-1 do   //Заголовки строк не трогаем
    for j:=1 to ColCount-1 do   //Заголовки столбцов не трогаем
      Cells[j, i]:='';
end;

   Хотя, оказывается, есть метод для очищения содержимого целого столбца или строки:

  StringGrid1.Cols[i].Clear; //Очищается столбец с номером i


  StringGrid1.Rows[i].Clear; //Очищается строка с номером i

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

var i, j: Integer;
begin
  with StringGRid1 do
    for i:=1 to RowCount-1 do  //Заголовки столбцов не трогаем - цикл от 1
      begin
        Rows[i].Clear;
        Cells[0, i]:="Заголовок строки i";
      end;
end;

   Казалось бы, можно поступить и по-другому, просто обнулить количество строк или
столбцов! Однако так делать неправильно, так как при их последующем добавлении
может оказаться, что каждая ячейка содержит прежние данные. А в Delphi4 даже при
уменьшении количества строк или столбцов содержавшиеся в них данные вообще не
пропадали, а так и повисали в воздухе! Так что так можно поступать только если в
добавляемых ячейках сразу будет новое непустое содержимое.

Графические возможности компонента StringGrid

   Таблица StringGrid Delphi обладает также и графическими возможностями. Ячейки


таблицы можно раскрашивать в произвольные цвета, выводить рисунки, размещать не
только однострочный, но и многострочный текст. Графические возможности StringGrid
определяются наличием у таблицы свойства Canvas - холста, на котором можно
воспроизводить любую графику стандартными методами Delphi. Кроме того, компонент
StringGrid имеет дополнительные методы, помогающие выводу графики в ячейки
компонента.

Компонент StringGrid умеет не хранить в своих ячейках не только текстовую


информацию, но и графику. Графические возможности StringGrid определяются
наличием у таблицы свойства Canvas - холста, на котором можно воспроизводить любую
графику стандартными методами Delphi. Кроме того, компонент StringGrid имеет
дополнительные методы, помогающие выводу графики в ячейки компонента.
Графическими методами в таблице StringGrid можно, например, выводить текст в ячейке
не только в одну, но и в несколько строк, произвольно раскрашивать ячейки, размещать
рисунки и т.д.

   Работа с графическими свойствами компонента StringGrid происходит в обработчике


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

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;


  Rect: TRect; State: TGridDrawState);
begin
  //ACol - индекс столбца
  //ARow - индекс строки
  //Rect - прямоугольник вывода, заданный ячейкой (ACol, ARow)
end;

   Для начала давайте выведем в ячейку (1, 1) компонента StringbGrid какой-нибудь


рисунок. Вывести рисунок в ячейку компонента StringGrid проще всего, предварительно
загрузив его в компонент Image:

Image1.Picture.LoadFromFile('Имя_файла');

   Загрузить рисунок в компонент Image можно, конечно, уже на этапе проектирования, в


Инспекторе Объектов, вызвав графический редактор нажатием кнопочки в свойстве
Picture.

   Затем нужно определить размеры загруженного рисунка:

W:=Image1.Picture.Width;
H:=Image1.Picture.Height;

   Далее, готовим ячейку под размещение рисунка. Для этого нужно задать её размеры
кратными размерам рисунка. Например, сделаем размеры ячейки в 10 раз меньше
размеров рисунка:

StringGrid1.ColWidths[1]:=Round(W/10);
StringGrid1.RowHeight[1]:=Round(H/10);

   Все эти манипуляции делаем предварительно, в обработчике OnCreate Формы,


например. Ну и, наконец, в обработчике OnDrawCell выводим рисунок:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;


  Rect: TRect; State: TGridDrawState);
begin
if ACol*ARow=1 then //Условие ACol*ARow=1 тождественно (ACol=1)and(ARow=1)
  StringGrid1.Canvas.StretchDraw(Rect, Image1.Picture.Graphic);
end;

   Таким образом, при перерисовке таблица просматривает все ячейки и, встретив


комбинацию ACol=1 и ARow=1, выводит в эту ячейку рисунок.

   Таким же способом можно и раскрасить заданные по любому условию ячейки в нужные


цвета. Например, раскрасим ячейки с положительными числами в зелёный цвет, с
отрицательными - в красный, с равными нулю в синий:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;


  Rect: TRect; State: TGridDrawState);
var X: Real;
begin
with StringGrid1 do
  begin
   try
    X:=StrToFloat(Cells[ACol, ARow]);
    if X>0 then Canvas.Brush.Color:=clGreen;
    if X<0 then Canvas.Brush.Color:=clRed;
    if X=0 then Canvas.Brush.Color:=clBlue;
   except
   end;
   Canvas.FillRect(Rect); //Текст тоже будет закрашен, его нужно
перерисовать:
   Canvas.TextOut(Rect.Left+2, Rect.Top+2, Cells[ACol, ARow]);
  end;
end;

   Осталось рассмотреть возможности компонента StringGrid по выводу текста в ячейку


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

//В обработчике OnCreate Формы подготавливаем высоту строки:


StringGrid1.RowHeight[0]:=(StringGrid1.Canvas.TexHeight('A')+2)*N; //N -
количество строк
//теперь в обработчике OnDrawCell выводим текст:
if (ACol=1) and (ARow=0) then
  begin
    Canvas.TextOut(Rect.Left+2, Rect.Top+2, 'Многострочный заголовок');
    Canvas.TextOut(Rect.Left+2, Canvas.TextHeight('A')+Rect.Top+2, 'Вторая
строка');
    Canvas.TextOut(Rect.Left+2, Canvas.TextHeight('A')*2+Rect.Top+2, 'Третья
строка');
  end;
   Разумеется, вывод лучше делать в цикле, заголовки столбцов записать в массив, ну и
добавить возможность центрирования заголовка в строке.

Совместная работа StringGrid и Excel

   Очень многие документы создаются и хранятся в формате электронных таблиц Microsoft


Excel. Несмотря на то, что эти таблицы обладают возможностями для автоматической
обработки документа, нам, дельфистам, гораздо приятнее работать в привычной среде,
что которая и обладает к тому же гораздо более развитыми возможностями. Давайте
посмотрим, как получать данные из Excel. Естественно, табличные данные будем
размещать в привычную нам таблицу StringGrid. А уж в ней мы горы своротим! читать
далее...

Очень многие документы создаются и хранятся в формате электронных таблиц Microsoft


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

   Для работы с Excel и другими программами из пакета Microsoft Office необходимо


добавить в список uses модуль ComObj:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ComObj;

   Далее, описываем глобальную переменную типа Variant:

var
  Form1: TForm1;
  Excel: Variant;

   Далее, нужно создать объект Excel. Excell Application создаётся пустым, без таблиц,
поэтому необходимо добавить хотя бы одну книгу. Делать это нужно в каком-либо
обработчике, например обработчике нажатия кнопки, хотя можно и сразу в OnCreate
Формы:

Excel:=CreateOleObject('Excel.Application');
Excel.Application.WorkBooks.Add('Имя_Файла.xls');

Если создаётся пустая книга, метод Add применяется без параметра - без имени файла.
Естественно, можно предложить пользователю выбрать файл:

with OpenDialog1 do
  if Execute then
    Excel.Application.WorkBooks.Add(FileName);

   Для отладки необходимо, чтобы таблица Excel была видимой, а также лучше запретить
задавать вопросы о сохранении данных при закрытии:

Excel.Visible:=True; //После отладки можно закомментировать эту строку


Excel.DisplayAlerts:=False;

   Сразу создайте метод закрытия объекта Excel, иначе при отладке, да и при работе
пользователя в компьютере наплодится столько невидимых процессов Excel, что мама
дорогая!.. В обработчике OnCloseQuery Формы напишите:

try
  Excel.Quit;
except
end;
CanClose:=True;

   Естественно, будет произведён выход из Excel, и затем закроется всё приложение. Но


если нам нужно после закрытия процесса Excel продолжить работу с программой, то этот
код помещается в обработчик нажатия кнопки. Однако, в данном случае его недостаточно.
Попробуйте, и вы убедитесь, взглянув в список процессов в Диспетчере Задач, что наш
процесс Excel жив и здоров! Это произошло потому, что он остаётся связанным с
переменной, его создавшей (Excel же). Для реального уничтожения процесса нужно
разорвать эту связь. Дополните вышеприведённый код строкой:

  Excel:=Unassigned;

и при нажатии кнопки закрытия наш Excel исчезнет из списка процессов.

   Теперь нужно получить данные из Excel. В Excel столбцы именуются буквами, но мы в


Delphi обращаемся к ним привычно, по порядковым номерам. Обратите внимание, что,
поскольку в Delphi первым в индексе идёт индекс столбца, а в таблице Excel индекс
строки, то индексы должны быть расположены на противоположных местах. В
обработчике нажатия кнопки:

with StringGrid1 do
  for i:=1 to RowCount-1 do
  for j:=1 to ColCount-1 do
    Cells[j, i]:=Excel.WorkSheets.Item['Лист1'].Cells[i, j];

   Маленькое предупреждение: если при отладке проверять внесение данных, то перед


нажатием нашей кнопки нужно завершить ввод в Excel - нажать Enter. Ведь если ячейка
таблицы Excel останется в режиме редактирования, то мы получим отказ от Excel.
   И ещё. Данные в Excel адресуются начиная с 1. Попытка получить содержимое
фиксированных ячеек не удаётся. Поэтому фиксированные ячейки в таблице StringGrid
при необходимости нужно заполнять самому, отдельно.

   А получить содержимое одной ячейки можно как указав номер строки и столбца, так и
непосредственно указав адрес ячейки:

var S1, S2: String;


begin
  S1:=Excel.WorkSheets.Item['Лист1'].Cells[5, 6];
  S2:=Excel.WorkSheets.Item['Лист1'].Range['F5'];
end;

   В переменных S1 и S2 будет одинаковое значение.


   Теперь в таблице StringGrid мы имеем данные для обработки, и делаем с ними что
хотим. Затем можно перенести обработанные данные назад в таблицу Excel. Делается это
совершенно аналогично, в обработчике нажатия другой кнопки:

for i:=1 to Grid.RowCount-1 do


for j:=1 to Grid.ColCount-1 do
  Excel.WorkSheets.Item['Лист1'].Cells[i, j]:=Grid.Cells[j, i];

   Если эти операции производятся с активным листом Excel, то можно сократить


написание, и вместо:

Excel.WorkSheets.Item['Лист1'].Cells[i, j]

   писать:

Excel.Cells[i, j]

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

var Sheet: Variant;


   S1, S2: String;
begin
  Sheet:=Excel.WorkSheets.Item['Лист1'];
  S1:=Sheet.Cells[5, 6];
  S2:=Sheet.Range['F5'];
end;

   Только имейте в виду, что таблица может содержать не только данные непосредственно
в ячейках, но и формулы. При записи данных из нашей таблицы StringGrid всё, кроме
непосредственно записываемого текста, будет уничтожено!

   Напоследок нужно заставить таблицу Excel сохранить обработанные данные:

Excel.ActiveWorkbook.SaveAs('Имя_Файла');//Или
SaveAs('OpenDialog1.FileName');

   Можно вывести отчёт на печеть. Вот как задана функция печати:

function PrintOut(
From: Variant;//Необязательно. Номер срааницы с которой начинается печать.
To: Variant;//Необязательно. Номер страницы по какую продолжается печать.
Copies: Variant;//Необязательно. Количество копий.
Preview: Variant;//Необязательно. Предварительный просмотр (True или False).
ActivePrinter: Variant;//Необязательно. Имя активного принтера.
PrintToFile: Variant;//Необязательно. При значении True печать будет идти в
файл.
Collate: Variant//Необязательно. При значении True копии страниц
объединяются.
                ): Workbook;

   Воспользоваться этой функцией можно как методом переменной, указывающей


страницу - Sheet (также Excel.ActiveWorkBook или Excel.WorkSheets):

  Sheet.PrintOut(1, 1, 1, False, True);

   Будет произведён вывод на печать с первой страницы по первую, одной копии, без
предварительного просмотра, без указания принтера - печать идёт в файл. Предварительно
будет выдан запрос на указание имени файла. Создаётся файл типа *.xps. Для его
просмотра нужны специальные программы.

   Естественно, в Delphi можно осуществлять также и форматирование ячеек, и другие


операции с таблицей Excel. Эту информацию добавлю чуть позже. А пока на первый раз
достаточно.

Работа с регионом ячеек Excel


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

   Регион ячеек таблицы Excel также имеет тип Variant и задаётся прямоугольником, с указанием
левой верхней и правой нижней ячеек:

var Range: Variant;


begin
  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]];
end;

   В частности, регион может состоять и из одной ячейки:

  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[1, 1]];

   Эту запись проще выполнить с указанием адреса как в таблице Excel:

Range:=Excel.Range['A1'];

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

Range:=Excel.Range['A1:D4'];

   А вот как выполнить перепись региона 100Х100 ячеек Excel в таблицу StringGrid:

var Range: Variant;


     i, j: Integer;
begin
  Range:=Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]];
  with StringGrid1 do
    for i:=1 to 100 do
    for j:=1 to 100 do
      Cells[i, j]:=Range.Cells[j, i];
end;

   Вот и всё! На моём компьютере, эта операция переписи региона 100х100 ячеек Excel в таблицу
StringGrid длится около 300 мсек, что на 2 порядка быстрее, чем чтение и запись по одной ячейке.

   А, например, операция занесения какого-либо одного значения во все ячейки региона
выполняется ещё проще. Занесём в наш вышеопределённый регион 100х100 слово 'Привет':

  Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]]:='Привет';


Очистка региона выполняется методом Clear:

  Excel.Range[Excel.Cells[1, 1], Excel.Cells[100, 100]].Clear;