понедельник, 28 июля 2008 г.

Подробно о компонентах. TTable.

Начнем урок нестандартно - с небольшего лирического отступления. Как известно, в Object Pascal классы описываются так:

type TMyClass = class(TParentClass) ... свойства, <br /> переменные, методы ... end; то есть принято имя класса начинать с буквы T, от слова type. Аналогично в C++ имена классов начинаются с С, от слова class. Сами компоненнты при этом часто называют без Т в начале: Table, Query и др. Поэтому в дальнейшем будем считать эти два вида именования в разговоре синонимами. Т.е. Table и TTable - один и тот же компонент. В программе же именовать их надо правильно, с буквой T в начале.

Установить компонент на форму можно как обычно - мышкой. Но от этого еще ничего не изменится. Ваша программа не научится просматривать или изменять данные. Для этого придется выполние еще кое-какие действия. Простейшие виды этих действий мы рассматривали в прошлом уроке. В этом посмотрим, что еще можно сделать с этим компонентом.

Установив компонент на форму, следует дать ему осмысленное имя. Например tabClients или ClientsTab если речь идет о таблице клиентов или еще что-нибудь в этом роде. Для этого установим свойство Name компонента. Далее нас будет интересовать свойство DatabaseName. Это имя псевдонима, которое создается в BDE Administrator или в Database Desktop. Подробнее об этом в первом уроке.

После этого установим свойство TableName. Это свойство укажет, на какую из таблиц будет указывать наш TTable в этой базе. После того, как вы установили эти свойства, можно свойство Active попробовать сменить c false на true. Если у вас получилось - вы все сделали правильно. Если нет - возможно вы неправильно указали имя базы или таблицы. (DatabaseName или TableName).

Кроме этих свойств нас заинтересуют следующие:

AutoCalcFields
При установке в false обработчик события OnCalcFields игнорируется. При установке в true - вызывается для каждой записи. Весьма полезно для вычисляемых полей, но имеет особенности, о которых ниже.
Filtered
При установке в false значение свойства Filter и обработчик OnFilterRecord игнорируются. При установке в true - выполняются для каждой записи.
Filter
Имеет смысл только при Filter = true. Фильтрует записи в таблице. Обычно применим тогда, когда можно по одному простому выражению определить, надо ли включать запись в результирующий набор данных или нет. Если для того, чтобы определить это недостаточно одного выражения, применяется обработчик события OnFilterRecord.
IndexFieldName и IndexFields
Позволяет выбрать активный индекс. Если значения не выбраны, текущим считается первичный индекс. В программе так и применяется: ClientsTab.IndexName := 'FamIdx'; или ClientsTab.IndexFieldNames := 'Fam;Im;Otch'; отсортирует таблицу клиентов по фамилиям именам и отчествам. ClientsTab.IndexName := ''; или ClientsTab.IndexFieldNames := ''; отсортитует по первичному ключу. в этих примерах предполагалось, что у вас есть таблица клиентов с индексом FamIdx по полям Fam, Im, Otch. Важно отметить такую особенность TTable при работе с индексами - можно написать ClientsTab.IndexFieldNames:= 'Fam;Im'; при этом таблица будет отсортирована по фамилиям и именам, игнорирую порядок по отчесвам. Но нельзя написать ClientsTab.IndexFieldNames := 'Fam;Otch'; т.к. это нарушает порядок полейЮ по которым был построен индекс.
MasterField и Mastersource
Применяются при организации отношений главный-подчиненный Подробнее как-нибудь позже.
ReadOnly
Простой способ сделать вашу таблицу доступной только для чтения.
UpdateObject
Применяется в паре с CashedUpdate, подробности при рассмотрении клиент-серверных приложений и компонента TQuery.

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

Те свойства, которые видны в Object Inspector лишь вершина айсберга. Большая часть полезностей доступна программным путем. Например, перечислим способы получить данные из поля:

  1. A := Table1Field1.Value;
  2. A := Table1.FieldByName('Field1').Value;
  3. A := Table1.FieldByName('Field1').AsString; // AsInteger, AsFloat и т.д.
  4. A := Table1.FieldValues['Field1'];
  5. A := Table1.['Field1'];
  6. A := Table1.Fields.Fields[1].Value;
  7. A := Table1.Fields.Fields[1].AsString; // AsInteger, AsFloat и т.д.

Кто еще знает, напишите мне, будет интересно.
А вот еще популярные свойтсва

BOF и EOF
Указывают на начало и конец таблицы. Применяются, например, так: while not Table1.EOF do begin ... ... Table1.Next; end; // while Важное замечание. При достижении конца таблицы, когда указатель указывает на последнюю запись, Table1.EOF вернет вам false. А вот если вы попытаетесь выйти за пределы таблицы (Table1.Next), вот тогда EOF вернет true;
RecordCount
Позволяет узнать общее количество записей в таблице.
RecNo
Указывает на номер текущей записи в таблице. В отличие от FoxPro и подобных, не может считаться идентификатором строки, так как зависит от индексации и других факторов. Если вам необходимо перемещаться по таблице, а затем возвращаться обратно, пользуйтесь ключевыми полями и первичными индексами. Или закладками (TBookmark).
State
Указывает на состояние таблицы. Применяется, например, так: if Table1.State in [dsInsert, dsEdit] then Table1.Post; т.е. перед вызовом метода Post сначала проверяется, а имеет ли смысл этот вызов.

