1616
Типичные вопросы [формы]
На этой станице помещены ответы на вопросы, которые я сам мучительно (или легко) находил, я не думаю, что ответы исчерпывающие, и если Вы найдёте, что что-нибудь дисанирует с Вашими представлениями, давайте обсудим и найдём лучшие решения... Кроме того, часто ответ может зависеть от версии как OS так и Visual FoxPro, проверить всё на всех версиях и платформах для меня просто непосильно. Изначально этот список был исключительно для VFP 5.0, в то время как сейчас это только VFP 7.0/8.0, я мог где-нибудь и пропустить упоминание о версии, и если Вы найдёте несоответствия, сообщите пожалуйста мне.

 

Вопрос: Почему при активизации окна выдаётся ошибка об отсутствии открытой таблицы? 

Ответ: Ну, видимо по тому, что кто-то уже успел её закрыть, если она вообще открывалась. Работа с полями данных из курсоров, “присоединённых” к оконным объектам, требует держать открытыми все используемые в окне курсоры. Это не вызывает трудностей с одним активным окном (используя Form.DataEnvironment.AutoOpen Tables = .T. (Default), и наоборот Form.DataEnvironment.AutoClose Tables = .F. ), но может привести к проблемам при одновременной работе с несколькими окнами, если в них имеются, например противоречивые установки Set Relation to … В таких случаях лучше всего использовать свойство Form.DataSession, установленное в значение 2-Private Data Session (в этом случае можно себе позволить и Form.DataEnvironment.AutoClose Tables = .T.). Здесь нужно предупредить, что в каждой из сессий данных, Вам следует побеспокоиться о наборе установок Set …, сразу после открытия сессии.

Вопрос: Почему во время работы в форме операция добавления выполняется успешно, а по выходу из формы все изменения теряются. В чём проблема? 

Ответ: Анализ неработающего варианта показал, что проблема была в “неосторожном” использовании функции TABLEUPDATE() при буферизованном источнике данных. Следует всегда анализировать возвращаемое значение этой функции, и если оно будет иметь значение .F. необходимо дополнительно использовать функцию AERROR() для уточнения причин отказа системы в сохранении данных (Visual FoxPro 5.0 имеет пример класса DataChecker в Vfp\Samples\Classes\Samples.vcx демонстрирующий корректные способы сохранения данных. Более простой способ сохранения данных Вы можете увидеть в Tasmanian Traders Sample: метод tsBaseForm.Save() в библиотеке классов Vfp\Samples\Tastrade\Libs\Tsbase.vcx). В данном конкретном случае отвергал сохранение триггер добавления данных соответствующей таблицы.

Вопрос: Как быстро осуществлять доступ к элементам управления в форме в режиме редактирования для настройки их свойств, если форма содержит PageFrame? Не удаётся с помощью мыши переключить закладки окна и/или выбрать какой-либо элемент. 

Ответ: Для выбора какого-либо элемента, содержащегося в контейнерном классе достаточно на нём нажать правую клавишу мыши и в раскрывшемся меню выбрать пункт Edit, после чего Вам становятся доступными для редактирования объекты верхнего уровня этого класса, и т.д.

Вопрос: Каким образом следует производить некоторые вычисления для сохранения результатов из формы? 

Ответ: Идеальным, но, к сожалению, не всегда возможным является триггеры соответствующих источников данных, так в триггере update для некоторой таблицы попытка внести изменения в эту же таблицу завершается ошибкой. Ошибку здесь также вызывает попытка перемещения указателя активной записи, что исключает возможность сканирования таблицы с целью выполнения некоторых вычислений. Таким образом, если результаты вычислений требуется произвести и сохранить в поля этой же, т.е. изменяемой записи, или для вычислений Вам требуется сканирование изменяемой таблицы, то скорее всего Вам необходимо будет изменить метод Save формы, т.е. сразу перед TABLEUPDATE() сделать вызов дополнительного метода, например PrepSave, который и выполнит необходимые вычисления и изменения перед сохранением записи. Нужно сказать, что процедуру, выполняющую вычисления и производящую эти изменения в таблице следует оформить в виде сохранённой процедуры, вызываемой из формы.
Следует также отметить, что дополнительные трудности у Вас возникнут при использовании редактируемого объекта Grid, поскольку он самостоятельно производит сохранение изменений из буфера своего источника при переходе с записи на запись. В последнем случае Вам потребуется переопределить Valid-функцию объекта, отображающего данные в столбце (Column), из которой и осуществить вызов метода Save формы. И все же лучший вариант заключается в таком проектировании структуры таблиц, чтобы все необходимые изменения могли быть выполнены в соответствующих триггерах, например, выделив вычисляемые поля в отдельную таблицу и связав ее соответствующим образом с исходной.

Вопрос: Я хотел бы получить листинг (исходный код) формы в целях отладки, но не знаю как это можно сделать. Есть ли хоть какой-нибудь вариант получения кода формы? 

