6. Работа с полями - компонент
TField TFieldКомпонент
TField позволяет обращаться к полям таблиц баз данных. Каждый набор данных - неважно, ТТаЫе или TQuery - состоит из записей, а те, в свою очередь, состоят из полей. Таким образом, в составе записи имеется минимум одно поле.В
Delphi имеется возможность использовать при работе с НД или все поля, определенные в данной ТБД на текущий момент, или использовать только часть существующих полей.Использование только части имеющихся полей дает то преимущество, что значения неиспользуемых полей не могут быть изменены ни вследствие алгоритмической ошибки, ни вследствие злонамеренного умысла, поскольку неиспользуемые поля считаются неизвестными.
Существует два способа задания состава полей для НД.
Первый состоит в том, что после создания НД (компонент ТТаЫе или
TQuery) не предпринимается никаких дополнительных действий по уточнению состава полей. Тогда:•
для компонента TTable - будет разрешен доступ ко всем полям, определенным в данный момент в ТБД, связанной с компонентом ТТаЫе;•
для компонента TQuery - будет разрешен доступ ко всем полям (в том числе и результатам выражений), указанным в списке возвращаемых полей в операторе SELECT. Хотя этот оператор в результате выполнения SQL-запроса может возвращать НД, составленный из нескольких физических ТБД, сами поля будут считаться принадлежащими к единому НД, образовавшемуся в результате выполнения SQL-запроса из свойства TQuery.SQL. К полю в этом случае можно обращаться с помощью метода FieldByName компонентов TTable и TQueryfunction FieldByName(const FieldName: string): TField;
или через свойство указанных компонентов
Fields [Index], которое возвращает указатель на тип TField. Подробнее об этом свойстве будет сказано ниже.Определить во время выполнения приложения, используются для набора данных все поля по умолчанию или только их часть, определенная посредством редактора полей, можно при помощи свойства набора данных
го
, rt property DefaultFields: Boolean;ЗАМЕЧАНИЕ. Аббревиатура го (
read only) означает, что свойство доступно только для чтения; rt (run time) означает, что свойство доступно только во время выполнения приложения. Полный список принятых сокращений см. в начале книги. Значение True указывает, что используются поля по умолчанию; False -что используются поля, определенные при помощи редактора полей.
Второй способ определения состава полей заключается в том, что для данного НД поля как компоненты
TField добавляются в форму с помощью редактора полей Delphi. О нем будет сказано чуть ниже.К таким полям можно обращаться через его имя, определяемое в свойстве
Name компонента TField, соответствующего данному полю.По умолчанию, при добавлении в форму компонента
TField, его имя генерируется так: берется имя НД (т.е. значение свойства Name компонента TTable или TQuery, к которому принадлежит поле) и к нему добавляется имя поля, взятое из структуры ТБД (TTable) или из запроса (TQuery).Так, поле с именем
FIO, используемый в НД Students, при добавлении в форму средствами редактора полей, получит имя StudentsFIO.ЗАМЕЧАНИЕ.
Это имя будет относиться целиком к компоненту TField, а не только к значению (например, "Иванов И.И."), которое поле FIO содержит в текущей записи ТБД. Компонент TField, как будет показано ниже, обладает рядом свойств, методов и событий, обращаться к которым следует через указание имени компонента TField и имени свойства, метода или обработчика события, например:StudentsFIO. Index
:= 5; // изменить порядковый номер поля StudentsFIO. Readonly := True; //запретить изменение значений поляТаким образом, экземпляр
StudentsFIO компонента TField трактуется не как конкретное значение, которое принимает поле FIO в конкретной строке ТБД, а как весь столбец набора данных, обладающий единым поведением, т.е. едиными свойствами, методами и событиями для всех записей набора данных. Значение поля для текущей записи доступно с помощью свойств Value, AsBoolean, AsString и т.д. Например:IF StudentsFIO.AsString = '
Иванов' then ....Итак, когда поле НД определено в форме в качестве экземпляра компонента
TField, к нему можно обращаться по имени (содержащемуся в свойстве Name), а также через метод НД FieldByName и свойство Fields [Index].Если хотя бы для одного поля НД создан компонент
TField, первый принцип, т.е. принцип использования всех полей ТБД или результата SQL-запроса, отвергается. В НД будут считаться определенными только те поля, для которых созданы компоненты TField, а иные поля - отвергаться как несуществующие для данного НД (TTable или TQuery).К "несуществующим" полям обратиться изданного НД нельзя никак. Такие попытки будут возбуждать исключительные ситуации с сообщением '
Field <имя> not found'.Вновь вернуться к принципу использования всех полей ТБД или результата SQL-запроса можно, удалив в редакторе полей все определенные ранее
TField или добавив с его помощью недостающие поля.То, какие поля определять в данном НД, зависит от назначения разрабатываемого приложения и диктуется целесообразностью.
ЗАМЕЧАНИЕ. Если нужно иметь доступ к полю, но не показывать его значений в компонентах, визуализирующих данные (например, в компоненте
TDBGrid), свойство Visible этого поля следует установить в состояние False.6.2. Использование редактора полей для организации компонентов
TField и установки значений их свойств Для того, чтобы определить один или несколько компонентов TField, нужно:1. Выбрать необходимый НД (компонент
TTable или TQuery);2. Нажать правую кнопку мыши;
3. Во всплывающем меню выбрать режим
Field Editor (запуская тем самым редактор полей};4. Вновь нажать правую кнопку мыши и во всплывающем меню выбрать
Add Fields',5. В появившемся списке полей ТБД (
TTable) или полей, участвующих в запросе (TQuery), выбрать необходимые (рис. 6.1)и нажать кнопку
Ok. Для каждого из указанных полей будет создан компонент TField:6. Если необходимо изменить свойства конкретного поля или написать обработчик для какого-либо события, необходимо в редакторе полей выбрать нужное поле и, используя инспектор объектов, установить значение в свойство или определить обработчик события.
Поля в таблицах баз данных различаются по типу - символьные, целочисленные, логические, blob-поля и т.д.
Соответственно, по типу различаются и компоненты
TField, и собственно, TField есть родительский тип, определяющий базовые свойства и методы для своих потомков, типизированных полей. Иерархия компонентов- полей такова:TField
TBIobField
большой двоичный объектTGraphicField
графическое поле(работает с содержимым blob-поля как с графическим изображением)TMemoField
мемо-поле (интерпретирует BLOB-поле как большой текст)TBooleanField
логическое полеTBinaryField
нетипизированное двоичное полеTBytesFieid
поле для хранения байтовых значений фиксированной длиныTVarBytesField
поле для хранения байтовых значений переменной длиныTDateTimeField
поле для хранения даты и времениTDateField
поле для хранения только датыTTimeField
поле для хранения только времзниTNumericField
поле для хранения числовых значенийTBCDField
BCD-значенийTFloaTField
значений с плавающей точкойTCurrencyField
в том числе в денежном форматеTIntegerField
целочисленных значенийTAutoIncField
в том числе автоинкрементныхTSmallIntField
в том числе коротких целыхTWordField
в том числе в формате беззнакового длинного целогоTStringField
поле для хранения строковых значенийОсобенности каждого типа будут рассмотрены ниже, равно как и процедуры и функции
Delphi, применяемые для работы с полями определенных типов. Покажем общие для всех этих типов свойства, методы и события.6.4 Обращение к полям и их значениям
Как было сказано в п. 6.1, следует различать обращение к полю и обращение к его значению. Рассмотрим способы обращения к полям.
К полю можно обратиться, указав имя поля несколькими способами :
1. Если полю соответствует компонент
TField - через имя данного компонента, которое определяется свойством Name. Повторим, что по умолчанию имя компонента TField устанавливается как результат сцепления имени TTable или TQuery и собственно имени поля в ТБД. Например, для поля FIO, определенного в ТБД, работа с которой происходит через Table 1, по умолчанию будет выбрано имя Table I FIO. Тогда использование поля происходит подобно приведенному ниже:Table1FIO.AsString := '
Иванов'; // илиTable1FIO.Value := '
Иванов';2.
Используя метод FieldByName ('ИмяПоля ') набора данных, function FieldByName(const FieldName: string): TField;Например
,Table1.FieldByName('FIO).AsString := '
Иванов'; //илиTable1.FieldByName('FIO).Value:= '
Иванов';3. Используя свойство
Fields[индекс] набора данных,property Fields[Index: Integer]: TField;
Индекс
является порядковым номером поля в определении ТБД. Отсчет идет от 0. Пусть поле Name определено в ТБД третьим по счету. Тогда его индекс равен 2 и использование поля может происходить так:Table1.Fields[2].AsString := '
Иванов'; // илиTablel.Fields[2].Value:= '
Иванов' ;Если поле входит в индекс данного НД, свойство
IsIndexField: Boolean;возвращает во время выполнения значение
True.4. Используя свойство набора данных
property FieldValues|const FieldName: string]: Variant;
Это свойство позволяет обращаться к полю через его имя, указываемое как содержимое параметра
FieldName, например: Tablel.FieldValues['FIO'] := 'Иванов';Поскольку свойство
FieldValues принимается для набора данных по умолчанию, его имя при обращении к полю можно опускать: Tablel['Name'] := 'Иванов' ;ЗАМЕЧАНИЕ 1. Более предпочтительным считается обращение к полю через его имя или через метод
FieldByName, поскольку в этом случае мы обращаемся к конкретному полю по его имени. Следовательно, к несуществующему полю обратиться нельзя.Пусть поле FIO удалено из структуры ТБД. Тогда обращение к нему по имени из ассоциированного с данной ТБД НД приведет к ошибке.
Менее предпочтительным является обращение к полю через свойство набора данных
Fields [индекс]. Если поле FIO было объявлено в ТБД вторым по счету (обращение Fields[l]), а затем удалено из структуры ТБД, обращение Fields[l] в программном коде будет воспринято как обращение к физически второму полю в ТБД. Этим полем будет третье поле, следовавшее за FIO перед тем, как FIO было удалено из структуры ТБД. После удаления FIO третье поле станет по счету вторым.Налицо алгоритмическая ошибка, которая может быть не распознана, если FIO и поле, ставшее после удаления FIO вторым, имеют одинаковые или совместимые типы.
Такие ошибки очень трудно локализовать. Поэтому, если это возможно, следует воздержаться от обращения к полю через
Fields [индекс].ЗАМЕЧАНИЕ 2. Свойство Index компонента TField содержит порядковый номер поля:
• в ТБД, если для компонента TTable, ассоциированного с данной ТБД, не создано ни одного компонента TField;
• в списке компонентов
TField, относящихся к данному НД - для компонента TTable, если для него определен хотя бы один TField, и всегда - для компонента TQuery.Порядок следования полей важен, когда содержимое НД визуализируется посредством компонента
TDBGrid. В этом случае номер столбца в компоненте TDBGrid соответствует номеру поля в списке TField. И наоборот, если изменить порядок столбца в компоненте TDBGrid (например, "перетащив" столбец на другое место), это приведет к изменению индекса и Fields[l] до перетаскивания будет относиться к другому полю, нежели Fields[l] после перетаскивания столбцов, поскольку свойство Index перетаскиваемого и некоторых других полей изменится.Это относится как к случаю, когда
TField для НД определены, так и к случаю, когда используются все поля ТБД. В последнем случае "перетаскивание" столбца в компоненте TDBGrid на новое место, конечно, не изменит физического порядка следования полей в структуре ТБД; однако с логической точки зрения его индекс (порядковый номер) для данного НД изменится.Последствия, которые могут принести в структуру НД изменения свойства
Index, делают обращения к полю через свойство Fields [индекс] набора данных нежелательными.6.5. Обращение к значению поля. Свойства
Value и AsNNNК значению поля можно обратиться при помощи свойств
Value и AsNNN. Свойствоproperty Value: Variant
возвращает значения следующих типов:
property Value Variant; //
Все компонентыproperty Value string; //TStringField, TBIobField
property Value Longint; //TAutoIncField, TIntegerField, TSmallintField,TWordField
property Value Double; //TBCDField, TCurrencyField, TFloatField
property Value Boolean; //TBooleanField
property Value TDateTime; //TDateField, TDateTimeField, TTimeField
Это дает возможность пользоваться свойствами приведения типов полей
(AsString, Aslnteger и т.д.) в гораздо меньших масштабах. Тем не менее, обойтись без них удается далеко не всегда.Аналогичные значения возвращает рассмотренное в предыдущем разделе свойство набора данных
FieldValues.Например
,var N : Integer;
N := TablelNumber.Value; // TablelNumber
типа TIngereFieldОбращение к значению поля через свойство
AsNNN.Существуют следующие свойства приведения типов полей:
property AsBoolean: Boolean;
property AsCurrency: Currency;
property AsDateTime: TDateTime;
property AsFloat: Double;
property Aslnteger: Integer;
property AsString: String;
property As Variant: Variant;
Каждое из этих свойств приводит значение поля к соответствующему типу данных, означенному в названии свойства. Например, если
TablelNumber -компонент TIntegerField (поле, хранящее целочисленные значения), для приведения его к типу String можно воспользоваться свойствомEditl.Text := TablelNumber.AsString;
Несомненно, тип поля должен быть совместимым с типом данных, к которому приводится значение поля. Например, если TablelSumma - компонент TFloatField (поле, хранящее вещественные значения), попытка привести его к несовместимому типу Boolean, IF TablelSumma.AsBoolean THEN ...приведет к ошибке компиляции.
В приводимой ниже таблице показана совместимость значений полей разных типов.
Обозначение:
= типы равнозначны;
+ преобразование возможно;
+ RI
преобразование возможно, округление до ближайшего целого;? преобразование происходит, если возможно; часто зависит от формата показа (свойство
DisplayFormat);х преобразование не разрешено;
Рассмотрим свойства семейства
AsNNN более подробно:property AsBoolean: Boolean; -
числовые значения приводятся к типу Boolean, если содержат 0 (False) или 1 (True). Символьные значения - если содержат в качестве первого символа "Y", "у", "Т" или "t" (или "Yes" или "True"), и False во всех иных случаях.property AsDateTime: TDateTime; -
для приведения к типу TDateTime значений TDateField, TDateTimeField и TTimeField, хотя вместо этого лучше использовать свойство Value, а также для приведения к типу TDateTime строковых значений, находящихся в соответствующем формате.property AsFloat: Double; -
служит для приведения к типу Double значений полей TFloatField, TBCDField и TCurrencyField, AsFloat, хотя вместо этого лучше использовать свойство Value.property Aslnteger: Longint; - служит для приведения к типу Longint полей типа TIntegerField, TSmallintField и TWordField, хотя вместо этого лучше использовать свойство Value.
Для полей типа
TStringField преобразование к Longint выполняется, если оно возможно.property AsCurrency: Currency; -
служит для приведения к типу Currency. property AsString: string; - служит для приведения к типу String. property As Variant: Variant; - служит для приведения к типу Variant.6.6. Форматирование значений полей при их показе в визуальных компонентах во время выполнения
OnGetTextПусть поле ТБД хранится не в том виде, в котором должно показываться. Тогда отформатировать его перед тем, как оно будет показано в визуальных компонентах, работающих с данными -
TDBGrid, TDBEdit и т.д., можно, определив алгоритм форматирования в обработчике события OnGetText для данного компонента TField.В процедуре-обработчике присутствуют такие параметры:
Text -
отформатированное значение, показываемое в столбце компонента TDBGrid, в TDBEdit или других визуальных компонентах, связанных с БД;Display Text -
позволяет определить, произошло событие OnGetText при показе значения (значений) поля (True) или при модификации пользователем значения поля (False).Пример.
Пусть поле Company входит в состав ТБД, содержимое которой показывается в TDBGrid с использованием Table 1. Необходимо при показе содержимое данного поля заключать в кавычки (хотя оно хранится без кавычек). Если пользователь захочет изменить значение поля в какой-либо записи, нужно показывать содержимое поля, представленное заглавными буквами.procedure TForm1.Table1CompanyGetText(Sender: TField; var
Text: OpenString;
DisplayText: Boolean);
begin
IF DisplayText THEN
Text := '"' + TablelCompany.AsString + '"'
ELSE
Text := AnsiUpperCase(TablelCompany.AsString);
end;
Когда происходит показ некоторой записи из
Table 1 в TDBGrid, для нее возникает событие OnGetText и вызывается приведенный выше обработчик с DisplayText = True. В результате в компоненте TDBGrid весь столбец, соответствующий полю TablelCompany, будет содержать значения в кавычках.Затем, если пользователь захочет в какой-либо записи изменить значение данного поля, оно будет выдано ему для редактирования заглавными буквами.
ОГРАНИЧЕНИЕ. Если для поля определен обработчик события
OnGetText, игнорируются режимы форматирования, определенные в свойствах DisplayFormat и EditMask данного поля. Свойство DisplayFormatСвойство
property DisplayFormat: string;
применяется
для форматирования при показе полей типа TDateField, TDateTimeField, TIntegerField, TSmallintField, TTimeField, TWordField.Для
форматирования полей типа TIntegerField, TSmallintField и TWordField может также применяться стандартная процедура procedure Str(X [: Width [: Decimals ]]; var S);Для
форматирования полей типа TDateField, TDateTimeField и TTimeField применяется функция function Date TimeToStr (Date Time: TDateTime): String;Для
форматирования полей типа TBCDField, TCurrencyField и TFloatField применяется функция function FloatToTextFmt(Buffer: PChar; Value: Extended:Format: PChar): Integer;
Поддерживаются следующие спецификаторы форматов:
Спец-р |
Что влечет |
0 |
Число. Если незначащий разряд равен 0, показывать его. |
# |
Число. Если незначащий разряд равен 0, не показывать его. |
|
Десятичная точка. Разделяет целую и дробную часть числа Принимается во внимание только первая точка, остальные игнорируются. |
' |
Разделитель тысяч. Каждая группа чисел из 3 разрядов в целой части отделяется от иных разрядов запятой. |
' |
Разделяет положительное, отрицательное и нулевое значение. |
Е+ |
Научный формат действительных чисел |
"XX' или 'XX' |
Символы внутри двойных или одинарных парных кавычек не форматируются и выводятся как есть. Например, число 123.45 с форматом '#.# "рублей"' выведется как '123.5 рублей' |
Примеры использования форматов.
Форматируемое число |
987654.321 |
-987654.321 |
0.27 |
-0.27 |
0 |
не форматировано |
987654.321 |
-987654.321 |
0.27 |
-0.27 |
0 |
0 |
987654 |
-987654 |
0 |
0 |
0 |
0.00 |
987654.32 |
-987654.32 |
0.27 |
0 |
0.00 |
# |
987654 |
-987654 |
|||
#.## |
987654.32 |
-987654.32 |
.27 |
-.27 |
|
#,##0.00 |
987,654.32 |
-987,654.32 |
0.27 |
-0.27 |
0.00 |
#,##0.00; (#,##0.0) |
987,654.32 |
(987,654.32) |
0.27 |
(0.3) |
0.00 |
#,##0.00;; Нолик |
987,654.32 |
-987,654.32 |
0.27 |
-0.27 |
Нолик |
О.ОООЕ+00 |
9.877Е+05 |
-9.877Е+0 |
2.700Е-01 |
-2.700E-01 |
0.000Е+00 |
#.###Е-0 |
9.877Е5 |
-9.877Е5 |
2.7Е-1 |
-2.7Е-1 |
0Е0 |
Пусть значение поля 3456.777. Тогда, если
DisplayFormat := '#.##', то будет показано 3456.78.ЗАМЕЧАНИЕ. Формат показа поля может быть динамически переназначен во время выполнения. Например, для заполнения данной таблицы мы воспользовались компонентами
Edit1 и Button1 и производили динамическую замену формата поля Table I Salary так:procedure TForm1.Button1Click(Sender: TObject);
begin
Table1Salary.DisplayFormat := Edit1.Text;
end;
DisplayFormat
игнорируется, если определен обработчик для события OnGetText.ЗАМЕЧАНИЕ.
В случае, если поле, требующее обязательного ввода в него значения (свойство Required = True), на момент запоминания в таблице БД (т.е. на момент начала выполнения метода Post) содержит пустое или нулевое значение, возбуждается исключение EDBEditError.6.7. Форматирование полей во время их редактирования
Свойство
property EditMask: string;служит для контроля правильности вводимых в поле значений. Ограничения накладываются при помощи формата. Если некоторый введенный символ не удовлетворяет маске, он не воспринимается.
Для строковых полей значение данного свойства может использоваться для форматирования не только входных, но и выходных значений вместе со свойством
Display Text.Маска представляет собой символьную строку. Она состоит из 3-х частей:
1.
Собственно маска;2. Символ, определяющий, будут ли литералы (символ после указателя '\') включаться в форматируемое значение как его часть (значение 1) или не будут(значение 0);
3. Символ, используемый в маске для представления пробела.
Символы, которые могут входить в маску:
! |
Подавляет ведущие пробелы. В отсутствие этого символа в данных подавляются хвостовые пробелы |
> |
Все следующие символы будут на верхнем регистре, пока не встретится символ < |
< |
Все следующие символы будут на нижнем регистре, пока не встретится символ > |
<> |
Регистр не проверяется. Все остается на том регистре, как ввел пользователь |
\ |
Следующий за ним символ является литералом, т.е. включается в форматируемое значение |
L |
В данной позиции должен появиться только символ алфавита |
| |
Аналогично L, но символ в данной позиции может и отсутствовать |
А |
В данной позиции должен появиться только символ алфавита или цифра |
а |
Аналогично А, но символ в данной позиции может и отсутствовать |
С |
В позиции обязателен любой символ |
с |
Аналогично С, но символ в данной позиции может отсутствовать |
0 |
В данной позиции обязателен цифровой символ |
9 |
В данной позиции должен появляться только цифровой символ или не появляться никакой |
# |
В данной позиции должен появляться только цифровой символ плюс или минус или не появляться никакой |
: |
Разделитель часов, минут и секунд для значения типа времени Если в данной национальной кодировке для указанных целей используется иной символ, он используется вместо символа ':' |
/ |
Разделитель месяца, дня и года в датах. Если в данной национальной кодировке для указанных целей используется иной символ, он используется вместо символа ':' |
; |
Разделитель частей маски |
_ |
Заменитель пробела в маске |
В модуле
Mask имеются следующие константы, которые определяют некоторые компоненты маски по умолчанию
Имя константы |
Нач.значение |
Смысл |
DetaultBlank |
_ |
Обозначение пробела в маске |
MaskFieldSeparator |
, |
Разделитель частей маски |
MaskNoSave |
0 |
Если 0, символы маски не будут включаться в значение; если 1,-будут. |
Пример.
Маска '!\(999\)000\-00\-00;1;_'. Введено '0952223344'. В период ввода представлялось на экране как '(095)222-33-44', запомнилось как '(095)222-33-44'. Если бы была использована маска '!\(999\)000\-00\-00;0;_', в период ввода представлялось на экране как '(095)222-33-44', запомнилось как '0952223344'.Значение свойства Edit Mask игнорируется, если определен обработчик для события OnGelText.
Свойство
го property EditMaskPtr: string;возвращает значение маски редактирования. Поскольку свойство доступно только на чтение, его следует использовать вместо
EditMask в тех случаях, когда маска должна быть только прочитана. В итоге мы защищаемся от случайных изменений маски.Свойство
property EditFormat: string;применяется для форматирования значений полей типа
TIntegerField, TSmallintField, TWordField перед их редактированием. Форматирование выполняется функцией Float To TextFmt.Пример.
Пусть значение поля 3456.777. Тогда если EditFormat := '#.#', то при редактировании значение поля первоначально будет показано как 3456.8.Свойство
property Text: string;содержит строковое изображение значения поля в том виде, в котором оно показывается в визуальном компоненте, когда НД находится в режиме редактирования (
dsEdit) . Свойство Display Text содержит строковое изображение значения поля, когда НД находится не в режиме редактирования.6.8. Проверка введенного в поле значения
Свойство IsNull и обработчики событий OnSetText, On Validate, OnChangeСвойство
IsNull: Boolean; во время выполнения возвращает True, если поле содержит пустое значение.Проверить введенное в поле значение на его соответствие некоторым ограничениям или условиям можно в обработчике события
OnValidate. Это событие наступает при изменении значения поля либо вручную, либо программно. Событие наступает до выполнения метода Post, который запоминает изменения БД. Поэтому, если полю присвоено неверное значение, выполнение метода Post можно предотвратить, выполнив метод Abort или возбудив исключительную ситуацию (raise Exception.Create}. Заметим, что для новых записей данное событие выставляется также, поскольку при занесении значений в поля пустые значения заменяются на непустые (т.е. модифи-цируются). Данный подход контроля правильности значений называется ориентированным на поля.Существует и другой подход,
ориентированный на записи. Он состоит в том, что в структуре ТБД при ее определении описываются ограничения на значения, которые может принимать данное поле. В этом случае контроль правильности ведется автоматически.Пример. Пусть поле
Table I Company не должно содержать символа '@'.procedure TForm1.Table1CompanyValidate(Sender: TField) ;
begin
IF POS('@',TablelCompany.AsString) > 0 THEN
begin
ShowMessage
('Обнаружен символ @!');Abort;
end;
end;
или
procedure TForm1.Table1CompanyValidate(Sender: TField);
begin
IF POS('@',TablelCompany.AsString) > 0 THEN
raise Exception.Create('
Неверное значение');end;
Если символ '@' содержится в значении, присвоенном полю, метод
Abort или принудительно возбужденная исключительная ситуация не позволят выполниться методу Post и запись с неверным полем не будет физически записана в БД НД останется в том состоянии, в котором он находился (режиме редактирования dsEdit или добавления новой записи dslnsert}.Для проверки введенного значения может быть использовано и другое событие,
OnSetText Подобно событию OnVahdate, оно возникает при изменении значения поля Однако на момент события новое значение полю не присвоено и, если этого не сделать программно в обработчике события, сделано никогда не будет.Например, пусть в поле
Table 1PUR_PRICE типа TFloatField было изменено значение с 10 на 200 Пусть новое значение не должно превышать 100 Тогда обработчик события будет выглядеть такprocedure TFormI.TablelPUR_PRICESetText(Sender: TField; const Text: String);
var Tmp : Real;
begin
Tmp := StrToFloat(Text);
IF Tmp > 100 THEN
ShowMessage('
Ошибочное значение')ELSE
TablelPUR_PRICE.Value := Tmp;
end;
что аналогично такому обработчику события
OnVahdate.procedure TFormI.TablelPUR_PRICEValidate(Sender: TField);
begin
IF TablelPUR_PRICE.Value > 100 THEN begin
ShowMessage('Ошибочное значение');
Abort;
END;//if
end;
Как видно, особенность события
OnSetText состоит в том, что в обработчик передается константа-параметр Text, содержащая в текстовом виде новое значение, назначенное полю, в то время как действительное значение поля остается без измененияТретье событие,
OnChange, может быть использовано для тех же целей, что и On Validateprocedure TFormI.TablelPUR_PRICEChange(Sender: TField);
begin
IF TablelPUR_PRICE.Value > 100 THEN
raise Exception.Create('Ошибочное значение');
end;
Порядок вызова обработчиков событий
OnSetText, On Validate, OnChangeПри изменении значения поля события, обработчики которых позволяют контролировать правильность занесенного в поле значения, вызываются в следующей последовательности
1 OnSetText;
2 On Validate,
3 OnChange
Это важно, когда действия по проверке правильности нового значения поля сосредоточены не в одном обработчике, а распределены в обработчиках разных событий
Относительно события
OnSetText известно, что если полю присвоено ошибочное значение, то нет нужды выполнять метод Abort или возбуждать исключительную ситуацию для предотвращения занесения этой записи в ТБД (поскольку новое значение в поле в этом случае еще не занесено) Наоборот, если поле удовлетворяет критериям правильности, в него программно нужно записать введенное пользователем новое значение (передаваемое в обработчик как параметр const Text String)В обработчиках событий
OnVahdate и OnChange, наоборот, в этом случае необходимо выполнять метол Abort или возбуждать исключительную ситуацию для предотвращения занесения этой записи в ТБД (поскольку новое значение в поле в этом случае уже занесено)Однако следует помнить, что событие
OnChange возникает только после события On Validate Поэтому, обработчик события OnChange может быть и не вызван, если обработчик On Validate выполняет метод Abort или возбуждает исключительную ситуацию для того, чтобы измененная запись с некорректным значением поля не была записана в ТБД6.8.2. Значение поля по умолчанию и ограничения на значения поля
Значение поля по умолчанию можно установить при помощи свойства
property DefaultExpression: string;В случае указания значений, отличных от целочисленного, они должны заключаться в кавычки
В компоненте TField могут быть определены ограничения на значения этого или иных полей. Ограничение указывается при помощи SQL-подобного синтаксиса в свойстве
property CustomConstraint: string;
например
,TablelOklad.CustomConstraint :=' Oklad >= 300 and Oklad <= 2000';
Свойство
property ConstraintErrorMessage: string;позволяет указать сообщение об ошибке, выдаваемое пользователю в случае, если введенное значение поля не удовлетворяет ограничению, указанному в свойстве
CustomConstraint, напримерTablelOklad. ConstraintErrorMessage := '
Оклад должен быть в диапазоне 300...2000 ';Свойство
property ImportedConstraint: string;содержит ограничения значения поля, "навязанные" сервером. Их не нужно переопределять; дополнительные ограничения можно наложить при помощи свойства
CustomConstraint.Свойство
ro property HasConstraints: Boolean;возвращает
True, если для поля определены ограничения в свойствах CustomConstraint, ImportedConstraint или DefaultExpression. В противном случае свойство возвращает False.6.9. Создание вычисляемых полей
Если необходимо создать вычисляемое поле, значение которого вычисляется по значениям других полей, поступают так:1. В редакторе полей необходимо создать новое поле, пометив его как
Calculated. Для этого нужно сделать текущим (при помощи мыши) необходимый НД, нажать правую кнопку мыши, выбрать в меню Field Editor и снова нажать правую кнопку мыши и выбрать в меню New Field. Затем в окне диалога необходимо указать имя поля, его тип и для строковых полей - длину (рис. 6.3).Для нового поля будет создан компонент
TField, доступ к которому отныне можно осуществлять редакторе полей. 2. Для компонента НД, к которому принадлежит вычисляемое поле, необходимо определить обработчик события OnCalcFields. Например, для НД Table 1, ассоциированному с ТБД "Сотрудники", будем заносить в вычисляемое поле TablelVychis! значение 'Да', если в поле Table1inYaz (знание иностранных языков) этой записи содержится значение True. В противном случае в поле TablelVychis! будем заносить пустое значение (форма приложения показана на рис. 6.4.):procedure TGridForm.TablelCalcFields(DataSet: TDataSet) ;
begin
IF Table1InYaz.Value THEN TablelVychos!.AsString := '
Да' ELSETablelVychosl.AsString := '';
end;
Событие
OnCalcFields возникает всякий раз, когда курсор (указатель записи) перемещается в НД от записи к записи (например, после выполнения методов Next, Last и т.д., или при движении по записям в TDBGrid вручную). Это событие возникает и при инициализации НД (после открытия), а также после фильтрации записей в НД, что, впрочем также связано с изменением местоположения указателя записи.Кроме того, если свойство набора данных
AutoCalcFields установлено в True, событие OnCalcFields наступает также и при модификации значений невычисляемых полей в режимах dslnsert и dsEdit данного НД или НД, реляционно с ним связанного (когда установлены ограничения целостности в самой ТБД, а не тогда, когда они подразумеваются).Процедура-обработчик события
OnCalcFields содержит реализацию алгоритма вычисления значения вычисляемого поля или группы полей. Необходимо помнить, что в этом обработчике значение может быть присвоено только вычисляемому полю и не может - полю, определенному в структуре таблицы БД.ЗАМЕЧАНИЕ. Иногда бывает необходимым присваивать вычисляемым полям значения, не содержащиеся в полях других таблиц. Иными словами, иногда бывает полезным записывать в вычисляемое поле значение некоторых переменных формы. Например, пусть мы добавляем записи в НД ТЫ, по некоторому алгоритму рассчитывая значения поля
Summa. Пусть для расчета значения поля Summa используется переменная TekOstatok. И мы хотим значение TekOstatok для каждой записи занести в вычисляемое поле TbITO.И здесь встречается одна сложность: занесение значения в вычисляемое поле производится в обработчике события
OnCalcFields и это происходит в то время, когда значения локальной переменной TekOstatok уже утрачены.В этой ситуации в процессе выполнения алгоритма, добавляющего записи в ТЬ1, приходится запоминать значения локальной переменной в каком-либо динамическом списке, а затем извлекать из него соответствующие элементы в обработчике события
OnCalcFields, присваивая значения этих элементов полю TbITO.6.10. Создание полей выбора данных (lookup-полей)
Кроме обычных полей, связанных с полями ТБД, и вычисляемых полей, в
Delphi имеется возможность создавать поля выбора данных (lookup).Поля выбора данных
одного набора данных содержат значения их другого набора данных, связанного по ключу с НД, к которому принадлежит поле выбора данных. Поле выбора данных всегда доступно только для чтения и не может быть одновременно полем выбора данных и вычисляемым полем. Реляционное отношение НД, служащего источником значений для поля выбора данных и НД, к которому оно принадлежит, есть "один-ко-многим" и реже "один-к-одному". Это означает, что на один вариант значения в наборе данных-источнике должно приходиться одно или несколько связанных значений в НД, к которому принадлежит поле выбора.Для определения поля набора данных необходимо создать новое поле в редакторе полей, сразу же установив радио-группу Field Type в значение Lookup (рис. 6.5.).
Затем устанавливаем значения свойств:
DataSet -
Имя НД-источника значений для поля выбора данных;Key Fields -
Индексные поля набора данных-владельца поля выбора данных. По этим полям НД-владелец соединяется с НД-источником значений поля выбора данных. Если в индексе имеется несколько полей, они перечисляются через точку с запятой;Lookup Fields -
Индексные поля НД-источника значений для поля выбора. По значениям этих индексных полей устанавливается связь набора-источника со значениями индексных полей НД-владельца поля выбора (они указаны в параметре Key Fields). Если в индексе имеется несколько полей, они перечисляются через точку с запятой;Result Field -
Поле набора данных-источника, возвращаемое в качестве результата. Необходимо следить, чтобы тип вновь создаваемого поля и поля результата совпадали.Указанным выше параметрам редактора полей соответствуют свойства компонента
TFieldproperty LookupDataSet: TDataSet;
property KeyFields: string;
property LookupKeyFields: string;
property LookupResultField: string;
Аналогичным по последствиям будет установка соответствующих свойств в инспекторе объектов для вновь добавляемого поля. Заметим, что свойство поля
property Lookup: Boolean;
должно быть установлено в
True.Разберем
пример отношения "один-к-одному" Пусть существует ТБД 'Сотрудники", которая включает в себя поля "Табельный номер" (TabNum), "ФИО" (FIO), "Должность" (Doljnost) и "Ученая степень" (UchStepen) С ней ассоциирован НД Table 1 (рис 6 6)и ТБД "Информация о сотрудниках", которая включает в себя ФИО сотрудника (поле FIO), год рождения (
GodRojd) и семейное положение (SemPoloj). С ней ассоциирован НД DataModule1 Table1 (рис 6 7). ТБД "Сотрудники" и "Информация о сотрудниках" связаны по индексу, образованному полями FIO Требуется при просмотре в ТБД "Сотрудники" выводить год рождения данного сотрудника ниже компонента TDBGrid, в DBTextl Для этой цели для Tablel, связанной с НД "Сотрудники", входим в редактор почеи и создаем новое поле, сразу же установив радио-группу Field Type в значение Lookup (рис 6 8)Затем устанавливаем значения свойств
DataSet
НД DataModule Table1, который связан с ТБД "Информация о сотрудниках"),Key Fields
поле FIO НД Tablel (ТБД "Сотрудники"), которое отображается в TDBGrid),Lookup Fields
поле FIO НД DataModulel Table (ТБД "Информация о сотрудниках"),Result Field
поле GodRojd НД DataModulel Tablel (ТБД "Информация о сотрудниках") Таким образом, нами определен новый компонент Table 1GR типа TField (а точнее, TStringField), источником данных для которого служит поле GodRojd ТБД "Информация о сотрудниках", из той ее записи, у которой значение поля FIO совпадает со значением поля FIO соответствующей записи ТБД "Сотрудники"Далее размещаем в форме компоненты
Label 1 и DBTextl (связанный с полем Table1GR) Тогда для текущей записи сотрудника в DBGndl в DBText1 отображается год рождения для данного сотрудника из ТБД "Информация о сотрудниках"(рис 6.9)Поля выбора данных могут использоваться для автоматического занесения информации в данную ТБД из другой ТБД. Часто это очень удобно. Рассмотрим пример, для чего несколько модифицируем показанные выше ТБД.
Допустим имеется ТБД "Сотрудники" состоящая из полей: уникальное
TabNum (табельный номер), FIO (ФИО), KodDol (код должности), и ТБД "Оклады", состоящая из полей уникальное KodDol (код должности), ОоЦпо$1(должность), Oklad (оклад). ТБД "Сотрудники" и "Оклады" состоят в связи "многие-к-одному" или, если смотреть со стороны ТБД "Оклады", "один-ко-многим". Полем связи является Doljnost.Как видно, в ТБД "Сотрудники" может быть несколько записей с одним и тем же значением поля KodDol (например, 144), в то время как в ТБД "Оклады" конкретный код должности может встречаться только единожды.
Присвоение кодов каким-либо атрибутам таблиц БД является весьма распространенным приемом. Если по этим атрибутам для нескольких ТБД имеют место реляционные отношения, для хранения индексов по кодам (цифровым полям) и их использования для доступа к данным требуется много меньше дискового пространства и времени, нежели для хранения индексов по оригинальным значениям. В нашем примере это обосновано: лучше строить индекс по коду (слово), нежели по символьному значению поля Doljnost (рис. 6.10). Это особенно актуально, если символьное поле имеет большую длину (например, 200 символов). Индексы по таким полям получаются очень большими. Другой выгодой от использования кодов является то, что в справочнике (lookup-ТБД) "Оклады" коды должности KodDol представляют собой автоинкрементное поле, т.е. поле, уникальное значение которого BDE устанавливает автоматически. В дальнейшем его менять нельзя. Это снимает необходимость каскадного изменения в дочерней ТБД "Сотрудники" при изменении значения поля связи (KodDol) в родительской ТБД "Оклады". ЗАМЕЧАНИЕ. Автоинкрементные поля более свойственны локальным СУБД. Для удаленных (серверных) СУБД автоинкрементные поля заменяются другими механизмами, например, генераторами (InterBase). Если бы связь между данными ТБД была установлена по полю Doljnost, a не KodDol, то при изменении значения должности с "приват-доцент" на "доцент" в ТБД "Оклады" на значение "доцент" должны были бы измениться значения всех записей в ТБД "Сотрудники", у которых поле Doljnost содержит значение "приват-доцент" (рис. 6.11, 6.12).Однако поскольку мы используем для связи между названными таблицами код, в ТБД "Оклады" значение поля
Doljnost можно менять сколь угодно много раз - на связь между ТБД это не окажет никакого влияния. Заметим, что по полю Doljnost в ТБД "Оклады" должен быть построен уникальный индекс, чтобы предотвратить возможность ввода двух одинаковых должностей с разными окладами. В данном примере кодом будет являться поле KodDol, a семантически значимым полем поле Doljnost. Более подробно с данным вопросом можно ознакомиться в разделе "Обеспечение ссылочной целостности"Поставим задачу. Пусть при вводе данных в ТБД "Сотрудники" нам известна должность конкретного сотрудника, но кода ее, мы, разумеется, не помним. Поэтому нужно:
• обеспечить просмотр и выбор интересующей должности из справочника (lookup-ТБД) "Оклады";
• обеспечить занесение кода этой должности из поля KodDol ТБД "Оклады" в поле KodDol ТБД "Сотрудники".
В форме НД SotrTable соответствует ТБД "Сотрудники", а НД OkladyTable соответствует ТБД "Oklady".Добавим в набор данных
SotrTable поле выбора данных Lookupchik (рис. 6.13) Значения параметров поля выбора, показанные на рис. 6.13, можно читать так "Исходя из реляционной связи по полю KodDol со стороны НД SotrTable (свойство Key Fields) и по полю KodDol (свойство LookupKeys) со стороны набора данных-источника значений поля выбора (набора данных OkladyTable, свойство DalaSel), заполнять поле реляционной связи в SotrTable значением, взятым из записи, которая будет выбрана в ниспадающем меню В ниспадающем меню показывать только поле Doljnost (параметр ResultField) НД OkladyTable "6.10.3. Буфер значений полей выбора данных
Свойство
property LookupCache: Boolean;
определяет, будут ли значения полей выбора данных храниться в кэше (буфере) - значение
True -, или нет (значение False).Свойство
ro property LookupList: TLookupList;
содержит список значений из набора данных-источника для полей выбора данных, индексированных набором значений полей, список которых содержится в свойстве
KeyFields. Метод ValueOfKey компонента TLookupList возвращает результирующее поле (значение поля выбора данных)Value := LookupList.ValueOfKey(DataSet.FieldValues[KeyFields]) ;
Список полей выбора данных формируется после открытия НД или после обновления списка методом компонента
Tfield procedure RefreshLookupList; TxxxField• TStringField -
хранит строковое значение длиной до 255 символов. Строки большей длины нужно хранить в blob-полях (TMemoField).Свойство
property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если символьные поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, дляпреобразования
ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.• целочисленные поля -
применяются для хранения целых чисел различной длины:TIntegerField-
от -2,147,483,648 до 2,147,483,647TSmallintField -
от -32,768 до 32,767TWordField
- от 0 до 65,535Свойства
property MaxValue: Longint; и property MinValue: Longint; могут определять максимальное и минимальное значение поля.• числовые поля с плавающей точкой - применяются для хранения целых чисел различной длины:
TFloatField -
числа, чьи абсолютные значения - 5.0*10-324 to 1.7*10+308 до 15-16TCurrencyField -
аналогично TFloatField, но в денежном форматеTBCDField -
вещественные десятичные числа с фиксированным числом разрядов после точки. До 18 символов. Диапазон представляемых чисел зависит от числа знаков. Применяется только для Paradox.Свойство
property Precision: Integer; позволяет указать число знаков после десятичной точки (по умолчанию 15).Свойства
property MaxValue: Longint; и property MinValue: Longint; могут определять максимальное и минимальное значение поля.• TBooleanField -
содержит значения True или False.• поля даты и времени:
TDateTimeField -
содержит значения даты и времени в формате TDateTime.TDateField -
значения даты в формате TDateTTimeField
- значения времени в формате TTime.• поля для хранения значений произвольных форматов:
TBIobField -
произвольное байтовое поле без ограничения длины.Метод
procedure LoadFromFile(const FileName: string); загружает содержимое поля из файла, метод procedure LoadFromStream(Stream: TStream); - из потока.Метод
procedure SaveToFile(const FileName: string); сохраняет содержимое поля в файле, метод procedure SaveToStream(Stream: TStream); - в потоке.Свойство
property BlobSize: Integer; содержит размер в байтах blob-поля данной записи.Свойство
property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если blob-поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, для преобразования ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.Свойство
property BlobType: TBIobType; возвращает тип blob-поля. Возможные значения: ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary.TBytesField -
произвольное байтовое поле без ограничения длины. Не имеет методов LoadFromFile, LoadFromStream, SaveToFile, SaveToStream. Свойствоproperty DataSize: Word
позволяет определить во время выполнения, сколько байт нужно для хранения поля в памяти.TVarBytesFieId -
произвольное байтовое поле длиной до 65,535 байт. Текущая длина может быть получена из первых двух байт поля.TMemoField -
строковое значение неопределенной длины (мемо-поле).Метод
procedure Clear; очищает мемо-поле.Метод
procedure LoadFromFile(const FileName: string); загружает содержимое поля из файла, метод procedure LoadFromStream(Stream: TStream); - из потока.Метод
procedure SaveToFile(const FileName: string); сохраняет содержимое поля в файле, метод procedure SaveToStream(Stream: TStream); - в потоке.Свойство
property BlobSize: Integer; содержит размер в байтах blob-поля данной записи.Свойство
property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если blob-поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, для преобразования ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.Для работы с мемо-полями в БД
Delphi предоставляет компонент TDBMemo. Его описание приводится в разделе "Компоненты для работы с текущей записью набора данных ".TGraphicField -
произвольное байтовое поле, трактуемое как графическое изображение.Свойство
го property DataType: TFieldType;возвращает информацию о типе данных поля.
Возможные значения:
ftUnknown
неизвестный или неопределенный типftString
строковое или символьное полеftSmallint
16-битное целоеftlnteger
32-битное целоеftWord
16-битное беззнаковоеftBoolean
логическое полеftFloat
числовое с плавающей точкойftCurrency
поле в денежном форматеftBCD
двоично-десятичное полеftDate
поле типа датыftTime
поле типа времениftDateTime
поле типа даты и времениftBytes
фиксированное число байт в двоичном представленииfiVarBytes
переменное число байт в двоичном представленииftAutoInc
автоинкрементное поле (автоматически увеличивающийся счетчик, 32-битное целое)FtBlob
большой двоичный объектFtMemo
текстовое мемо-полеFtGraphic
графическое полеFtFmtMemo
форматированное текстовое мемо-полеFtParadoxOle
поле Paradox OLEFtDBaseOle
поле dBASE OLEFtTypedBinary
типизированное двоичное поле
Вид поля определяется свойством
property FieldKind: TFieldKind;
Возможны следующие значения:
fkData
физическое поле в базе данныхfkCalculated
вычисляемое полеfkLookup
поле, возвращающее значение (lookup-поле)fkInternalCalc
вычисляемое поле, значение которого можно хранить в наборе данныхЗАМЕЧАНИЕ. Поле
awssiflcInternalCalc есть вычисляемое поле, которое может записываться в набор данных. Однако не следует путать его с просто вычисляемым (fkCalculated) полем. Последнее определяется как вычисляемое в редакторе полей и алгоритм его расчета задается в обработчике события набора данных OnCalcFields. Просто вычисляемые поля не могут храниться в качестве полей НД.Поле вида
fkInternalCalc может храниться в НД, но оно не определяется как вычисляемое в редакторе полей, а вычисляется SQL-сервером или BDE и содержится в "живом" (то есть доступном для изменения) наборе данных, возвращаемом как результат выполнения SQL-запроса.Пусть после выполнения запроса
SELECT TOVAR AS
Т, ZENA AS Z FROM TOVARY WHERE ZENA > 100получим набор данных, состоящий из полей Т и Z. Чтобы определить для такого поля, какое физическое поле в таблице послужило источником его формирования, используют свойство
property Origin: string;Однако, значение свойства
Origin доступно в среде редактора полей и только для тех полей, для которых явно создан компонент TField в редакторе полей. 5. Проверка применимости символа в полеСвойство
function IsValidChar(InputChar: Char): Boolean; virtual;возвращает
True, если символ InputChar может быть записан в поле того или иного вида:
Тип поля |
Верные символы |
FtBoolean |
все |
FtSmallInt |
числа 0 ..9, плюс (+), минус (-). |
FtWord |
числа 0 ..9, плюс (+), минус (-) |
FtAutoInc |
числа 0 ..9, плюс (+), минус (-) |
FtDate |
все |
Ftlnteger |
числа 0 ..9, плюс (+), минус (-) |
FtTime |
все |
FtCurrency |
числа 0 ..9, плюс (+), минус (-), буква Е или е и разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере) |
FtDateTime |
все |
ftFloat |
числа 0 ..9, плюс (+), минус (-), буква Е или е и разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере) |
ftBCD |
числа 0 ..9, плюс (+), минус (-), разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере) |
ftString |
все |
ftVarBytes |
все |
ftBytes |
все |
ftBlob |
все |
ftDBaseOle |
все |
ftFmtMemo |
все |
ftGraphic |
все |
ftMemo |
все |
ftParadoxOle |
все |
ftTypedBinary |
все |
ftUnknown |
все |