Опубликован: 25.06.2014 | Уровень: для всех | Доступ: платный | ВУЗ: Учебный центр "ANIT Texno Inform"
Лекция 22:

Записи и вариант. Сетка строк TStringGrid

Вариант

В Lazarus существует еще один интересный тип данных - variant. Вариантный тип был разработан в Object Pascal для поддержки работы с механизмом OLE (англ. Object Link and Embedding - внедрение и связь объектов). Даже если вы впервые слышите про такой механизм, вы с ним наверняка знакомы, и не раз им пользовались. Каждый раз, когда вы копируете таблицу MS Excel в MS Word, или картинку из Paint в тот же MS Word, или текстовое поле из Word в Excel; другими словами, любой объект, созданный в одной программе, копируете в другую программу - вы используете OLE.

В контексте Lazarus variant может содержать любое число - целое, или вещественное, строку, логическое значение, время-дату, массив, тот же OLE-объект. При этом компилятор сам определяет реальный тип варианта на этапе выполнения программы. Причем сразу же выделяет под переменную этого типа память - если значение не указано, этой переменной присваивается ноль.

Давайте рассмотрим следующий пример:

var
   MyV: variant;
begin
   MyV:=10;   //вариант содержит целое число
   MyV:= 2.5;   //вариант содержит уже вещественное число
   MyV:= 'Какой то текст';   //вариант содержит строку
   MyV:= True;   //а теперь вариант содержит логическое значение
    

Как видно из примера, мы присваиваем одной и той же вариантной переменной значения разных типов. При этом компилятор сам определяет наиболее подходящий по значению тип, и переделывает вариантную переменную под него. Например, в строке

   MyV:=10;
    

компилятор найдет наиболее подходящий под значение тип - Byte, и изменит переменную MyV под него. Таким образом, программист может не беспокоится о типах - за него эту работу выполнит компилятор. В вышеописанном примере переменная MyV поменяла свой тип 4 раза!

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

С переменными типа variant можно производить различные действия, например, складывать. При этом различные типы данных будут преобразовываться под нужный тип, если это возможно. Давайте попробуем работу с переменной типа variant на практике. Откройте Lazarus с новым проектом. Не меняя имен по умолчанию, сразу сохраните проект в папку 24-02. На форму установите только одну кнопку и сгенерируйте для нее событие OnClick. Его код следующий:

procedure TForm1.Button1Click(Sender: TObject);
var
  v1, v2, v3: variant;
begin
  v1:= '5';  //присвоили символ
  v2:= '10';  //теперь строку
  v3:= 20;  //теперь целое число
  v1:= v1 + v2 + v3;  //теперь все сложили

  ShowMessage(IntToStr(v1));
end;
    

Не торопитесь запускать эту программу, сначала попробуйте ответить: какое число в итоге выведет сообщение ShowMessage? На первый взгляд, результат будет 35 (5 + 10 + 20). Однако не спешите с выводами, подумайте. Мы к символу '5' прибавили строку '10'. Поскольку все однотипные операции производятся слева-направо, то в результате мы получим строку '510'. А затем мы к этой строке прибавляем целое число 20. Вариантная переменная, которая сначала имела символьный тип, затем строковый, получила целочисленный тип! А если к числу 510 прибавить 20, то сколько получится? Конечно, 530! Сохраните проект, запустите его на выполнение и убедитесь в этом сами.

Сетка строк TStringGrid

Напоследок познакомимся с еще одним полезным инструментом - сеткой строк TStringGrid. Это сетка, похожая на сетку в электронной таблице MS Excel, она позволяет вводить и редактировать табличную информацию в виде строк и колонок.

Загрузите Lazarus с новым проектом. Форму переименуйте в fMain, проект сохраните в папку 24-03 под именем Setka, а модулю формы дайте имя Main. Чтобы ваша форма соответствовала моей, установите ширину width = 625, а высоту height = 195. В Caption напишите "Редактирование сетки TStringGrid".

Теперь перейдите на вкладку Additional, найдите и установите на форму компонент TStringGrid. Рядом с ним вы можете увидеть еще одну сетку, TDrawGrid. Разница между ними в том, что сетка TStringGrid самостоятельно прорисовывает данные в ячейках, а в TDrawGrid это придется делать вручную. Так как TStringGrid удобней, её и будем изучать. У сетки установите ширину 615, а высоту 120 пикселей.

Ниже установите одну простую кнопку TButton, в Caption которой напишите "Добавить". В результате, у вас должна получиться вот такая форма:

Форма проекта

увеличить изображение
Рис. 24.3. Форма проекта

