Для блокирования элементов управления можно использовать компонент ActionList, предназначенный для синхронизации элементов управления.
Блокировка попыток пользователя изменить немодифицируемый набор данных должна выполняться также при добавлении и удалении записей.
При выполнении метода Edit непосредственно перед переводом набора данных в режим редактирования возникает событие BeforeEdit, которое можно использовать для проверки возможности перехода в этот режим. Например, при попытке пользователя редактировать запись ему может быть предложено подтвердить свои действия. Для отмены процесса редактирования в обработчике события BeforeEdit можно сгенерировать "тихое" исключение.
При переходе в режим редактирования с помощью кнопки Button проверку его допустимости можно выполнить в обработчике события ее нажатия. Однако обычно удобнее использовать обработчик события BeforeEdit, т. к. оно генерируется при переводе набора данных в режим редактирования любым способом. Вот пример соответствующей процедуры:
procedure TForml.TablelBeforeEdit(DataSet: TDataSet); begin
Глава 7. Навигационный доступ к данным с помощью BDE
211
if MessageDlg('Выполнить редактирование?',
reconfirmation, [mbYes, mbNo], 0) о mrYes then Abort; end;
После перевода набора данных в режим редактирования можно с помощью инструкций присваивания изменять значения полей текущей записи. При этом нужно учитывать тип поля, выполняя при необходимости операции приведения типов. Например:
Tablel.FieldByName('City').AsString:= Editl.Text;
Tablel.FieldByName('Code').AsInteger:= StrToInt(Edit2.Text);
Tablel.FieldByName('Price').AsFloat:= StrToFloat(Edit3.Text);
Перед выполнением приведенных инструкций набор данных Tablel должен находиться в режиме редактирования или вставки. Если редактор Edit2 или Edit3 содержит данные в формате, не соответствующем целому и вещественному числам, то генерируется исключение.
Для проверки, вносились ли изменения в запись, можно проанализировать свойство Modified типа Boolean. Если свойство имеет значение True, то было изменено значение как минимум одного поля текущей записи.
После ввода информации сделанные изменения должны быть или подтверждены, или отменены.
Метод Post записывает модифицированную запись в таблицу БД, снимает блокировку записи и переводит набор данных в режим просмотра. Если набор данных не находился в режиме редактирования, то вызов метода Post приведет к генерации исключения. Перед его выполнением автоматически вызывается обработчик события BeforePost типа TDataSetNotifyEvent, а сразу после выполнения — обработчик события AfterPost типа TDataSetNotifyEvent. Используя событие BeforePost, можно проверить сделанные изменения и при необходимости отменить их, например, прервав выполнение метода с помощью вызова "тихого" исключения.
Рассмотрим процедуру, в которой осуществляется редактирование записей:
procedure TForial.btnPriceChangeClick(Sender: TObject); var bml: TBookmark; coeff, x: real; begin
// Проверка, является ли набор данных модифицируемым if not Tablel.CanModifу then begin
MessageDlg('Записи изменять нельзя!', mtError, [mbOK], 0);
exit;
end; // Получение коэффициента try
coeff:= StrToFloat(Editl.Text);
212
Часть II. Технологии доступа к данным
except
MessageDlg('Неправильный коэффициент!' + #13#10 + 'Повторите ввод.', mtError, [mbOK], 0); if Editl.CanFocus then Editl.SetFocus;
exit; end; // Запоминание позиции текущего указателя bml: = Tablel.GetBookmark;
// Отключение отображения записей визуальными компонентами Tablel.DisableControls; // Перебор всех записей Tablel.First; while not Tablel.EOF do begin
// Чтение цены из очередной записи
х:= Tablel.FieldByName('Price').AsFloat;
// Пересчет цены
x:= x * Coeff;
// Изменение цены в текущей записи
Tablel.Edit;
Tablel.FieldByName('Price').AsFloat:= x;
Tablel.Post;
// Переход к следующей записи
Tablel.Next;
end; // Восстановление позиции текущего указателя //и отображение записей визуальными компонентами if Tablel.BookmarkValid(bml) then Tablel.GotoBookmark(bml); if Tablel.BookmarkValid(bml) then Tablel.FreeBookmark(bml); Tablel.EnableControls; end;
Здесь для всех записей набора данных Tablel выполняется пересчет поля цены Price. Цены умножаются на коэффициент, который введен в редакторе Editl.
Метод Post вызывается автоматически при переходе к другой записи с помощью методов First, Last, Next и Prior, если набор данных находится в режиме редактирования, и изменения в записях не подтверждены. Поэтому в приведенном примере метод Post можно было не вызывать, т. к. сразу после него вызывается метод Next. Однако при использовании методов FindFirst, FindLast, FindNext и FindPrior неподтвержденные изменения в записях будут потеряны.
Пользователь подтверждает сделанные в записях изменения, управляя соответствующими компонентами, явно или неявно вызывающими метод Post. Конкрет-
Глава 7. Навигационный доступ к данным с помощью BDE
213
ные действия пользователя зависят от используемых компонентов. Например, при работе с компонентом DBGrid изменения подтверждаются (фиксируются) при переходе к другой записи или нажатии клавиши <Enter>, а в компоненте DBNavigator можно нажать кнопку Post Edit (Подтвердить изменения).
Независимо от способа вызова, метод Post может завершиться неудачно, например, если не заданы значения полей, которые не могут быть пустыми, или значение выходит за установленные для него допустимые пределы. В этом случае набор данных обычно возвращается в состояние, которое было до перехода в режим редактирования. При ошибке выполнения метода Post генерируется событие OnPostError типа TDataSetErrorEvent. Кодируя обработчик этого события, можно попытаться исправить ошибку.
Метод Cancel отменяет изменения, выполненные в текущей записи, и возвращает набор данных в режим просмотра. При выполнении метода Cancel вызываются обработчики событий BeforeCancel и AfterCancel типа TDataSetNotifyEvent.
Пользователь может отменить сделанные в записях изменения, используя элементы управления компонентов. Например, при работе с сеткой DBGrid изменения отменяются нажатием клавиши <Esc>, а в компоненте DBNavigator — нажатием кнопки Cancel Edit.
В случае применения механизма транзакций для отмены изменений сразу в нескольких записях можно обратиться к методу RollBack класса TDateBase.
При редактировании текущей записи последовательность инструкций присваивания и вызовов метода Post можно заменить вызовом метода SetFields.
Процедура SetFields (const Values: array of const) устанавливает все или часть значений полей текущей записи. Параметр Values содержит массив значений, которые присваиваются этим полям, при этом порядок значений соответствует порядку полей в наборе данных. Кроме того, должны соответствовать типы полей и типы присваиваемых им значений. Если значений в массиве меньше, чем полей в наборе данных, то эти значения присваиваются первым полям, а оставшиеся поля не изменяются. Чтобы не присваивать значение какому-либо полю, в качестве соответствующего значения задается Nil, в результате поле не изменяется. Если число значений в массиве превышает число полей, то при выполнении метода SetFields генерируется исключение.
(_____ ЗамечаниеJ
Повторим еще раз, что значения Nil и Null не равны друг другу и имеют разный смысл. Nil означает отсутствие значения, a Null— пустое (нулевое) значение. Если вместо значения Nil указать значение Null, то поле будет изменено и содержать значение Null.
Если для набора данных использовался Редактор полей, то при выполнении метода SetFields учитывается порядок полей, установленный с помощью этого Редактора. В противном случае принимается порядок полей, определенный при создании таблицы БД.
214
Часть II. Технологии доступа к данным
Метод SetFields удобно использовать для изменения значений нескольких полей. После его выполнения набор данных автоматически возвращается в режим просмотра. Пример использования метода SetFields в программе:
Tablel.Edit;
Tablel.SetFields ([Nil, 'Иванов П.О.', Nil, 'Переводчик', 650]);
Здесь во второе, четвертое и пятое поля текущей записи набора данных Tablel заносятся фамилия, должность и оклад соответственно. Значения первого и третьего полей этой записи не изменяются.
Добавление записей
Добавлять записи можно только к модифицируемому набору данных, в противном случае будет сгенерировано исключение.
Для добавления записи нужно выполнить следующие действия:
1. Перевести набор данных в режим вставки.
2. Задать значения полей новой записи.
3. Подтвердить сделанные изменения или отказаться от них, после чего набор данных снова переходит в режим просмотра.
Для добавления записей используются методы Insert, insertRecord, Append и AppendRecord.
Метод insert переводит набор данных в режим вставки и добавляет к нему новую пустую запись. Новая запись вставляется в позицию, на которой находится указатель текущей записи. При необходимости перед вызовом метода insert нужно выполнить перемещение текущего указателя в требуемую позицию набора данных.
После перевода набора данных в режим вставки дальнейшие действия по заданию (изменению) значений полей, подтверждению или отмене сделанных изменений не отличаются от аналогичных действий при редактировании записи. При этом для задания или изменения значений полей используются инструкции присваивания и метод SetFields, а для подтверждения или отмены изменений — методы Post и Cancel. Некоторые поля новой записи могут остаться пустыми, если до подтверждения им не были присвоены значения.
(_____ ЗамечаниеJ
При переходе в режим вставки к набору данных добавляется пустая запись, значения полей которой не заданы. Если запись добавляется к подчиненному набору данных, связанному с главным набором связью "главный-подчиненный", то индексные поля автоматически получают корректные значения, и программист может не заботиться об их заполнении.
Рассмотрим следующий пример:
procedure TForml.ButtonlClick(Sender: TObject); begin
Глава 7. Навигационный доступ к данным с помощью BDE
215
Tablel.Insert;
Tablel.FieldByName('Name').AsString:= Editl.Text;
Tablel.FieldByName('Group').AsString:= Edit2.Text;
Tablel.Post;
end;
Здесь в новой записи задаются значения полей фамилии (Name) и группы (Group), остальные поля остаются пустыми. В этом и последующих примерах предполагается, что набор данных не связан с главным набором данных, и полям не были автоматически присвоены значения как индексным полям.
Часто удобно устанавливать значения сразу нескольких полей с помощью метода SetFields. Напомним, что после выполнения этого метода набор данных автоматически возвращается в режим просмотра, и запись считается включенной в набор данных. Если требуется изменить часть полей новой записи, то нужно использовать редактирование, а не вставку. Например, так:
procedure TForml.btnlnsertClick(Sender: TObject);
begin
if not Tablel.CanModifу then begin
MessageDlg('Записи изменять нельзя!', mtError, [mbOK], 0);
exit;
end;
Tablel.Insert;
Tablel.SetFields ([Nil, edtName.Text, Nil, edtPost.Text, IntToStr(edtCode.Text)]);
end;
Здесь значения полей новой записи пользователь вводит в редакторах. Первое и третье поля, а также поля с номером больше пяти остаются пустыми. Перед добавлением записи выполняется проверка, можно ли изменять набор данных Tablel.
Метод insertRecord объединяет функциональность методов Insert и SetFields, выполняя те же действия, что и их последовательный вызов. Процедура InsertRecord (const Values: array of const) вставляет в позицию указателя текущей записи новую запись, задавая значения всех или части ее полей. Например:
procedure TForml.btnlnsertClick(Sender: TObject);
begin
Tablel.InsertRecord ([Nil, edtName.Text, Nil, edtPost.Text,
IntToStr(edtCode.Text)]); end;
Методы Append и AppendRecord отличаются от методов Insert и InsertRecord тем, что вставляют запись в конец набора данных, а не в позицию указателя текущей записи.
216
Часть II. Технологии доступа к данным
Пользователь управляет набором данных, в том числе вставкой записи, с помощью элементов управления формы. Для компонента DBGrid новая запись добавляется к набору данных при нажатии клавиши <Insert> или при переходе на последнюю запись. Если в форме находится компонент DBNavigator, то новая запись добавляется при нажатии кнопки Insert Record.
При добавлении новой записи любым методом возникают события
Beforelnsert и Afterlnsert типа TDataSetNotifyEvent, а также событие OnNewRecord типа TDataSetNotifyEvent. В обработчиках событий Beforelnsert и OnNewRecord можно выполнить действия, связанные с проверкой набранных пользователем данных или с заполнением (инициализацией) части полей новой записи. Вот пример соответствующей процедуры:
procedure TForml.TablelNewRecord(DataSet: TDataSet);
begin
Tablel.FieldByName('Unit').AsString:= 'штука';
Tablel.FieldByName('NDS').AsString:= '20';
end;
При утверждении или отмене изменений, связанных с добавлением новой записи, также генерируются события BeforePost и AfterPost или BeforeCancel и AfterCancel.
Удаление записей
Удаление текущей записи выполняет метод Delete, который работает только с модифицируемым набором данных. В случае успешного удаления записи текущей становится следующая запись, если же удалялась последняя запись, то курсор перемещается на предыдущую запись, которая после удаления становится последней. В отличие от некоторых СУБД, в Delphi удаляемая запись действительно удаляется из набора данных.
Обычно метод Delete вызывается для удаления просматриваемой записи, однако с его помощью можно удалить и редактируемую запись. Если набор данных находится в режиме вставки или поиска, то вызов метода Delete аналогичен вызову метода Cancel, отменяя соответственно вставку или поиск записи.
(_____ ЗамечаниеJ
Если набор данных пуст, то вызов метода Delete порождает исключение.
При удалении записи генерируются события BeforeDelete и AfterDelete типа TDataSetNotifyEvent. Используя обработчик события BeforeDelete, можно отменить операцию удаления, если не соблюдаются определенные условия.
Если выполнение метода Delete приводит к ошибке, то возбуждается исключение, и генерируется событие OnDeleteError, в обработчике которого можно
Глава 7. Навигационный доступ к данным с помощью BDE
217
выполнить собственный анализ ошибки. Вот небольшая процедура, в которой перед удалением записи запрашивается подтверждение пользователя:
procedure TForial.btnDeleteClick(Sender: TObject);
begin
if MessageDlg('Удалить запись?',
reconfirmation, [mbYes, mbNo], 0) = mrYes then Tablel.Delete; end;
Удаление нескольких последовательно расположенных записей имеет особенность, связанную с тем, что при вызове метода Delete в цикле по перебору удаляемых записей не нужно вызывать методы, перемещающие указатель текущей записи. После удаления текущей записи указатель автоматически перемещается на соседнюю (обычно следующую) запись. Так можно удалить все записи набора данных:
procedure TForml.btnDeleteAHClick(Sender: TObject);
var n: longint;
begin
Tablel.Last;
for n:= Tablel.RecordCount downto 1 do Tablel.Delete;
end;
В примере перебор записей выполняется с конца набора данных. После удаления текущей записи указатель снова оказывается на последней записи.
Для набора данных Table удалить все записи можно также с помощью метода EmptyTable, который вызывается в режиме исключительного доступа к таблице БД.
Перед удалением записи часто предварительно выполняется поиск записи (записей), удовлетворяющей заданным условиям. Для отбора группы удаляемых записей используется фильтрация. Метод Delete позволяет удалить записи, видимые в наборе данных. Поэтому с помощью фильтрации можно временно оставить в наборе данных записи, которые подлежат удалению, а после удаления фильтрацию отключить.
Пример формы приложения
Обычно визуальные компоненты, предназначенные для редактирования, добавления и удаления записей, группируются на форме и работают взаимосвязанно. Вместе или рядом с этими компонентами часто располагают элементы управления сортировкой, фильтрацией и поиском. Тем самым для пользователя обеспечивается удобство выполнения различных операций с данными.
Для примера рассмотрим форму (рис. 7.10), с помощью которой можно изменять записи таблицы, содержащей сведения о товарах. В информацию о товаре входят уникальный код записи (поле Code), название товара (поле Name), единица измерения (поле Unit), цена единицы (поле Price) и примечание (поле
218
Часть II. Технологии доступа к данным
Note). Поле названия и поле цены товара обязательно должны быть заполненными, поле кода — автоинкрементное, и его значение при добавлении новой записи формируется автоматически.
Рис. 7.10. Форма приложения для работы с товарами
Ниже приводится код модуля uChange формы Forml приложения.
unit uChange; interface
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DBTables, Grids, DBGrids, StdCtrls, Mask, DBCtrls, ExtCtrls;
type
DBGridl DataSourcel Tablel btnEdit cbCanEdit pnlChange Labell dbeName |
TForml = class(TForm)
TDBGrid; TDataSource; TTable; TButton; TCheckBox; TPanel; ■ TLabel; TDBEdit;
Глава 7. Навигационный доступ к данным с помощью BDE
219
dbePrice:
dbeNote:
Label2:
Label3:
Label4:
lblChangeKind:
btnChangeOK:
btnChangeCancel:
dbcbUnit:
btnClose:
lblRecordCount:
btnlnsert:
btnDelete:
TDBEdit;
TDBEdit;
TLabel;
TLabel;
TLabel;
TLabel;
TButton;
TButton;
TDBComboBox;
TButton;
TLabel;
TButton;
TButton;
procedure StateBrowse(Sender: TObject); procedure StateChange(Sender: TObject); procedure btnEditClick(Sender: TObject); procedure cbCanEditClick(Sender: TObject); procedure TablelBeforeEdit(DataSet: TDataSet); procedure btnCloseClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure btnChangeOKClick(Sender: TObject); procedure btnChangeCancelClick(Sender: TObject) procedure btnDeleteClick(Sender: TObject); procedure btnlnsertClick(Sender: TObject); procedure TablelBeforePost(DataSet: TDataSet); procedure TablelBeforelnsert(DataSet: TDataSet)
private
{ Private declarations } public
{ Public declarations }
end;
Forml: TForml; implementation {$R *.DFM}
// Режим просмотра
procedure TForml.StateBrowse(Sender: TObject);
220
Часть II. Технологии доступа к данным
= False; = False; = clBlack; = 'ПРОСМОТР ЗАПИСИ' |
begin
lblRecordCount.Caption:= 'Всего записей ToStr(Tablel.RecordCount);
cbCanEditClick(Sender); btnChangeOK.Enabled btnChangeCancel.Enabled lblChangeKind.Font.Color lblChangeKind.Caption end;
+ Int-
// Режимы изменения
procedure TForml.StateChange(Sender: begin
btnEdit.Enabled:= False;
btnlnsert.Enabled:= False;
btnDelete.Enabled:= False;
btnChangeOK.Enabled:= True;
btnChangeCancel.Enabled:= True;
end;
TObject)
procedure TForml.FormCreate(Sender: TObject); begin
// Исходное состояние элементов управления StateBrowse(Sender);
// Первоначально изменение записей запрещено cbCanEdit.Checked:= False;
// Запрет автоматического перехода в режим редактирования DataSourcel.AutoEdit:= False; // Формирование списка единиц измерения товара dbcbUnit.Items.Clear; dbcbUnit.Items.Add('шт.'); dbcbUnit.Items.Add('упак.'); dbcbUnit.Items.Add('кор.'); end;
procedure TForml.cbCanEditClick(Sender: TObject);
var bml: TBookmark;
begin
// Запоминание положения текущей записи
bml:= Tablel.GetBookmark;
// Отключение отображения изменений данных в визуальных компонентах
Tablel.DisableControls;
if not cbCanEdit.Checked then begin
Глава 7. Навигационный доступ к данным с помощью BDE
221
Tablel.Active:= False;
Tablel.Readonly:= True;
Tablel.Active:= True;
// Блокирование элементов, связанных с переходом
// в режимы изменения записей
btnEdit.Enabled:= False;
btnlnsert.Enabled:= False;
btnDelete.Enabled:= False;
end
else begin
Tablel.Active:= False;
Tablel.Readonly:= False;
Tablel.Active: = True;
// Разблокирование элементов, связанных с переходом
// в режимы изменения записей
btnEdit.Enabled:= True;
btnlnsert.Enabled:= True;
// Если набор данных пуст, то удаление записей запрещено
if Tablel.RecordCount > О
then btnDelete.Enabled:= True else btnDelete.Enabled:= False;
end; // Возврат к текущей записи
if Tablel.BookmarkValid(bml) then Tablel.GotoBookmark(bml); if Tablel.BookmarkValid(bml) then Tablel.FreeBookmark(bml); // Включение отображения изменений данных в визуальных компонентах Tablel.EnableControls;
end;
procedure TForml.TablelBeforeEdit(DataSet: TDataSet);
begin
// Запрос на подтверждение перехода в режим редактирования
if MessageDlg('Будете редактировать?',
mtConfirmation, [mbYes, mbNo], 0) о mrYes then Abort; end;
// Переход в режим редактирования
procedure TForml.btnEditClick(Sender: TObject);
begin
Tablel.Edit;
lblChangeKind.Font.Color:= clRed;
lblChangeKind.Caption:= 'РЕДАКТИРОВАНИЕ ЗАПИСИ';
222
Часть II. Технологии доступа к данным
StateChange(Sender);
if dbeName.CanFocus then dbeName.SetFocus; end;
procedure TForml.TablelBeforelnsert(DataSet: TDataSet);
begin
// Подтверждение перехода в режим вставки
if MessageDlg('Добавить запись?',
mtConfirmation, [mbYes, mbNo], 0) <> mrYes then Abort; end;
// Переход в режим вставки
procedure TForml.btnlnsertClick(Sender: TObject); begin
Tablel.Insert;
lblChangeKind.Font.Color:= clBlue; lblChangeKind.Caption:= 'ВСТАВКА ЗАПИСИ'; StateChange(Sender);
if dbeName.CanFocus then dbeName.SetFocus; end;
// Переход в режим просмотра удаляемой записи
procedure TForml.btnDeleteClick(Sender: TObject);
begin
// Подтверждение перехода в режим просмотра удаляемой записи
if MessageDlg('Удалить запись?',
mtConfirmation, [mbYes, mbNo], 0) о mrYes then Exit; lblChangeKind.Font.Color:= clRed; lblChangeKind.Caption:= 'УДАЛЕНИЕ ЗАПИСИ'; StateChange(Sender);
if btnChangeCancel.CanFocus then btnChangeCancel.SetFocus; end;
procedure TForml.TablelBeforePost(DataSet: TDataSet);
begin
// Проверка, задано ли значение для названия товара
if dbeName.Text = '' then begin
Beep;
MessageDlg('He задано название товара!', mtError, [mbOK], 0);
if dbeName.CanFocus then dbeName.SetFocus;
Abort;
end; // Проверка, задано ли значение цены товара if dbePrice.Text = '' then begin
Глава 7. Навигационный доступ к данным с помощью BDE
223
Веер;
MessageDlg('Не задана цена товара!', mtError, [mbOK], 0);
if dbePrice.CanFocus then dbePrice.SetFocus;
Abort;
end;
end;
procedure TForial.btnChangeOKClick(Sender: TObject);
begin
// Утверждение изменений в текущей записи (редактируемой или новой)
// или удаления текущей записи (просматриваемой)
if Tablel.State in [dsEdit, dslnsert]
then Tablel.Post
else if lblChangeKind.Caption = 'УДАЛЕНИЕ ЗАПИСИ' then Tablel.Delete; StateBrowse(Sender);
end;
procedure TFo rial.btnChangeCancelClick(Sender: TObject); begin
// Если набор данных находился в режиме просмотра (при удалении записи), // то никаких действий метод Cancel не выполняет Tablel.Cancel; StateBrowse(Sender); end;
// Закрытие формы
procedure TFonal.btnCloseClick(Sender: TObject); begin Close; end;
end.
В качестве набора данных используется компонент Tablel, записи которого отображает сетка DBGridl. Значения полей текущей записи отображаются в компонентах edtName, dbcbUnit, edtPrice и edtNote. Установка свойств, определяющих взаимные отношения компонентов, связанных с набором данных, выполнена при разработке приложения через Инспектор объектов.
Пользователь не может переводить набор данных в режимы изменения с помощью визуальных компонентов, т. к. свойство AutoEdit источника данных DataSourcel установлено в значение False.
Для перевода набора данных в режимы редактирования и вставки служат кнопки Изменить (btnEdit) и Добавить (btninsert) соответственно. Переход в эти режимы осуществляется вызовом методов Edit и insert, после чего название
224
Часть II. Технологии доступа к данным
режима отображается в надписи lblChangeKind, блокируются кнопки, связанные с переходом в режимы изменения, и разблокируются кнопки btnChangeOK и btnChangeCancel, позволяющие принять или отменить изменения, сделанные при редактировании или вставке записи. Для подтверждения или отмены изменений вызываются методы Post или Cancel, после чего кнопки подтверждения снова блокируются, а кнопки перехода в режимы изменения разблокируются. Изменение и задание значений полей выполняются с помощью компонентов edtName, dbcbUnit, edtPrice и edtNote.
При переходе в режимы, связанные с изменением записей, пользователю предлагается подтвердить свои действия, что программируется в обработчиках событий OnBeforeEdit и OnBeforelnsert.
Программирование удаления записей отличается от предыдущих действий по изменению набора данных, т. к. режим удаления у набора данных отсутствует. При нажатии кнопки Удалить (btnDelete) набор данных остается в режиме просмотра текущей записи. Однако надпись lblChangeKind указывает на переход в режим удаления, и если пользователь подтвердит удаление, нажав кнопку btnChangeOK, то для удаления текущей записи вызывается метод Delete. Таким образом, понятие "режим удаления" относится к форме, а не к набору данных. Запрос на подтверждение перехода в режим удаления кодируется в обработчике события нажатия кнопки btnDelete.
Флажок cbCanEdit с заголовком Редактирование разрешено включает и отключает возможность изменения в наборе данных. Управление этой возможностью осуществляется через свойство Readonly набора данных. Свойство переключается только при закрытом наборе данных. После нового открытия набора данных указатель текущей записи устанавливается на первую запись, поэтому перед переключением свойства Readonly положение указателя текущей записи запоминается с помощью закладки, а после переключения это положение восстанавливается. При разрешении изменений в наборе данных проверяется число его записей, и если набор данных пуст, то кнопка btnDelete блокируется.
Работа со связанными таблицами
Между отдельными таблицами БД может существовать связь, которая организуется через поля связи таблиц. Поля связи обязательно должны быть индексированными. Связь между таблицами определяет отношение подчиненности, при котором одна таблица является главной, а вторая — подчиненной. Обычно используется связь "один-ко-многим", когда одной записи в главной таблице может соответствовать несколько записей в подчиненной таблице. Такая связь также называется "мастер-детальный" (Master-Detail). После установления связи между таблицами при перемещении в главной таблице текущего указателя на какую-либо запись в подчиненной таблице автоматически становятся доступными записи, у которых значение поля связи равно значению поля связи текущей записи главной таблицы. Такой отбор записей подчиненной таблицы является своего рода фильтрацией.
Глава 7. Навигационный доступ к данным с помощью BDE