1919
Вопрос: С чего начать и как практически использовать объектный подход при программировании в Microsoft Visual FoxPro, после того как Мы определились со структурой прикладных данных?
Ответ: Прежде всего, на мой взгляд, следует упомянуть следующие моменты относительно данных:
Необходимо пройтись по всем таблицам и для каждого из полей придать значение заголовку поля (Caption) и дать определение поля (Field comment) на закладке Fields, если Вы этого ещё не сделали. При этом следует также указать русские названия таблиц (псевдонимы таблиц) (Name) и дать определения таблицам (Table comment) на закладке Table. Следует отметить, что хоть версия Visual FoxPro 5.0 и позволяет вводить многословные псевдонимы таблиц, лучше все же иметь однословные псевдонимы, полученные соединением слов посредством символа ‘_’. Относительно названий полей, следует сказать, что хоть для таблиц, помещённых в базу данных Visual FoxPro и имеется ограничение в 128 символов, рано или поздно может возникнуть необходимость переноса данных на SQL Server, где, конечно же, имеются свои ограничения (например, для MS SQL Server 6.5 длина поля 30 символов, к тому же имена полей, конечно же используются при программировании на сервере [Transact-SQL], где первый символ ‘@‘ обозначает рабочую локальную переменную, уменьшая тем самым фактическую длину поля до 29 символов), более того, название индекса в Visual FoxPro 5.0 составляет всего 10 символов, остальные будут отброшены, если вы создадите индекс, базируясь на поле.
Далее проследите, пожалуйста, за тем, чтобы для всех primary keys были определены функции, возвращающие новые значения ключей (default value), это же самое следует проделать и для всех значений ключей foreign keys в каждой из таблиц. Код соответствующих функций должен быть помещён в базу данных в виде сохранённых процедур. Поверьте, что это не проблема конечного приложения, пытаться корректно генерировать новые значения ключей, это непременно должно быть свойством данных, а не конечного клиента. Пример простой сохранённой процедуры, позволяющей получать новые значения ключей для таблиц, Вы можете найти в Vfp\Samples\Tastrade\Data\Testdata.dbc - FUNCTION NewID(). Относительно значений для foreign keys, думаю, что можно придерживаться следующего правила: в любой таблице, являющейся источником данных для других таблиц, первая запись, с начальным значением ключа primary key, - является значением по умолчанию для всех других таблиц, т.е. именно это значение следует определить в качестве свойства default value для всех полей foreign keys.
Пройдите ещё раз по всем полям ваших таблиц и посмотрите критически: нельзя ли на этапе ввода, используя маску поля (Input mask) отсечь чисто формальные ошибки ввода; определите также формат отображения данных (Format); а если есть необходимость, создайте функции контроля данных уровня поля (Rule) и определите текст сообщения (Message), выдаваемого при нарушении этих правил на закладке Fields. Если имеется необходимость проверки условия между значениями разных полей одной записи таблицы, то непременно следует воспользоваться функцией контроля данных уровня таблицы (Rule) и определите текст сообщения (Message), выдаваемого при нарушении этого правила на закладке Table.
Наконец, зайдите в среду редактирования базы данных (Modify database ... exclusive) и, вызвав диалог контроля целостности данных (Database/Edit Referential Integrity…), установите необходимые опции контроля (Restrict | Cascade | Ignore) на события изменений данных (Update, Delete, Insert) между всеми связанными парами таблиц. Если нужные Вам пары таблиц не появились в диалоге контроля целостности данных, то зайдите в редактирование соответствующих таблиц и установите им primary key, после чего определите межтабличные связи, т.е. foreign key. Здесь уместно ещё раз повторить, что, используя функцию TABLEUPDATE() при буферизованном источнике данных, следует всегда анализировать возвращаемое значение этой функции, и если оно будет иметь значение .F. необходимо дополнительно использовать функцию AERROR() для уточнения причин отказа системы в сохранении данных и известить об этом пользователя, посредством выдачи соответствующего предупреждения.
Далее, для создания собственно приложения на начальном этапе Вы можете воспользоваться, по крайней мере, двумя заготовками: классом cApplication, создаваемым Application Wizard (Tools \Wizards\All…) или классом tastrade в примере Tasmanian Traders Sample (Vfp\Samples \Tastrade\Libs\ Main.vcx), унаследованном от Application в Vfp\Samples\Tastrade\Libs\Tsgen.vcx. В первом случае Вам, скорее всего, потребуется, наследуясь от cApplication, создать собственный класс приложения, добавляя в него нужную функциональности из классов tastrade и Application, например поддержку Toolbar. Конечно же, Вы можете написать всё с нуля самостоятельно, однако, скорее всего при этом Вы обнаружите, что пишете то, что уже написано в вышеупомянутых классах.
Наконец, для обеспечения “стандартной функциональности” работы с формой, лучший вариант заключается в использовании класса tsBaseForm и наследованного от него tsMaintForm из Vfp\Samples\Tastrade\Libs\Tsbase.vcx. Если Вам принципиально не нравится, как работают формы в примере Tasmanian Traders Sample, то, конечно же, Вы можете не придерживаться моих советов и писать всё с нуля самостоятельно. Однако поверьте мне, что Вам придётся написать те же самые методы, и очень сомневаюсь, что они будут существенно отличаться по содержанию, когда Вы заставите их работать. Конечно же, указанные выше классы обладают лишь минимально необходимой функциональностью, и Вам потребуется их развивать и совершенствовать по мере необходимости. Если же Вы не планируете использовать объектно-ориентированный стиль программирования и клиент-серверную технологию для работы с данными, то видимо, Вам серьёзно нужно подумать об использовании какого-нибудь другого средства программирования баз данных для написания клиентских приложений.
Вопрос: Как в экземпляре класса tsBaseForm из Vfp\Samples\Tastrade\Libs\Tsbase.vcx предотвратить использование Toolbar, поскольку в данном конкретном случае формы у меня нет необходимость в функциях навигации между записями?
Ответ: Достаточно изменить значение свойства cToolbar этого класса с названия класса tsToolbar (действующего по умолчанию) на пустую строку. Если при этом у Вас всё же остаётся необходимость в использовании других, отличных от навигационных методов, как-то: AddNew, Save, Restore и т.д., то Вам потребуется альтернативные способы вызова этих методов, например, посредством добавления в форму командных кнопок. Далее, в целом ряде методов, например AddNew, имеется безусловное использование свойства Enabled кнопки cmdNew, поэтому следует обеспечить условное обращения к этому свойству: IF TYPE(‘oApp’) = ‘O’ AND !EMPTY(ThisForm.cToolbal), произведя соответствующую корректировку во всех аналогичных случаях в методах данного класса.
Вопрос: Имею визуальный класс-контейнер и не получается во время выполнения изменить у его экземпляра "геометрию" вложенных в него элементов. При попытке изменений происходт чего-то совершенно непонятное. Имеится ли какой-нибудь вариант это сделать?
Ответ: Создайте временный экземпляр класса Form (oMyTempForm = CreateObject("Form")) и создавайте экземпляр Вашего класса-контейнера как экземпляр класса Form (используя метод oMyTempForm.AddObject("oMyNewObj1","MyBaseObj")). По завершению изменений сохраните подправленный экземпляр класса в Вашу библиотеку классов. (oMyTempForm.oMyNewObj1.SaveAsClass(MyClassLibName, MyNewClassName [, Description]))
Вопрос: Неожиданно наткнулся на проблему: функция PEMSTATUS() с аттpибутом 0 в exe работает только если включен флажок Debug info, и выдаёт всегда .F. в противном случае. Это действительно так?
Ответ: Хм... действительно имеется такая проблема.
Вопрос: Как подменить Quick Start форму на своё окно в VFP-приложении, созданном Application Wizard?
Ответ: В экземпляре Вашего класса (app_application) (производного от _framewk._application), если в Application Builder было указано "Быстрый пуск", то стоит так:
Class: app_application
Properties:
cstartupformclass = app_favoritepicker
lStartupForm = .T.
... и требуется заменить на app_MyClassFirstForm, однако такой класс, должен корректно себя вести в методе DoDocumentPickerDialog() в app_application(<-_framewk._application)
... и чтобы упростить задачу, можно поступить по-простому:
- завести в app_application дополнительное свойство типа: This.lContinue
- и переопределить DoDocumentPickerDialog() на что-нибудь типа:
LPARAMETERS tcClass,tcClassLib,tlAlternateMode
IF !This.lContinue
This.lContinue = .T.
RETURN This.DoForm("MyFirstFormSCX")
ELSE
RETURN DODEFAULT(tcClass,tcClassLib,tlAlternateMode)
ENDIF