(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Проектирование приложений для работы с базами данных и создание универсальных форм-справочников

Источник: delphiplus
Сергей Хуторцев aka Linco

Сколько я себя помню программистом, в большинстве случаев писал программы, работающие с Базами данных (приложения БД). Причиной этому возможно послужил тот факт, что Базы Данных, как универсальные хранилища информации используются везде, начиная от хранения сведений о клиентах в крупных корпорациях и банках, кончая списками продукции в магазинах и документацией в любой бухгалтерии. И теперь хочется поделиться своими мыслями и наработками по поводу проектирования приложений БД. Собственно не надо быть семи пядей во лбу, чтобы сварганить простенькую программу с использованием Баз Данных, благо все средства для этого в Delphi есть, все-таки RAD. Многие ругают Delphi вообще и Delphi'стов в частности именно за это: "Дескать, любой ламер может взять нужный компонент, набросать на форму контролов, и получить готовое и РАБОТАЮЩЕЕ приложение". Хочется возразить, Delphi только среда, и что получится на выходе у программиста, зависит только от кривизны его рук, наличия знаний и желания. Мы "варганить" не будем, подойдем к процессу со всей ответственностью.

Итак, схема проектирования:

  1. Сбор информации. Вам необходимо знать все, что хотят пользователи, заказчик или Ваше руководство от этой системы. В цивилизованном обществе принято давать разработчику ТЗ, а также разделять программистов-аналитиков, проектирующих систему, от просто-программистов, пишущих код, и тем более различать администраторов и разработчиков Баз Данных. Однако нам до такого, как пешком до луны, поэтому программист должен быть "все-в-одном-флаконе", а вместо ТЗ мы получаем сомнительные руководства типа: "Хочу чтоб она все делала все, а я бы сидел в уютном кресле, чесал правую пятку, и отдавал мысленные приказания". Причем часто руководства письменные. Добро пожаловать новый пациент. На поиск приемлемого компромисса уходит определенное время. Также нелишним будет изучить конкурирующие системы, системы с похожей функциональностью, тут Google рулит.
  2. Выбор платформы. Включает в себя как выбор железа, в соответствии с планируемой нагрузкой на БД с учетом масштабируемости, так и выбор СУБД. Существует множество критериев, и для каждого они свои. Для кого-то важна цена/бесплатность продукта, для кого-то производительность. Однако нужно реально оценивать возможности СУБД и не использовать Oracle, если Ваша таблица за 2 года вырастет на 100000 записей. Или не использовать Access если…., ну вообще не использовать Добавьте к этому затраты на администрирование БД. Здесь главное иметь представление о том, что вы собираетесь сделать, и какой результат хотите получить, а также о возможностях различного железа и СУБД. Некоторые запущенные случаи требуют применения не клиент-сервер, а трехзвенки, что также надо учесть Для себя я давно выбрал Firebird, как мощную масштабируемую систему корпоративного уровня, удобную и легкую как по весу так и в эксплуатации/администрировании.
  3. Грамотное проектирование структуры БД, с максимальным вынесением логики работы на уровень сервера БД. Ибо зачем делать лишнюю работу на клиенте, если она лучше и быстрее сделается на сервере. Плюс унифицированность системы. Чем грамотней и продуманней начальная структура БД, тем меньше геморроя мы получаем на следующих этапах. Да и расходы на поддержку существенно уменьшаются. Здесь необходим опыт. Если программист разбирается в Oracle не факт, что он также качественно и сходу разберется, например, в Interbase/Firebird. У всех свои особенности работы, а знание особенностей приходит с опытом работы. И неважно каким образом осуществляется проектирование, с использованием технических средств типа ErWin и иже с ним, или на бумажке карандашиком, главное вcе равно в голове.
  4. Собственно проектирование и разработка интерфейса к БД. Ни один пользователь никогда не полезет в дебри утилит администрирования БД, а тем более не будет использовать SQL для получения или изменения каких-либо данных. Тут существует обратно-пропорциональная зависимость: чем универсальней программное средство, тем тяжелей оно в понимании и эксплуатации. Пользователю надо дать интерфейс, причем интерфейс довольно узкоспециализированный. Т.е. отрезать, разжевать и положить в рот необходимую ему информацию. Причем, в большинстве своем пользователи хотят чтобы все делалось при их минимальном участии, ну в крайнем случае согласны нажимать одну кнопку. Будучи главой компании, занимающейся разработкой такого программного обеспечения, или хотя бы начальником отдела кадров, я все-таки попытался бы совместить разработчика БД с программистом. Если в силу особой сложности проекта или иных технических причин это невозможно, то программист должен максимально тесно контактировать с разработчиком БД и ясно для себя представлять ее структуру. Не факт, что идеальная структура БД, которую с такой гордостью вчера представляли Вам, не заставит программиста рвать и метать, поскольку будет чрезвычайно тяжело реализовываться в программе, поддерживаться и масштабироваться. При проектировании интерфейса также можно выделить несколько этапов:
    • Примерно представить как все это должно выглядеть и какую функциональность выдавать пользователям. Исходя из этого, определиться с минимально необходимым набором компонентов для реализации. Из моего опыта работы, а также из общения с умными людьми были выделены несколько проектов: Поскольку работаю с Firebird, это FibPlus - лучшие компоненты доступа на сегодняшний момент, FastReport - лучший генератор отчетов, в качестве визуальных компонентов: таблица и часть контролов - EhLib, дерево VirtualTreeView, TBX toolbar для красоты, ну и JVCL, как бесплатный и огромный набор различных компонентов заменяющих и расширяющих VCL. Также полезно иметь наборы красивых картинок для кнопок, ибо ничто так не радует пользователей как красивые заставки и картинки.
    • Найти, скачать, купить данные компоненты.
    • Создать программу

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

Лично для меня в написании приложений БД есть несколько сложностей, решение которых я и хочу подсказать. Самое тяжелое это конечно рутина. Во всех пунктах нашей схемы есть элементы творчества: общение с пользователями-пациентами и с продвинутыми пользователями, поиск аналогов программы в сети, обдумывание различных функций и фишек будущей системы, анализ и подбор железа (чаще всего сводится к принципу "бери-что-есть, новое нини"), проектирование структуры БД и описание бизнес-логики, и наконец, само программирование. Рутина - это минимальный необходимый набор действий для обеспечения работы приложения, т.е. до получения какого-либо результата. Когда знаешь в точности, как и что ты должен сделать, и все это проще простого, но приходится делать это десятки и сотни раз. Там, где отсутствует творчество. Боитесь?, я нет, ибо сегодня наш рассказ о том, как побороть одно из ее проявлений, а именно о проектировании универсальных форм-справочников.

Любая БД имеет много объектов внутри, таблиц, триггеров, процедур и другого барахла, о котором рядовому пользователю знать вовсе не обязательно. Исключим клинические случаи, которые недостойны гордого звания Базы данных, когда студент-недоучка делает на Access базу данных, содержащую одну таблицу со списком учащихся группы. Почти всегда в нормальной БД есть группа вспомогательных таблиц, созданных, например, для поддержания целостности данных, или для более полного охвата предметной области. Единственной функцией таких таблиц может быть просто подстановка значений в основную таблицу. Простой пример, телефонный справочник, в простейшем тривиальном случае он представляет собой таблицу соответствия город-абонент-номер. Поскольку один город соответствует множеству абонентов, можно и нужно вынести список городов в отдельную таблицу, таблицу-справочник, а в основной таблице оставить просто цифру которая будет указывать на город, и связать их по ForeignKey. Таким образом убиваем сразу десять зайцев ракетой: получаем нормализацию БД, экономия места в основной таблице(вместо города пишем цифру), при добавлении нового телефонного номера он выбирается из списка, одинаковое написание всех городов (к сожалению многие пользователи страдают хронической неграмотностью, да еще и требуют чтобы при поиске/выборке не было упущено ни одного значения, а программист выкручивайся, описывай методы нечеткой логики поиска, да еще в SQL переводи, чтобы находило и Москва и Масква, Мсква). Не нужно приписывать мне столь гениальное решение, это один из стандартных принципов проектирования БД. К городу можно добавить ряд дополнительных полей, например, население города, чтоб знать каков процент абонентов, код города и т.п. Хорошо если таблица-справочник одна или несколько, а если довольно много? В построении интерфейса к ней нет ничего особенно сложного, обычно такие таблицы редко редактируются или дополняются. Он сводится к таблице, как вариант группе DataAware контролов, средствам навигации и управления данными, поиску, выборке, и составлению отчетов. Само по себе все это сложности не представляет, однако повторенное двадцать раз начинает бесить. Во многих случаях даже визуальный ряд интерфейсов к разным таблицам полностью идентичен. Программисты, как известно, народ очень ленивый, разработка программы идет постепенно, делая интерфейс к одной таблице, еще не представляют как будет выглядеть интерфейс к другой, а когда делают второй - видят, что все идет идентично, но писать что-то универсальное лень, во-первых, copy-paste легче, во-вторых, придется исправлять и первый, рабочий уже интерфейс. На третьем и последующих интерфейсах приходит та самая рутина. И вот однажды, победив в героическом сражении лень, да и таблиц-справочников было много, решил написать универсальное решение, на основе которого можно было бы легко и просто добавлять интерфейсы к новым таблицам в приложение. Сразу оговорюсь, я привожу лишь одно из возможных решений, для очень ленивых программистов, кто по каким-либо причинам еще не добрался или не сподобился до решения проблемы. Наверняка у многих, кто работает с БД, подобные решения есть. Сначала была попытка пойти по пути наименьшего сопротивления, Delphi позволяет создавать полного наследника формы, т.е. не только ее свойств и методов, но также и визуального ряда. Но решение оказалось абсолютно немасштабируемым, убрать или добавить что-либо в наследник оказалось практически невозможно. И было решено оставить внешний вид интерфейса на откуп программисту и сосредоточиться на кодировании.

Итак, что мы имеем с гуся? Необходима универсальная форма-интерфейс к определенной таблице БД, с возможностями навигации, добавления, изменения, удаления записей, выборки/поиска нужных записей, составления по ним отчетов, определенного управления отчетами, с реакцией на изменения данных. Реализовывать будем в три этапа (каждый этап в отдельном модуле, чтобы эффективней использовать решения по отдельности): 1. Поскольку это форма, наследуем от Tform, а также вынесем в этот модуль все, что касается ее внешнего вида и поведения. В нашем случае форма должна отображаться как отдельно, так и внутри Twincontrol контейнера. Для этого используем CreateParams. Мы хотим добиться от формы определенной функциональности, поэтому воспользоваться Tform.CreateParented не сможем.

unit childform;

interface

uses windows,forms,classes,controls;

type
Tchildform = class(TForm)
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
   private
     Faschild:boolean;
     Fparent:Twincontrol;
   protected
     procedure CreateParams(var params:Tcreateparams); override;
     procedure Loaded; override;
   public
     constructor Create(Aowner:Tcomponent); reintroduce; overload;
     constructor Create(Aowner:Tcomponent; Aparent:Twincontrol);reintroduce; overload;
   end;

implementation

{ Tchildform }

constructor Tchildform.Create(Aowner: Tcomponent);
begin
   Faschild := false;
   inherited Create(Aowner);
end;

constructor Tchildform.Create(Aowner: Tcomponent; Aparent: Twincontrol);
begin
   Faschild:=true;
   Fparent:=Aparent;
   inherited Create(Aowner);
end;

procedure Tchildform.CreateParams(var params: Tcreateparams);
begin
   inherited CreateParams(params);
   if Faschild then
     params.Style:=params.Style or WS_CHILD;
end;

procedure Tchildform.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   action:=cafree;
end;

procedure Tchildform.Loaded;
begin
   //параметры отображения формы
   inherited;
   if Faschild then
     begin
       align:=alclient;
       borderstyle:=bsnone;
       bordericons:=[];
       Parent:=Fparent;
       Position:=poDefault;
     end
   else
     begin
       position:=pomainformcenter;
       borderstyle:=bsdialog;
     end;
end;

end.

2. Наследуем от нашей формы. Во-первых, привяжем нашу форму к определенному набору данных, таблице. Во-вторых, реализуем все необходимые нам общие методы для работы с данными. В основном эти методы итак присутствуют в любом наследнике Tdataset, однако в ряде случаев нам необходимо провести некоторые подготовительные мероприятия перед их непосредственным применением. Например, уточнить у пользователя действительно ли он хочет удалить записи. Одновременно с этим мы получаем возможность централизованного управления и выполнения дополнительных действий. Для моего случая метод Delete вообще был заменен собственной формой, демонстрирующей пользователю все зависимые по FK CASCADE DELETE записи в других таблицах (они также были бы удалены, Firebird). В-третьих, реализуем методы составления и управления отчетами. Также сделаем нашу форму частично Dataaware, чтобы иметь возможность реагировать на события, происходящие с данными. Например, можем управлять доступностью кнопок сохранения записи, т.е. делать ее активной только в случае изменения данных. Методы поиска и фильтрации добавлять сюда не стал, поскольку в большинстве своем там очень важна визуальная реализация режима поиска. Например, в ehgrid выделение и фильтрация реализованы отлично. Так же, как пример, можно посмотреть реализацию организации фильтрации в DeveloperExpress grid. Там реализована древовидная структура фильтров. Для поддержки выборки части записей введен Bookmarklist:Tstringlist, содержащий список закладок выделенных записей, по такому принципу работает ehgrid, и реализация метода FastReport beforeprint обеспечивающая вывод только записей списка.

unit dbforms;

interface

uses windows,forms,controls,classes,db, dialogs,
   sysutils, childform,
   frxDesgn, frxDCtrl, frxClass, frxDBSet,
   frxExportHTML,frxExportRTF,frxExportPDF;

type

   TExportFilter=(EF_RTF, EF_PDF, EF_HTML );

   TMyDataLink=class(TDatalink)
   private
     Fediting:boolean;
     Fmodified:boolean;
     FonEditingchange:TnotifyEvent;
     procedure setediting(value:boolean);
   public
     procedure EditingChanged; override;
     property OnEditingChange: TNotifyEvent read FOnEditingChange write FOnEditingChange;
   end;

   TcustomDBForm =class(Tchildform)
   private
     FreportCreated:boolean;
     Fdatachange:Tnotifyevent;
     Fdatalink:Tmydatalink;
     Fdatasource:Tdatasource;
     Fbookmarklist:Tstringlist;
     Fcursor:Tbookmark;
     Fopendlg:Topendialog;
     FfrxDS:TfrxDBdataset;
     FfrxReport:TfrxReport;
     FfrxRTFExport: TfrxRTFExport;
     FfrxHTMLExport: TfrxHTMLExport;
     FfrxPDFExport: TfrxPDFExport;
     procedure frx_beforeprint(Sender: TfrxReportComponent);
     procedure DoOndatachange(Sender:Tobject);
   protected
     procedure savecursor;
     procedure gotocursor;
     procedure first;
     procedure last;
     procedure add;
     procedure delete;
     function applydata:boolean;
     procedure canceldata;
     procedure initfastreport;
     procedure freefastreport;
     function GetReportInitialDir:string;
     function GetReportName:string;
     procedure createreport(bookmarklist:Tstringlist=nil);
     procedure DesignReport(bookmarklist:Tstringlist=nil);
     procedure ExportReport(Exportfilename:string=''; filter:TExportfilter=EF_RTF);
   public
     constructor Create(Aowner:Tcomponent; DS:Tdatasource); reintroduce; overload;
     constructor Create(Aowner:Tcomponent; Aparent:Twincontrol;DS:Tdatasource); reintroduce; overload;
     destructor Destroy; override;
   published
     property OnDataChange:Tnotifyevent read Fdatachange write Fdatachange;
     property DataSource:Tdatasource read Fdatasource write Fdatasource;
     property ReportCreated:boolean read Freportcreated;
     property Report:Tfrxreport read Ffrxreport;
   end;

implementation

{ TcustomDBForm }

procedure TcustomDBForm.add;
begin
   //переписка всех этих простых методов управления Dataset дает нам возможность осуществления
   //определенного комплекса действий, например управления и контроля за транзакциями в Firebird
   // это мы увидим в реализации наследника этого класса
   Fdatasource.DataSet.Append;
end;

procedure TcustomDBForm.delete;
begin
   //диалог удаления в более сложных случаях можно вставить
   //сложный диалог, например с анализом зависимостей foreign keys
   if messagedlg('Удалить запись?', mtWarning,[mbYes,MbNo],0)=mrYes
     then Fdatasource.dataset.Delete;
end;

procedure TcustomDBForm.first;
begin
   Fdatasource.DataSet.First;
end;

procedure TcustomDBForm.last;
begin
   Fdatasource.DataSet.Last;
end;

procedure TcustomDBForm.savecursor;
begin
   //перед любыми действиями требующими движения курсора таблицы
   //(например составление отчета) сохраняем наше местоположение
   Fcursor:=Fdatasource.DataSet.GetBookmark;
end;

procedure TcustomDBForm.gotocursor;
begin
   // восстанавливаем положение курсора
   with Fdatasource.dataset do
     if BookmarkValid(Fcursor)
       then GotoBookmark(Fcursor);
end;

function TcustomDBForm.applydata: boolean;
begin
   if Fdatasource.DataSet.State in [dsedit,dsinsert] then
     try
       Fdatasource.DataSet.Post;
       result:=true;
     except
       result:=false;
     end;
end;

procedure TcustomDBForm.canceldata;
begin
   Fdatasource.DataSet.cancel;
end;

constructor TcustomDBForm.Create(Aowner: Tcomponent; DS: Tdatasource);
begin
   //заполняем и инициализируем необходимые поля
   if DS=nil then
     raise Exception.Create('Невозможно создать форму без привязки к данным.');

   Fdatasource:=DS;
   //здесь создаем Tmydatalink для реализации событий изменения данных таблицы
   Fdatalink:=Tmydatalink.create;
   Fdatalink.OnEditingChange:=Doondatachange;
   Fdatalink.DataSource:=Fdatasource;

   initfastreport;
   inherited Create(Aowner);
end;

constructor TcustomDBForm.Create(Aowner: Tcomponent; Aparent: Twincontrol; DS: Tdatasource);
begin
   if DS=nil then
     raise Exception.Create('Невозможно создать форму без привязки к данным.');

   Fdatasource:=DS;
   Fdatalink:=Tmydatalink.create;
   Fdatalink.OnEditingChange:=DoOndatachange;
   Fdatalink.DataSource:=Fdatasource;

   initfastreport;
   inherited Create(Aowner,Aparent);
end;

destructor TcustomDBForm.Destroy;
begin
   Fdatalink.Free;
   freefastreport;
   inherited;
end;

procedure TcustomDBForm.DoOndatachange(Sender: Tobject);
begin
   //событие изменения данных
   if assigned(Fdatachange) then
     Fdatachange(Self);
end;

procedure TcustomDBForm.ExportReport(Exportfilename: string; filter: TExportfilter);
var    FN:string;
begin
   //прямой экспорт отчета

   //проверяем создан отчет или нет
   if not(reportcreated) then
     createreport; //если нет создаем

   if exportfilename<>'' then
     begin
       if extractfilename(exportfilename)=exportfilename
         then FN:=getreportinitialdir+exportfilename
         else Fn:=exportfilename;
     end
   else
     with Tsavedialog.Create(self) do
       try
         initialdir:=getreportinitialdir;
         if execute then FN:=filename;
       finally
         free;
       end;

   if fn='' then exit;
   //экспортируем в зависимости от фильтра экспорта
   case filter of
     EF_RTF: begin
       FfrxRTFExport.FileName:=FN;
       FfrxRTFExport.defaultpath:=extractfilepath(FN);
       Ffrxreport.Export(Ffrxrtfexport);
     end;
     EF_PDF: begin
       FfrxPDFExport.FileName:=FN;
       FfrxPDFExport.defaultpath:=extractfilepath(FN);
       Ffrxreport.Export(FfrxPDFexport);
     end;
     EF_HTML: begin
       FfrxHTMLexport.filename:=FN;
       FfrxHTMLExport.defaultpath:=extractfilepath(FN);
       Ffrxreport.Export(FfrxHTMLexport);
     end;
   end;

end;

procedure TcustomDBForm.createreport(bookmarklist: Tstringlist);
begin
   //создаем отчет здесь знатоки FR могут меня поправить
   //буду только рад, однако приведенная схема работает на ура
   //опция: выбор только существующих файлов
   Fopendlg.Options:=Fopendlg.Options+[offilemustexist];

   if Fopendlg.Execute then
   begin
     savecursor;
     //загружаем шаблон
     Ffrxreport.LoadFromFile(Fopendlg.FileName);
     //проверяем соответствует ли создаваемый отчет Dataset'у
     //для этого при создании отчета пишем в Dataset.name
     if Ffrxreport.ReportOptions.Name<>getreportname
       then
         if messagedlg('Внимание! Данный отчет не предназначен для этих данных. Все равно открыть?',mtwarning,[mbYes,mbNo],0)=mrNo
           then exit;
     //Если открыли, значит он уже для других данных ставим метку
     Ffrxreport.ReportOptions.Name:=getreportname;
     //добавляем dataset в список доступных в отчете
     if Ffrxreport.Report.DataSets.Find(FfrxDS)=nil
       then Ffrxreport.Report.DataSets.Add(FfrxDS);
     Ffrxreport.Report.DataSet:=nil;

     //bookmarklist - список выделенных записей для отчета
     //в ehgrid такой есть по умолчанию, в иных случаях можете создать и заполнить его сами
     if bookmarklist<>nil
       then Fbookmarklist.Assign(bookmarklist)
       else Fbookmarklist.Clear;

     Freportcreated:=true;
     Ffrxreport.ShowReport(true);
     gotocursor;
   end;
end;

procedure TcustomDBForm.DesignReport(bookmarklist: Tstringlist);
begin
   //создание нового отчета
   //включаем возможность создания нового имени файла
   //остальное по аналогии
   Fopendlg.Options:=Fopendlg.Options-[offilemustexist];
   if Fopendlg.Execute then
   begin
     savecursor;      if fileexists(Fopendlg.FileName) then
       begin
         Ffrxreport.LoadFromFile(Fopendlg.FileName);
         if Ffrxreport.ReportOptions.Name<>getreportname
           then
             if messagedlg('Внимание! Данный отчет не предназначен для этих данных. Все равно открыть?',mtwarning,[mbYes,mbNo],0)=mrNo
               then exit;
       end
     else
       begin
         Ffrxreport.ReportOptions.Name:=getreportname;
         if Ffrxreport.Report.DataSets.Find(FfrxDS)=nil
           then Ffrxreport.Report.DataSets.Add(FfrxDS);

         Ffrxreport.SaveToFile(Fopendlg.filename);
         Ffrxreport.loadfromfile(Fopendlg.filename);
     end;

   Ffrxreport.Report.DataSet:=nil;

   if bookmarklist<>nil then
     Fbookmarklist.Assign(bookmarklist)
     else Fbookmarklist.Clear;

   Ffrxreport.DesignReport;
   gotocursor;
end;
end;

procedure TcustomDBForm.initfastreport;
begin
   // инициализация отчета
   Fopendlg:=Topendialog.Create(self);
   Fopendlg.Filter:='Файлы отчета(*.fr3)/*.fr3';
   Fopendlg.DefaultExt:='fr3';
   Fopendlg.InitialDir:=GetReportInitialDir;

   Fbookmarklist:=Tstringlist.Create;

   Ffrxreport:=Tfrxreport.Create(self);
   Ffrxreport.OnBeforePrint:=frx_beforeprint;
   Ffrxreport.EngineOptions.DoublePass:=true;

   //userdataset/dbdataset
   FfrxDS:=TfrxDBdataset.Create(self);
   FfrxDS.DataSource:=Fdatasource;
   //имя локального датасета отчета
   FfrxDS.Name:='LocalfrxDS';
   FfrxDS.UserName:='LocalFRXDS';

   //создаем экспорты
   Ffrxrtfexport:=Tfrxrtfexport.Create(self);
   Ffrxhtmlexport:=Tfrxhtmlexport.Create(self);
   Ffrxpdfexport:=Tfrxpdfexport.Create(self);

   Fbookmarklist:=Tstringlist.create;
end;

procedure TcustomDBForm.freefastreport;
begin
   Fbookmarklist.Free;
   Ffrxrtfexport.Free;
   Ffrxhtmlexport.Free;
   Ffrxpdfexport.Free;
   Fopendlg.Free;
   Ffrxreport.Free;
   FfrxDS.Free;
end;

procedure TcustomDBForm.frx_beforeprint(Sender: TfrxReportComponent);
   function findbookmark(BM:string):boolean;
   begin
     result:=Fbookmarklist.IndexOf(BM)<>-1;
   end;
   procedure SHobjects(show:boolean);
   var i:integer;
   begin
     for i:=0 to Tfrxdataband(sender).Objects.count-1 do
       Tfrxcomponent(Tfrxdataband(sender).Objects.Items[i]).visible:=show;
   end;
  //mainbody====
  //вариант фильтрации отчета по содержимому
  //при генерации отчета все датазависимые компоненты не входящие в список
  //bookmarklist не отображаются
begin
   if Fbookmarklist.count>0 then
     if (sender is Tfrxdataband)
       or (sender is Tfrxgroupheader)
       or (sender is Tfrxgroupfooter)
         then sender.Visible:=findbookmark(Fdatasource.dataset.fieldbyname('ID').AsString);
end;

function TcustomDBForm.GetReportInitialDir: string;
begin
   //функция определяет директорию в которую будут писаться отчеты
   result:=extractfilepath(paramstr(0));
end;

function TcustomDBForm.GetReportName: string;
begin
   result:=Fdatasource.DataSet.Name;
end;

{ TMyDataLink }

procedure TMyDataLink.EditingChanged;
begin
   SetEditing(inherited Editing);
end;

procedure TMyDataLink.setediting(value: boolean);
begin
   if FEditing <> Value then
     begin
       FEditing := Value;
       FModified := False;
       if Assigned(FOnEditingChange) then FOnEditingChange(Self);
     end;
end;

end.

3. Последний модуль призван привязать предыдущий модуль к конкретным компонентам доступа к БД и дополнить его функциональность. В нашем случае компоненты FibPlus, а изменить нам надо методы сохранения и отмены изменения данных, с учетом транзакционной модели.

unit fibdbform;

interface

uses dbforms, pfibdataset,classes,db,controls,sysutils;

type
   TFIBDBForm=class(TcustomDBForm)
   protected
     function applydata:boolean;
     procedure canceldata;
   public
     constructor Create(Aowner:Tcomponent; DS:Tdatasource); reintroduce; overload;
     constructor Create(Aowner:Tcomponent; Aparent:Twincontrol;DS:Tdatasource); reintroduce; overload;
   end;

implementation

{ TFIBDBForm }

function TFIBDBForm.applydata: boolean;
begin
   with (datasource.dataset as Tpfibdataset) do
     try
       inherited applydata;
       if UpdateTransaction.InTransaction then updateTransaction.Commit;
       result:=true;
     except
       updatetransaction.Rollback;
       result:=false;
     end;
end;

procedure TFIBDBForm.canceldata;
begin
   with (datasource.dataset as Tpfibdataset)do
     begin
     inherited canceldata;
     if UpdateTransaction.InTransaction then UpdateTransaction.Rollback;
     fullrefresh;
   end;
end;

constructor TFIBDBForm.Create(Aowner: Tcomponent; DS: Tdatasource);
begin
   if not (DS.DataSet is Tpfibdataset) then
     raise Exception.create('Данный класс может использоваться только с FIB+ Dataset');
   inherited Create(Aowner,DS);
end;

constructor TFIBDBForm.Create(Aowner: Tcomponent; Aparent: Twincontrol; DS: Tdatasource);
begin
   if not (DS.DataSet is Tpfibdataset) then
     raise Exception.create('Данный класс может использоваться только с FIB+ Dataset');
   inherited Create(Aowner,Aparent,DS);
end;

end.

Теперь процесс создания новой формы сводится к ее визуальному проектированию и наследованию ее не от Tform, а от одного из наших классов, и прописыванию необходимых методов навигации и управления данными. Повторюсь, это решение приведено как основа. Кто-то добавит свои функции и методы, кто-то, хорошо знакомый с FastReport, кое-что исправит. Но решение хорошее и с успехом работает во многих моих проектах.

Ссылки по теме

Файлы для загрузки


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 13.05.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Enterprise Connectors (1 Year term)
Delphi Professional Named User
Stimulsoft Reports Server Team 10 users
ABBYY Lingvo x6 Многоязычная Домашняя версия, электронный ключ
Zend Studio Commercial License 1 Year Free Upgrades
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
СУБД Oracle "с нуля"
Работа в Windows и новости компании Microsoft
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100