Ответ: Для этого Вы должны 1) сохранить форму как класс ('File\Save as class...') в Вашу vsx-библиотеку; 2) открыть эту библиотеку используя Class Browser (Tools\Class Browser); 3) выбрав таким образом созданный класс в Class Browser, выполнить пункт 'View Class Code' (кнопка на панели инструментов).
(Class Browser в версии 6.0 поддерживает просмотр многих типов файлов [см. Files of type при открытии], в том числе и scx, поэтому не обязательно делать это через библиотеку)
Вообще средства отладки в VFP 6.0 очень разнообразны, лично я часто просто вставляю команду SET STEP ON в метод, работу кода которого необходимо пронаблюдать под отладчиком. Может быть здесь уместно напомнить, что поиск (CTRL+F) имеет опцию Scope: All objects, позволяющую искать фрагмент во всех методах класса, а не только в текущем.

Вопрос: На форме я использую PageFrame, на закладках которого имеются TextBox-ы с определёнными ControlSource. Как теперь, если я изменяю активную запись находясь на одной закладке, обновить данные в TextBox-ах другой, в момент переключения на неё? 

Ответ: Попробуйте во все события MyForm.PageFrame1.Page[i].Activate() добавить строки
DODEFAULT()
This.Refresh()
*
* ... or ...
*
DODEFAULT()
IF !ThisForm.App_picbtns1.EditMode
	 This.Refresh()
ENDIF
... или нечто подобное, но обязательно с This.Refresh(). Лучше для этого создать базовый класс MyPageFrame, а в формах использовать производный от него.

Вопрос: Ничего не понимаю, создаю форму, используя Form Wizard, устанавливаю SET DELETED ON, запускаю форму, а удалённый записи доступны. Как побороть? 

Ответ: Скорее всего у формы Вы имеете свойство MyForm.DataSession = 2-Private Data Session... в этом случае, для каждой такой формы, создаётся своя сессия данных, установки SET's которой, не имеют никакого отношения к установкам в Default Data Session(1)... Поэтому в каждой такой форме их необходимо устанавливать каждый раз сразу после создания новой сессии данных для формы...
Имеются два таких места где это лучше сделать:
- если Вы используете Local View (SELECT's), то скорее всего Вам необходимо также SET DELETED ON перед их открытием, и тогда лучше поместить код подобный следующему
IF ThisForm.DataSession # 1
   SET DATASESSION TO ThisForm.DataSessionID
   SET TALK OFF
   SET DELETED ON
   SET MULTILOCKS ON
   SET EXCLUSIVE OFF
ENDIF
в событие MyForm.DataEnvironment.BeforOpenTables()
- если сказанное выше не критично, то достаточно этот же код поместить в событие MyForm.Load(), т.е. перед открытием Ваших оконных сontrol's, возможно придётся ещё добавить
IF !EMPTY(ALIAS()) 
     LOCATE && or GO TOP
ENDIF

Вопрос: Как для TextBox формы поддержать работу с Windows Clipboard? 

Ответ: Достаточно в main menu оставить соответствующие пункты меню... см. пункты меню после генерации меню через Menu\Quick menu

Вопрос: Из формы вызываю модальную форму, причём последняя не scx-файл, а является классом моей vcx-библиотеки... Как мне теперь вернуть из неё некоторое значение в вызывающую форму?  

Ответ: Если кратко, то в Вашей модальной форме выполните метод ThisForm.Hide() в MyModalForm.cmdSaveValue.Click() [... читайте внимательно Hide() method в MSDN :-)], при этом в вызывающей форме начинает выполняться команда следующая сразу за loMyModalForm.Show(1), причем ссылка loMyModalForm всё ещё действительная (в любом противном случае уже разрушена :-), ...и может быть использовна для получения значения из какого-нибудь специального public oMyModalForm.uRetValue :-)... Hу а для того, чтобы передать значение Вашей модальной форме из вызывающей, используйте параметры в MyModalForm.Init(tuP1[,tuP2,...]), возможно заведя дополнительные private-свойства для этих самых параметров, чтобы "донести" их значения до события MyModalForm.cmdSaveValue.Click() :-)
Интересный вариант решения как-то показал Mike Korneev на fido7 news RU.VISUAL.FOXPRO:
*********** any.prg
local lc_Tmp
lc_Tmp='1234'
createobj('ff',@lc_Tmp)
? lc_Tmp

define class ff as form
	autocenter=.t.
	width=200
	height=100
	add object txt as textbox with top=20, left=50, width=100
	add object cmd as commandbutton with top=60, left=50, height=25, width=100
	
	func cmd.click
		thisform.hide
	endfunc
	
	func init(rc_Val)
		this.txt.value=rc_Val
		this.show(1)
		rc_Val=this.txt.Value
		return .f.
	endfunc
enddef
********** end of any.prg

Вопрос: Как предотвратить повторный запуск формы в VFP-приложении? 

Ответ:
- самый простой способ: вставить в пункт меню открытия формы в качестве выражения в Skip for: !WEXIST('MyForm'), но это если есть пункт меню для вызова формы...
- другой вариант, хранить массив ссылок где-нибудь в классе MyApplication, открытие форм осуществлять методом этого самого класса, который собственно и будет рулить корректным запуском/уничтожением... другими словами в точности так, как делает Wizard Application VFP 6.0 в
Class Library: HOME()+'wizards/_framewk.vcx'
Method: DoForm(..., tlNoMultipleInstances, ...)
в то время как режим устанавливается через Application Builder на закладке Form - Единственный экземпляр...
- наконец, нет проблем написать в базовом класса для всех Ваших форм в событии MyBaseForm.Load() просмотр ссылок в _SCREEN.Forms(), и возвращать .F. если форма с таким именем уже существует.
 
 
Hosted by uCoz