Продолжение статьи “Давайте дружить таблицами”
Три самые популярные кнопки в приложениях, создаваемых в среде разработки My Visual Dtabase, – это кнопки редактирования табличных данных: добавление, изменение и удаление записи. Обычно они находятся рядом с каждой таблицей, которую пользователь намеревается редактировать. Их функциональность определяется визуальными настройками, а создание занимает несколько секунд. Но в нашем приложении кнопок редактирования всего три, поэтому они будут не обычными, а “богатырскими”. Такие кнопки будут легко справляться со всеми таблицами приложения.

Располагаются эти кнопки на главной форме (1) и имеют традиционные названия и картинки (2). Но на этом их сходство со стандартными кнопками заканчивается, так как вся их сила заключена в скриптах. А сила скриптов – в соглашениях, которые необходимо соблюдать при наименовании форм приложения.
Соглашения
Соглашения – это правила, которые устанавливает разработчик для самого себя. Это очень полезная практика, которая позволяет писать эффективный код. Подробно об этой технике и различных примерах её использования написано в моей книге “Визуальное программирование”, глава 12. Выгодное соглашение.
Ниже представлены соглашения, которые помогли в составлении эффективного кода для кнопок редактирования.
Названия для форм табличного представления
Название формы должно состоять из трехсимвольного префикса frm, за которым следует название таблицы, отображение которой осуществляется данной формой. После названия таблицы может идти суффикс. Суффикс отделяется от названия таблицы символом подчеркивания и состоит из произвольного числа символов. Благодаря суффиксу можно создавать несколько форм табличного представления для одной таблицы.
frm<название таблицы>[_<суффикс>]
Например, в нашем приложении в таблице classType хранятся различные сведения, отображаемые различными формами: данные о типах (frmClassType_TypeList) и классах, а классы представлены как в виде списка (frmClassType_List), так и в виде дерева (frmClassType_Tree).
Названия для форм редактирования
Название формы редактирования состоит из префикса efm и названия таблицы, для редактирования которой предназначена данная форма.
efm<название таблицы>
Скрипты
Добавление новой записи использует метод формы NewRecord(). В качестве первого параметра в него передаётся название таблицы.
Примечание. Второй параметр используется при создании дочерних записей в древовидной структуре, но требует наличия на форме редактирования компонента с определенными настройками. В нашем приложении будет использована другая технология добавления в дерево.
procedure frmMain_btnNew_OnClick (Sender: TObject; var Cancel: boolean); // добавлдение var tmpID: integer; tmpTableName: string; tmpEditForm:TAForm; tmpForm:TAForm; begin // если раскрыта вкладка с примером, то создаём пример if frmMain.pgcMain.ActivePage = frmMain.tshExample then begin efmExample.NewRecord('example'); end else // если раскрыта вкладка навигатора и пользователь выделил таблицу, то создаем запись в таблице if (frmMain.pgcMain.ActivePage = frmMain.tshNavigation) and (ActiveGrid <> nil) then begin // определяем имя редактируемой таблицы, испльзуя соглашение по наименованию форм // <frm><имя таблицы>[_<суффикс>] tmpTableName := Grid_GetTableName(ActiveGrid); // используем соглашение для формы редактирования: // форма редектровани называется по шаблону: // <efm><название таблицы> tmpEditForm := App_GetFormByName('efm'+tmpTableName); CForm(ActiveGrid,tmpForm); // некторые таблицы зависят от главных, поэтому нужна проверка if not Form_CheckMasterForm(tmpForm.Name) then ShowHint(ActiveGrid,'Выберите запись в главной таблице') else // открываем форму редактирования tmpEditForm.NewRecord(tmpTableName); end; end;
Процедура редактирования тоже весьма элегантна, и основана на методе формы ShowRecord(), в который в качестве параметров передаются название таблицы и номер редактируемой записи.
procedure frmMain_btnEdit_OnClick (Sender: TObject; var Cancel: boolean); // редактирование var tmpID: integer; tmpTableName: string; tmpEditForm:TAForm; begin if frmMain.pgcMain.ActivePage = frmMain.tshExample then begin if IDExample <> -1 then begin efmExample.ShowRecord('example',IDExample); LoadExample; end; end else if frmMain.pgcMain.ActivePage = frmMain.tshNavigation then begin tmpID := ActiveGrid.dbItemID; tmpTableName := Grid_GetTableName(ActiveGrid); if tmpID = -1 then ShowHint(ActiveGrid,'Выберите запись') else begin // используем соглашение: // форма редектровани называется по шаблону: // <efm><название таблицы> tmpEditForm := App_GetFormByName('efm'+tmpTableName); if tmpEditForm<>nil then tmpEditForm.ShowRecord(tmpTableName, tmpID ); end; end; end;
Удаление выглядит аналогично редактированию, но использует в своей работе SQL команду DELETE:
procedure frmMain_btnDelete_OnClick (Sender: TObject; var Cancel: boolean); var tmpID: string; tmpTableName: string; begin if frmMain.pgcMain.ActivePage = frmMain.tshExample then begin if IDExample <> -1 then if MessageBox( 'Удалить пример?', 'Удаление', MB_YESNO + MB_ICONWARNING ) = mrYes then begin SQLExecute('delete from example where id = '+IntToStr(IDExample) ); UpdateDatabase('example'); end; end else if (frmMain.pgcMain.ActivePage = frmMain.tshNavigation) and (ActiveGrid<>nil) then begin tmpID := IntToStr(ActiveGrid.dbItemID); tmpTableName := Grid_GetTableName(ActiveGrid); if tmpID = '-1' then ShowHint(ActiveGrid,'Выберите запись') else if MessageBox( 'Удалить запись?', 'Удаление', MB_YESNO + MB_ICONWARNING ) = mrYes then begin SQLExecute('delete from '+tmpTableName+' where id = '+tmpID ); UpdateDatabase(tmpTableName); end; end; end;
Вышеуказанные процедуры используют ключевую функцию Grid_GetTableName(). Её реализация выглядит очень лаконично.
function Grid_GetTableName( AGrid: TdbStringGridEx ):string; // возвращает имя таблицы var tmpForm:TAForm; begin // используем соглашение по наименованию формы: // <frm><имя таблицы>[_<суффикс>] // то есть может быть несколько форм с табличным представлением для одной таблицы, // они должны различаться суффиксом CForm(AGrid,tmpForm); Result := DeleteSuffix(DeleteClassName(tmpForm.Name)); end;
Примечание. При попытке создать аналогичную функцию без использования соглашений о наименовании, я обнаружил ошибку в работе MVDB, а именно: у компонента TdbTreeView в свойстве dbGeneralTable отсутствует название таблицы, которое он отображает. Эта ошибка помешала создать универсальную функцию, которая бы работала как для таблиц, так и для дерева.
А вот ещё одна используемая в процедурах “богатырских” кнопок полезная функция, которая возвращает форму приложения по имени:
function App_GetFormByName(AName: string): TAForm; // получение формы по названию var tmpForm: TAForm; i: integer; begin AName := UpperCase(AName); for i := 0 to Screen.FormCount - 1 do begin Result := TAForm(Screen.Forms[i]); if UpperCase(Result.Name) = AName then exit; end; Result := nil; end;
Продолжение “Гений кроется в деталях”