Прежде, чем приступить к кодированию, выделите на форме сетку и посмотрите в Инспекторе объектов на её свойства. Познакомимся с наиболее значимыми из них.

BorderStyle - стиль обрамления. Может иметь только два значения - с обрамлением и без него.
ColCount - количество колонок в сетке. По умолчанию их 5. При необходимости, это значение можно изменять программно, увеличивая или уменьшая количество колонок.
DefaultColWidth - ширина колонок по умолчанию. К сожалению, всем колонкам устанавливается одинаковая ширина, хотя, как правило, разные колонки должны иметь разную ширину. Во время работы программы ширину каждой колонки можно будет изменить программно.
DefaultDrawing - прорисовка данных по умолчанию. Если стоит True, то компонент сам будет отображать введенные данные, иначе это придется делать программисту.
DefaultRowHeight - высота строк по умолчанию. Установлено 20 пикселей, но этот размер немного великоват, рекомендую изменить его на 18. Хотя это на любителя, и зависит от размера шрифта.
FixedCols - количество фиксированных колонок. Они выделяются серым цветом, и всегда первые. Это свойство можно назвать заголовком строк. Практически не бывает необходимости делать более одной такой колонки, а иногда фиксированные колонки и вовсе не нужны. По умолчанию установлена одна колонка.
FixedRows - количество фиксированных строк. По умолчанию тоже одна, и работает так же, как и FixedCols. Как правило, эта строка служит заголовком колонок.
GridLineWidth - толщина разделительных линий. Попробуйте поставить ноль - линии исчезнут. Верните единицу.
Options - самое главное свойство компонента. Оно содержит много настроек, которые раскроются, если щелкнуть по плюсу слева от названия свойства. Эти настройки являются выключателями (True-разрешено, False-запрещено). Все настройки мы рассматривать не будем, их много, и большинство из них обычно не используется, кроме того, в справочнике Lazarus нет описаний этих настроек. Рассмотрим основные:
goAlwaysShowEditor - если True, то редактировать ячейку можно сразу при выделении. Если False, то для редактирования нужно нажать <Enter> или <F2>.
goColMoving - можно ли мышью перемещать колонки на другое место.
goColSizing - разрешено изменять ширину колонки мышью.
goFixedVertLine - рисовать ли вертикальные линии у фиксированных ячеек? По умолчанию True.
goDrawFocusSelect - разрешено выделять ячейку, которая находится в фокусе ввода.
goEditing - можно ли редактировать сетку? То есть, вводить данные с клавиатуры. По умолчанию установлено False, то есть, редактировать нельзя. Игнорируется, если включен элемент goRowSelect. Если вы хотите, чтобы пользователь мог вводить в сетку данные, а обычно это нужно, этот параметр следует установить в True.
goFixedVertLine - прорисовка вертикальных линий у фиксированных ячеек.
goHorzLine - прорисовка горизонтальных линий у нефиксированных ячеек.
goRangeSelect - разрешение выделять несколько ячеек. Не работает, если включен элемент goEdit.
goRowMoving - можно ли мышью перемещать строки на другое место.
goRowSelect - выделяется вся строка. Если равно False, то только одна ячейка.
goRowSizing - разрешено изменять высоту строки мышью.
goTabs - можно ли переключаться на другие ячейки с помощью клавиши <Tab>.
goThumbTracking - разрешена ли прорисовка данных в ячейках при прокрутке. Если нет, то данные будут обновлены после прокрутки.
goVertLine - прорисовка вертикальных линий у всех остальных (нефиксированных) ячеек.
RowCount - количество строк в сетке. По умолчанию их 5, как и колонок.
Row - закрытое свойство, содержит индекс текущей (выделенной) строки. Строка может быть выделена целиком, или в ней может быть выделена только одна ячейка. Поскольку свойство закрыто, то в Инспекторе объектов вы его не найдете, однако при кодировании, программно, это свойство доступно. Вы можете узнать индекс текущей строки, но не сможете его изменить с помощью свойства Row.

Также сетка TStringGrid имеет пару полезных методов, с которыми тоже стоит познакомиться.

DeleteRow - метод удаляет строку, индекс которой указывается в параметре. Например:

StringGrid1.DeleteRow(StringGrid1.Row);  //удалить текущую строку
StringGrid1.DeleteRow(0);  //удалить первую
StringGrid1.DeleteRow(StringGrid1.RowCount-1);  //удалить последнюю
    

SortColRow - метод выполняет сортировку данных в сетке как по строкам, так и по колонкам. Имеет два параметра:

SortColRow(IsColumn: Boolean;  index: Integer);
    