Есть еще много других свойств, но их мы будем рассматривать по мере работы.

Кроме свойств нас еще будут интересовать методы. Наиболее используемые:

Edit
Переводит строку в режим редактирования. Без этого вы не сможете в ней ничего изменить. Пример ниже.
Insert/Append
Добавляют строку. Таблица автоматически переходит в режим редактирования добавленной строки. Отличие - Append добавляет строку в конец, а Insert - в текущую позицию.
Post
Сохраняет внесенные изменения. Необходимо вызывать для каждой измененной записи, перед переходом на следующую. Пример применения: while not EmployeeTab.eof do begin EmployeeTab.Edit; EmployeeTab['TabNo'] := EmployeeTab.RecNo; EmployeeTab.Post; EmployeeTab.Next; end; // while Этот фрагмент присваивает всем работникам табельные номера в соответствии с их порядком в таблице. Напомню, что порядок может зависеть от индекса, фильтра и других параметров.
Cancel
Отказ от изменений.
Delete
Удаляет запись.
First/Last
Переход на первую/последнюю запись. Какая запись считается первой/последней, зависит от индекса, фильтра и других параметров.
Prior/Next
Переход на предыдущую/последующую запись.
SetRange, SetRangeStart, SetRangeEnd
используются для фильтрации
FindKey
Поиск записи по ключу. Ключ определяется текущим индексом.
FindNearest
Обычно применяется, когда не найдена запись, точно соответствующая ключу, и надо найти хоть что-нибудь.
Locate
Весьма интересный метод. Позволяет искать по любому полю или его части, по возможности использует имеющиеся индекы, даже если они не активны. Медленне, чем FindKey. Обычно применяется, когда нет ключа для требуемого поля.
Lookup
Аналогична Locate, но перемещения не происходит. Просто возвращается ключ подходящей строки.

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

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

BeforeEdit, BeforeDelete, BeforePost и другие
Названия говорят сами за себя. Вызываются сразу перед соответствующим событием. Однако следует проявлять осторожность - не следует выполнять действия, которые могут повторно вызвать этот же метод. Т.е. нельзя с обработчике BeforePost выполнять сохранения изменений, т.к. это повторно вызовет этот же метод. Стандартная ошибка начинающих. Или вызвать метод Edit в обработчике BeforePost, не учтя, что после внесения изменений они будут сохраняться, что повлечет повторный вызов BeforePost, который вызовет Edit... до бесконечности.
AfterEdit, AfterDelete, AfterPost и другие
Аналогично предыдущим, названия говорят сами за себя. Вызываются сразу после соответствующего события. Те же предостережения.
BeforeScroll, AfterScroll
Вызываются при каждом перемещении между записями.
OnCalcFields
При установленном AutoCalcFields вызывается для каждой записи. Именно в обработчике этого события и происходит вычисление значений вычисляемых полей. Удобно, но надо помнить о:
  1. Если вычислений будет слишком много, программа будет заметно тормозить.
  2. Если внутри этого обработчива выполнить переход на другую запись, этот же метод будет вызван для той записи, и так дале, что может выйти боком.
OnFilterRecord
Возвращает true или false, что указывает, обращать внимание на эту запись или нет. Слишком объемные вычисления могут замедлить программу.
OnPostError, OnEditError и т.п.
Позволяют вам самим обработать исключительную ситуацию. Что-то исправить, или хотя бы вывести сообщение об ошибке на понятном пользователю языке.

Ну хватит. Выводы, которые вы для себя должны были сделать за последние 2 урока:

  • Простые приложения можно слепить на Delphi за пару минут не сильно напрягаясь.
  • Когда надо добавить функциональность, которую не обеспечивают простые решения, у вас есть множество средств для этого. Это очень сильная черта, присуща именно Delphi. В других стредах у вас либо связаны руки, либо вы получаете большую функциональность вместе с большой сложностью. В Delphi вы можете сами выбрать баланс между простотой и функциональностью.
  • В Delphi очень большая библиотека компонент для работы с базами данных. Она вам очень поможет.
  • Всего не запомнишь. Наиболее употребляемые свойства, методы запоминаются, об остальных достаточно просто знать, что они существуют. При необходимости это можно найти в документации.
  • Многое зависит от приемов программирования. Какие-то изобретаешь сам, какие-то заимствуешь у других. Бывает, что и менее опытный чем ты товарищ придумает элегантное решение какой-то проблемы. Не грех и поучиться друг у друга.