Если IsColumn имеет значение True, то сортировка производится по колонке с индексом index. Если False, то по строке с индексом index. Как правило, данные требуется сортировать именно по колонкам, например, по колонке с фамилиями.

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

s:=  StringGrid1.Cells[1, 1];  //считали  значение
StringGrid1.Cells[1, 1]:= 'Новое значение';  //записали значение
    

Как видно из примера, ячейки хранятся в списке ячеек Cells, при обращении к ячейке первым индексом является номер колонки, вторым - строки. Индексация начинается с нуля, поэтому самая верхняя левая ячейка будет иметь индекс [0, 0]. Если в сетке есть фиксированные строки и(или) колонки, именно они будут иметь нулевой индекс. То есть, если нужно указать в фиксированной строке заголовок третьей колонки, делать это надо так:

  StringGrid1.Cells[2, 0]:= 'Заголовок';
    

Изменять ширину отдельных колонок нужно программно, используя свойство ColWidth:

ColWidth[<индекс_колонки>]
    

Например, так:

StringGrid1.ColWidths[0]:= 120;
    

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

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

Доделаем сетку. Высоту строк (свойство DefaultRowHeight) установите в 18 пикселей. Разверните свойство Options и установите в True параметр goEditing, чтобы пользователь мог редактировать данные сетки. Количество фиксированных строк и колонок оставим по умолчанию - 1. Это будут заголовки столбцов и строк. Их нужно заполнить, для этого выделите форму и сгенерируйте для неё событие OnCreate. Код события такой:

procedure TForm1.FormCreate(Sender: TObject);
begin
  //заполняем заголовки строк:
  StringGrid1.Cells[0, 1]:= 'Иванов И.И.';
  StringGrid1.Cells[0, 2]:= 'Петров П.П.';
  StringGrid1.Cells[0, 3]:= 'Николаев Н.Н.';
  StringGrid1.Cells[0, 4]:= 'Бонд Дж.';
  //заполняем заголовки колонок:
  StringGrid1.Cells[1, 0]:= 'Год рождения';
  StringGrid1.Cells[2, 0]:= 'Место рождения';
  StringGrid1.Cells[3, 0]:= 'Прописка';
  StringGrid1.Cells[4, 0]:= 'Семейное положение';
  //меняем ширину колонок:
  StringGrid1.ColWidths[0]:= 120;
  StringGrid1.ColWidths[1]:= 90;
  StringGrid1.ColWidths[2]:= 150;
  StringGrid1.ColWidths[3]:= 100;
  StringGrid1.ColWidths[4]:= 120;
end;
    

Код, думаю, простой и понятный. Как только программа загрузится, сетка будет настроена. Теперь для колонки "Год рождения" создадим маску ввода, чтобы пользователю оставалось только вписать цифры. Выделите сетку, в Инспекторе объектов перейдите на вкладку События и сгенерируйте событие OnGetEditMask. Код события следующий:

procedure TForm1.StringGrid1GetEditMask(Sender: TObject; ACol, ARow: Integer;
  var Value: string);
begin
  if ACol=1 then Value:= '99.99.9999 г.';
end;
    

Как видите, в событии становятся доступными такие переменные, как ACol и ARow, в которых находится индекс соответственно, текущих колонки и строки. В переменную Value следует прописать текст маски, мы это делаем, только если текущей является вторая колонка (индексация с нуля):

  if ACol=1 then Value:= '99.99.9999 г.';
    

Теперь запрограммируем кнопку, дав пользователю возможность добавлять новые фамилии. Сгенерируйте для кнопки событие OnClick, код события следующий:

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  if InputQuery('Укажите новое имя',
                'Впишите фамилию, имя и отчество в формате "Фамилия И.О.":',
                s) then begin
    StringGrid1.RowCount:= StringGrid1.RowCount + 1;
    StringGrid1.Cells[0, StringGrid1.RowCount-1]:= s;
  end;
end;
    

С помощью функции-запроса InputQuerry мы получаем в переменную s фамилию, имя и отчество. Затем добавляем еще одну строку:

    StringGrid1.RowCount:= StringGrid1.RowCount + 1;
    

В заключение, в последнюю строку первого (фиксированного) столбца мы вписываем эти новые ФИО:

    StringGrid1.Cells[0, StringGrid1.RowCount-1]:= s;
    

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

Вот и все. Сохраните проект, запустите его на выполнения и опробуйте сетку в деле:

Приложение в работе

увеличить изображение
Рис. 24.4. Приложение в работе
Инга Готфрид
Инга Готфрид
Александр Скрябнев
Александр Скрябнев

Через WMI, или используя утилиту wmic? А может есть еще какие более простые пути...