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

 
Разделы:

   Контролы
   Среда данных
   Формы
   Интернет
   Меню
   ООП
   Код программ
   Отчёты
   Прочие вопросы


Контролы 

Среда данных 

Формы 

Интернет 

Меню 

ООП 

Код программ 

Отчёты 

Прочие вопросы 


Контролы 

Вопрос: Как вернуть числовое кодовое значение из ComboBox с двумя полями (символьное название, числовой код) для записи его в числовое поле таблицы, т.е. когда данные таблицы для ComboBox являются подчинёнными некоторой родительской таблицы по числовому кодовому полю? 

Ответ: Для версии Visual FoxPro 3.0 исчерпывающий ответ зарегистрированные пользователи могут найти в Internet по адресу: http://msdn.microsoft.com/library/ (анг.) в разделе Preodicals/FoxTalks/Volume 8 1996 : (http://www.pinpub.com/foxtalk/) статья Use a Combo Box to Select Foreign Keys http://msdn.microsoft.com/library/periodic/period96/D1/FT0896.htm (для краткости детали я здесь не обсуждаю). В версии Visual FoxPro 5.0a достаточно у ComboBox установить свойство BoundTo в значение: .T., отличное от .F. (default), конечно же определив свойства: BoundColumn, ControlSource, RowSource и RowSourceType соответствующим образом.

Вопрос: Каким образом я могу отображать подчинённое множество записей в Grid для некоторой записи в родительской таблице, если источником данных для Grid является View? 

Ответ: Принципиально имеется две возможности сделать это: использовать параметрический запрос в качестве источника данных к Grid или установить для него соответствующее свойство Filter. Каждый из этих случаев может существенно отличаться объёмом пересылаемой информации между сервером данных и клиентом, нужно также отметить, что в первом случае Вам потребуется явное применение функции REQUERY() для источника данных Grid после перемещения указателя текущей записи в родительской таблице, чтобы повторно выполнить запрос данных согласно изменившемся условиям. См. также
Q142974 Views.exe - One-To-Many Form Using a Parameterized View на Microsoft.com
Q156169 HOWTO: Ignore Parameters in View or Query If Blank

Вопрос: Как добавить CheckBox и ComboBox в качестве отображаемых элементов столбцов в Grid? 

Ответ: Прежде всего, следует отметить, что для CheckBox тип данных соответствующего ему поля источника данных должен быть логическим, в то время как использование ComboBox подразумевает, что Вы определите для него свойства: RowSource, RowSourceType, Style (данные, отображаемые в ComboBox или подчинённый источник данных) и ControlSource, BoundColumn, BoundTo (возвращаемое значение, т.е. редактируемое поле таблицы-источника данных Grid, для которого ComboBox собственно и отображает подчинённое значение).
Для добавления CheckBox проще всего использовать Builder для Grid (вызов по правой кнопке мыши Builder…), где на закладке Layout следует переустановить значение Control type в CheckBox для соответствующего столбца Grid. Проследите, чтобы перед вызовом Builder используемые таблицы были предварительно открыты, поскольку он может анализировать содержание только открытых таблиц, предоставляя Вам информацию для настройки.
Элементы CheckBox или ComboBox можно добавить и во время редактирования Grid. Для этого выберите Ваш столбец в качестве активного (Columni в окне Properties) и перетащите нужный Вам элемент (CheckBox или ComboBox) из Forms Controls Toolbar в первую строку Grid. Если теперь Вы раскроете ComboBox объектов в окне Properties, то увидите, что выбранный Вами элемент добавлен в качестве элемента столбца (сразу после Text1), хотя и не является пока активным. Наконец, чтобы активизировать добавленный Вами элемент переустановите свойство CurrentControl для столбца Columni на нужный Вам элемент.

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

Ответ: В VFP6.0 магическое действие в Grid.Header.Click() следующее:
LOCAL lnRecNo
lnRecNo = IIF(!EOF(), RECNO(), 0)
ThisForm.DataEnvironment.cursor1.Order = "Column_Order" 
*-- or SET ORDER TO TAG Column_Order
WITH This.Parent.Parent
    .ActivateCell(1, .ActiveColumn)
    IF lnRecNo # 0
        GO (lnRecNo)
    ELSE
        GO TOP 
    ENDIF
    .Refresh()
ENDWITH
См. также раздел "PRB: Incremental Search with Grid Causes Screen Flicker" в MSDN

Вопрос: У меня не удаляется запись в Grid, подскажите как я это должен сделать корректно. 

Ответ: Если у Вас установлена блокировка в блокировку записи (buffering record) и свойство Grid.DeleteMark сброшено (.F.), то Вам может помочь следующий код:
...
WITH ThisForm
    IF VARTYPE(.ActiveControl) = "O";
            AND .ActiveControl.BaseClass == "Grid";
            AND !INLIST(CURSORGETPROP("Buffering"), 4, 5);
            AND !.ActiveControl.DeleteMark
        *-- Grid с DeleteMark = .F. и buffering record имеет проблему 
        *-- при удалении записи, необходимо временно
        *--  установить свойство DeleteMark = .T.
        LOCAL loGrid
        loGrid = .ActiveControl
        loGrid.DeleteMark = .T.
    ENDIF
ENDWITH
lnRecNo = RECNO()
DELETE
llError = !TABLEUPDATE(.T.)
IF TYPE("loGrid") = "O"
    loGrid.DeleteMark = .F.
ENDIF
...
См. также http://support.microsoft.com/kb/q191641/

Вопрос: Как в объекте Grid удалить (скрыть с экрана), а позже восстановить, на прежнее место, колонку (объект Column)? При этом, необходимо чтобы при удалении колонки на ее место вставала колонка справа. 

Ответ: Попробуйте следующее в методе Header1.Click():
WITH This.Parent
    IF .Visible 
        .Visible = .F.
        .Width = 0
    ELSE
        .Visible = .T.
        .Width = 75 && -- или прежний размер
                    ** -- из предварительно созданного свойства-массива формы  
    ENDIF
ENDWITH

можете установить Grid1.GridLineColor = RGB(128, 128, 128), чтобы скрытая колонка "не бросалась в глаза"... как, надеюсь, самый простой вариант...

Вопрос: Я хочу добавить данные в ComboBox, так чтобы иметь два столбца, у меня нет присоединённого источника данных, данные я хотел бы добавлять программно, но у мкня ничего не получается. Как это можно сделать? 

Ответ: Если я правильно понял проблему, Вам нужно использовать: для первой колонки: .AddItem(...) ...и для всех остальных: .AddListItem(...)

Вопрос: Размещаю на форме ActiveX-ный компонент ImageList. Прописываю в нём имеджы. Размещаю на той же форме ActiveX-ный компонент Toolbar. Делаю в нём ссылку на ImageList. Завершаю редактирование Toolbara. Открываю его снова, ссылка - none. Что с этим делать?  

Ответ: Выполняйте инициализацию свойства MyForm.MyToolbar1.Object.ImageList в runtime, подобно следующему:
#DEFINE tbrDefault	0
#DEFINE tbrCheck	1
#DEFINE tbrButtonGroup	2
#DEFINE tbrSeparator	3
#DEFINE tbrPlaceholder	4
#DEFINE tbrDropDown	5 

#DEFINE MAXBUTTONS	2 

LOCAL lcCurKey, loButton, lnCntImg, lnCntBtn
lnCntImg = 0
WITH ThisForm.MyToolbar1.Object
	.ImageList = ThisForm.MyImageList.Object
	WITH .Buttons
		FOR lnCntBtn = 1 TO MAXBUTTONS
			lnCntImg = lnCntImg+1
			lcCurKey = SYS(2015)
			.Add(,lcCurKey)
			loButton = .Item(.Count)
			WITH loButton
				.Image = lnCntImg 
				* ... etc
			ENDWITH
			loButton = NULL
		ENDFOR	
	ENDWITH
ENDWITH
Обратите внимание на два момента:
использование ключевого слова Object, чтобы адресоваться не к VFP-покрытию объекта, а непосредственно к его свойству/методу
чтобы получить ссылку на Button я использую Buttons.Item(Buttons.Count) а не возвращаемое значение методом Add()
Подозреваю, что для этого тривиального объекта, это всё смотрится как "лишние навороты", но лишь до некоторых пор..., и конечно всё зависит от версии объектов... :-)
См. также Q163803 в MSDN

Вопрос: У меня не получается обратиться к UDF в ControlSource столбца Grid в форме. 

Ответ: Попробуйте использовать следующее выражение: (UDF(Table.Field1[,...])[...]). Обратите внимание на "лишние" внешние скобки и фактический параметр в UDF: поле-таблицы.

Вопрос: Хочу показать значение memo-поля таблицы в столбце Grid, однако у меня отображается только слово Memo, а мне нужно значение. Как это можно сделать? 

Ответ: Самый простой способ добавить "лишние" внешние круглые скобки подобно ThisForm.MyGrid.ColumnX.ControlSource = "(MyTable.MyMemoField)", также можно вставить Editbox в качестве CurrentControl столбца и отображать значение memo-поля через него.

Вопрос: Хочу многострочный заголовок столбцов в Grid. Что можно сделать? 

Ответ: См ID: Q133164 В MSDN или на http://www.microsoft.ru/catalog/article.asp?article_id={4CA8A85C-B996-11D4-9DAA-00508B8B6DC3} или http://support.microsoft.com/kb/q133164/

Вопрос: В приложении использую ActiveX control, создаю дискрибутив используя VFP Setup Wizard. После установки на клиенте получаю ошибку: OLE error code 0x80040112: Appropriate license for this class not found. Можно ли это как-то побороть?  

Ответ: см. "INFO: OLE Control Licensing in Visual FoxPro" в MSDN а также "HOWTO: Register an ActiveX Control (.ocx) Manually".

Вопрос: У меня Windows NT, пытаюсь из RichTextBox Control организовать печать, но не удаётся из CommonDialog Control получить device context hDC property, чтобы передать первому в метод SelPrint. Другими словами: можно ли вывести информацию из RichTextBox Control на печать? 

Ответ: Хм... посмотрел... действительно не работает :-( ... Однако, попробуй чего-нибудь типа:
#DEFINE C_DRVNAME "winspool"
DECLARE LONG CreateDC IN Gdi32.dll ;
  STRING @ lpszDriver, ; &&// driver name
  STRING @ lpszDevice, ; &&// device name
  STRING @ lpszOutput, ; &&// not used; should be NULL
  STRING @ lpInitData  &&// optional printer data
LOCAL nCntPrn
LOCAL ARRAY laPrinters[1,2]
nCntPrn = APRINTERS(laPrinters)
IF nCntPrn = 0
 RETURN .F.
ENDIF
LOCAL lcPrinter, lhDC
lcPrinter = laPrinters[1,1]
lhDC = CreateDC(C_DRVNAME, lcPrinter, NULL, NULL)
IF lhDC = 0
 RETURN .F.
ENDIF
ThisForm.Olecontrol1.Object.SelPrint(lhDC)
CLEAR DLLS
C_DRVNAME как я понял из MSDN CreateDC() для Windows 95/98 должно быть NULL в то время как у меня тоже NT, а по сему "winspool"

Вопрос: Могу ли я как-нибудь вставить Internet Explorer в форму VFP? 

Ответ: Попробуйте что-нибудь подобно следующему:
#DEFINE C_URL	"http://vfpdmur.narod.ru/" &&- Insert your URL here

RELEASE ox
PUBLIC ox
ox = CreateObject("MyInternetExplorer")
IF TYPE('ox') = 'O' AND !ISNULL(ox)
	WITH ox
		.oWeb.Navigate(C_URL,,"_self")
		IF !.Visible
			.Visible = .T.
		ENDIF
	ENDWITH
ELSE
	MESSAGEBOX("Error in CreateObject('MyInternetExplorer')", 16, 'Error')
ENDIF

DEFINE CLASS MyInternetExplorer AS form
	ADD OBJECT oWeb AS CWebExp
	Caption = "My Internet Explorer"
	PROCEDURE Init
		WITH ThisForm
			.Width = SYSMETRIC(21)*3/4
			.Height = SYSMETRIC(22)*3/4
			.Resize()
		ENDWITH
	ENDPROC
	PROCEDURE Resize
		ThisForm.oWeb.resize()
	ENDPROC
ENDDEFINE

DEFINE CLASS CWebExp AS olecontrol
	OleClass = "Shell.Explorer"
	PROCEDURE Resize
		WITH This
			.Width = ThisForm.Width - 1
			.Height = ThisForm.Height - 1
		ENDWITH
	ENDPROC
	PROCEDURE Refresh
		NODEFAULT
	ENDPROC
ENDDEFINE

Вопрос: Нужен h-файл с определением констант для Excel 2000 Где можно взять? 

Ответ: Где взять именно для Excel 2000 не знаю, для Excel 97 есть на http://www.universalthread.com Однако, есть ещё такая утилита "GetConstants - COM constants to .h file" на http://www.west-wind.com/files/getconstants.zip

Вопрос: SelectOnEntry on MouseClick for TextBox как организовать? 

Ответ: Попробуйте выполнить KEYBOARD '{SHIFT+END}' в событии MyTextBox.GotFocus()

Вопрос: Не получается в ActivX Comctl2.MonthView.2 воспользоваться DayBold(), возвращает "Function argument value, type, or count is invalid (Error 11)". Можно это как-нибудь побороть? 

Ответ: Попробуйте как-нибудь так:
 
WITH ThisForm.Olecontrol1.Object 
	FOR lnCnt = 1 TO 42 
		.DayBold(TTOC(.VisibleDays(lnCnt))) = .T. 
	ENDFOR 
ENDWITH 


Среда данных 

Вопрос: Почему триггер выдаёт ошибку? 

Ответ: Потому что на момент выполнения триггера данные, не удовлетворяют его требованиям. Для того, чтобы понять что именно происходит, лучше всего проследить соответствующий участок кода в отладчике. Без анализа конкретной ситуации можно предположить, что буферизация записей изменяемой таблица не соответствует логике внесения изменений, прописанных в коде. Например, Вы вставляете новую запись в таблицу, без использования свойств default value для полей, только намериваясь внести соответствующие изменения, а триггер уже отработал.

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

Ответ: Лучший вариант: переустановить свойство Form.BufferMode окна в значение 2-Optimistic. В этом случае, если свойства Form.DataEnvironment.Cursor(i).BufferModeOverride у источников данных окна будут установлены в значение по умолчанию: 1-Use Form Setting (Default), то все отмеченные выше проблемы снимаются автоматически, впрочем Вы вольны устанавливать способ буферизации курсоров окна так, как найдёте нужным.

Вопрос: Почему могут возникнуть проблемы с удалением записей из таблицы, имеющей Primary Key? 

Ответ: Данная проблема возникает потому, что не удаётся всегда уследить за корректной установкой системного режима Set Deleted в состояние On при использовании таблицы, а в состоянии Off конечно требования по уникальности значения Primary Key нарушаются. Чтобы устранить эту проблему следует индексам, соответствующим как Primary Key, так и Candidate назначить значение фильтра, содержащего выражение: Not Deleted(). Нужно иметь ввиду, что при этом не будет выполняться Rashmore оптимизация, поэтому сказанное выше приемлемо, если нет проблем производительности. В противном случае Вам следует подумать над возможностью снова использовать прежде помеченные к удалению записи (см. RECALL Command)

Вопрос: Почему создаваемые в Visual FoxPro таблицы не имеют кодовой страницы 1251? 

Ответ: Это странно, если у Вас установлена руссифицированная версия Windows. Но в любом случае, нужно добиться того, чтобы значение, получаемое от функции Cpcurrent() было 1251. Если это на Вашей машине, достаточно выбрать пункт Tools/Options… и изменив режим Collate sequence в значение Russian на закладке Data, выполнить Set As Default. Далее, неплохо было бы пройтись по созданным Вами проектам и в каждом из них, открыв диалог Project Info…, на закладке Files выполнить пункт Update Native Code Page для отображения кодовых страниц файлов, включенных в проект. Программа Cpzero поможет Вам изменить кодовые страницы любых файлов со структурой DBF. Наконец, на клиентских машинах, использующих выполнимые файлы, следует попытаться обеспечить установку кодовой страницы путём добавления параметра: CODE PAGE=1251 в файлы Config.fpw в текущих каталогах этих исполняемых фалов.

Вопрос: Я создал двухтабличный Local view с использованием ключевого слова Join из построителя View, а он не работает так, как я ожидал. В чём дело? 

Ответ: Microsoft Visual FoxPro 5.0a имеет проблему при создании Local view, содержащих ключевое слово Join: в список таблиц, следующих сразу за ключевым словом FROM, помещается полный список таблиц, участвующих в запросе, тогда как при наличии Join, требуется исключить из него те таблицы, которые используются непосредственно в Join. Далее, если в Join вовлечено более двух таблиц, то условия "соединения" записей, т.е. параметры ON, должны следовать сразу за соответствующими Join, в то время как построитель LocalView создаёт группу параметров ON в конце списка параметров Join. Мной написана утилита ModeView.exe, которая поможет Вам произвести соответствующую корректировку Local view базы данных, правда ею придётся пользоваться после любых изменений с сохранением ваших Local view, содержащих Join непосредственно перед их использованием в приложении [Получить копию ModeView.exe]. См. также http://support.microsoft.com/kb/q157254/

Вопрос: Имеется ли простая возможность изменить месторасположение базы данных у FoxPro приложения? 

Ответ: Если речь идёт о свойстве Database у объектов CursorX в DataEnvironment форм и отчётов, то Вы можете легко его менять в runtime в событии DataEnvironment.BeforOpenTables() (по крайней мере начиная с версии 5.0) или в событии MyBaseForm.Load() установив MyForm.DataEnvironment.AutoOpenTables = .F. и воспользовавшись методом MyForm.DataEnvironment.OpenTables() явно. Вы также можете:
При новом месторасположении базы данных, в то время как её прежнее месторасположение уже недоступно, в среде редактирования последовательно открыть все формы и отчёты и на вопрос о невозможности открыть базу данных указать её новое месторасположение. Внесённые изменения следует, конечно, сохранить при выходе из диалога редактирования формы/отчёта.
Если предложенный вариант Вам кажется утомительным, Вы можете произвести соответствующие изменения, открыв файлы *.scx и *.frx "вручную". (См. для *.scx поля: Class и Properties, а для *.frx поля: Name и Expr. Вам необходимо отредактировать свойство Database у объекта Cursor).
Наконец последний вариант можно выполнить программно. Для этой цели мной написана утилита ChangeDb.exe. [Получить копию ChangeDb.exe]

Вопрос: В VFP 6.0 при попытке получить соединение с таблицей базы данных Visual FoxPro через ADO-ODBC я получаю несуществеющие ошибки. Имеется ли возможность сделать то, чего я хочу? 

Ответ: Действительно, ADO-ODBC VFP 6.0 имеет проблему, в том числе и не работает пример: http://msdn.microsoft.com/vfoxpro/downloads/download.asp?ID=025 , однако после установки http://msdn.microsoft.com/vfoxpro/downloads/download.asp?ID=027 проблема исчезает. И ещё: нужно регистрировать VFP-источник данных под System DSN в ODBC-Administrator.

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

Ответ: Перед попыткой обращения к любой сохранённой процедуре из базы данных Вам следует сделать её активной, используя команду SET DATABASE TO dbc_name. Проблема может возникнуть и при компиляции, если в коде непосредственно прописано обращение к хранимой процедуре, а сама база данных не включена в проект. В этом случае достаточно дополнительно воспользоваться EXTERNAL ARRAY MyDbcFunction непосредственно в коде перед обращением к процедуре, чтобы объяснить эту особенность компилятору.

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

Ответ: Прежде всего, советую Вам посмотреть свойство Exclusive всех Cursor'ов в DataEnvironment Ваших форм и отчётов, посмотрите также значение параметра Open Exclusive в Tools\Options... на закладке Data, очень может быть, что эти параметры требуют открытия баз данных/таблиц в режиме Exclusive... Для открытия таблиц из кода следует делать что-нибудь подобно следующему:
IF !DBUSED("MyDbc")
    OPEN DATABASE [full_path]MyDbc.dbc SHARED
    IF !DBUSED("MyDbc")
        ?CHR(7)
        MessageBox("Can not open database: MyDbc", 16, "Error")
        RETURN .F.
    ENDIF
ENDIF
SET DATABASE TO MyDbc
IF !INDBC("MyTable", "Table")
    ?CHR(7)
    MessageBox("No table: MyTable in database: MyDbc", 16, "Error")
    RETURN .F.
ENDIF
USE MyDbc!MyTable IN 0 SHARED

Вопрос: Вроде простая задача: например, есть таблица примерно такой структуры: Код клиента, дата оплаты, сумма. Коды клиента в таблице повторяются. Как можно одним запросом получить результат: Код клиента-дата последней оплаты сумма. А то приходится разбивать на два подзапрса -некрасиво :-(  

Ответ: Где-нибудь так:
SELECT * FROM MyDbс!Payments p ;
    WHERE STR(p.idclient) + DTOS(p.date) IN;
        (SELECT STR(p.idclient) + DTOS(MAX(p.date));
            FROM MyDbс!Payments p;
            GROUP BY p.idclient)

... тоже как бы два в одном, а если для конкретного клиента, то можно так:

SELECT TOP 1 *;
FROM MyDbс!Payments p;
WHERE p.idclient = ?idclient;
ORDER BY p.date DESC

Вопрос: Имею VFP 6.0+SP3 VS6 и сейчас появился класс Session. Не покажете ли пример его использования? 

Ответ: Как-нибудь примерно так:
#DEFINE C_DATABASE	"D:\Program Files\Microsoft Visual Studio\";
	+"MSDN\99OCT\1033\SAMPLES\VFP98\Tastrade\Data\Tastrade.dbc"
RELEASE goSec
PUBLIC goSec
goSec = CreateObject('MyclsSession')

DEFINE CLASS MyclsDE AS DataEnvironment
	Comment = 'MyMultiUsedDEObject'
	InitialSelectedAlias = 'Employee'
	
	ADD OBJECT Cursor1 AS cursor WITH ;
		Database = C_DATABASE, ;
		Alias = 'Employee', ;
		CursorSource = 'Employee', ;
		Exclusive = .F.
	* ...
	PROCEDURE BeforeOpenTables
		*... change if need
	ENDPROC 
ENDDEFINE

DEFINE CLASS MyclsSession AS Session
	DataSession = 2
	Name = 'MySession'
	oDataEnvironment = NULL
	FUNCTION Init
		This.oDataEnvironment = CreateObject('MyclsDE')
		IF VARTYPE(This.oDataEnvironment) = 'O'
			This.oDataEnvironment.OpenTables()
		ENDIF
	ENDFUNC
ENDDEFINE
Обратите внимание, к моменту возникновения события MyclsDE.BeforeOpenTables() экземпляр объекта MyclsDE уже существует, и Вы можете из кода изменить его непосредственно перед открытием источников данных.
Нужно также иметь ввиду, что метод AddObject(...) контейнера и функция CreateObject(...) работают по разному:
1) метод This.AddObject('MySession') добавляя экземпляр класса Session к контейнерам, имеющим св-во DataSession, - HИКОГДА и HИКАКИХ HОВЫХ DS не создаёт... и действует так:
- если родительский объект имеет значение св-во DataSession = 2-Private DS, то в DS этого самого родительского объекта.
- если родительский объект имеет значение св-во DataSession = 1-Default DS, то в DS Default(1)
2) в свою очередь, поведение при создании экземпляра объекта класса Session через CreateObject('MySession') несколько другое:
- если создаваемый объект имеет значение св-во DataSession = 2-Private DS, то всегда создаётся новая DS, причём значение свойства MyForm.DataSessionID переустанавливается в значение последней созданной сессии.
- если создаваемый объект имеет значение св-во DataSession = 1-Default DS, то в DS Default(1)

Вопрос: Я не создаю в базе объект Connection. Просто в коде делаю lnHndl = sqlconnect(lcDSN,lcLogin,lcPassw) и хотел бы предотвратить сообщение об ошибке, обрабатывать его программно. Как мне это сделать? 

Ответ: Попробуйте выполнить
=SQLSETPROP(0,"DispWarnings", .F.) 
=SQLSETPROP(0,"DispLogin", 3)  && DB_PROMPTNEVER
 

Вопрос: Поставил VFP6.0, и хотя установлен "Prompt for code page" запроса не происходит :-( В чем проблема?  

Ответ: Читайте внимательно в MSDN раздел "SET CPDIALOG"... и там написано:
ON - (Default) Displays the Code Page dialog box when you open a table
and the following conditions are true:
- The table is opened exclusively.
- The table is not marked with a code page.
Другими словами зачем вызывать диалог, если поменять кодовую страницу всё равно не получится :-)

Вопрос: Можно ли выполнить команду PACK для таблицы через ODBC? 

Ответ: См. HOWTO: Pack a Table Through the Visual FoxPro ODBC Driver (VFPODBC.dll)
на http://support.microsoft.com/kb/q234756/

Вопрос: Возможно ли в VFP из программы добавлять/изменять/удалять в ODBC Администраторе DSN? 

Ответ: Все действия возможны через Win32Api используя SqlConfigDataSource из Odbccp32.dll и функции досупа к системному реестру:
  - для добавления см. [Q142216] HOWTO: Create ODBC Data Sources Using SqlConfigDataSource в MSDN
  - для чтения/изменения: см. [Q191638] HOWTO: Programmatically Access the Registry in Visual FoxPro ("ODBC Registry") в MSDN т.е. класс ODBCReg из HOME(2)+"classes\registry.prg" и пример в Solution.app в Win32Api "Read ODBC Regestry values" ("Sample Class Libraries" в MSDN)
  - для удаления см. [Q195262] HOWTO: Delete ODBC Data Source Using SqlConfigDataSource
см. также
  - [Q165866] How to Use File DSNs and DSN-less Connections
  - Примеры подключения различных Баз Данных через ADO (на www.relib.com)

Вопрос: Никак не могу получить полной Rushmore оптимизации (SYS(3054) говорит, что оптимизация частиная) на таблице с единственным символьным полем. Чего бы это могло быть? 

Ответ: Попробуйте установить SET DELETED OFF перед выполнением SQL-SELECT :-) ... Если в последнем случае получите полную оптимизацию, то попробуйте добавить индекс INDEX ON DELETED() TAG DEL в Вашу таблицу ... Теперь, как надеюсь, оптимизация будет полной и при SET DELETED ON

Вопрос: Хотелось бы вести автоматически поле данных типа "дата последних изменений", однако в trigger update для таблицы этого у меня не получается. Почему и как это можно сделать? 

Ответ: К сожалению в Update trigger невозможно сделать изменения в записи, по поводу которой он вызван, поскольку бесконечная рекурсия :-( и в Update trigger возможно только внести изменения в другую таблицу, имеющую связь с исходной типа один-к-одному. В противном случае Вы можете это сделать:
- в Record Rule для таблицы вызвать ХП
- в MyBaseForm.Save()
- в событиях Valid() соответствующих контролов

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

Ответ: Действительно, в процессе на стороне клиента интервалом обновления данных рулит второй параметр в SET REFRESH , т.е раньше этого значения (если не использовать SYS(1104) [в VFP 6.0 см. Q269284 Knowledge Base Articles INFO: Undocumented Function SYS(1104) или VFP 7.0], которая производит обновления в момент выполнения... ну или REQUERY() в случае Remote View/SELECT :-) получить новые значения невозможно.
Далее, если кэшь даже и обновлена (явно через SYS(1104), или прошёл интервал, указанный вторм параметром в SET REFRESH), то в любом случае, чтобы обновлённые данные в текущей записи стали "реально новыми", требуется "дёрнуть" за указатель записи (типа GO (RECNO('ChangedTable')) IN ChangedTable)
Другими словами, выполнять GO (RECNO('ChangedTable')) IN ChangedTable (или как-то иначе выудить новые значения, например Refresh() для "присоединённых данных") раньше чем не истёк интервал, указанный в качестве второго параетра в SET REFRESH, для VFP 5.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. если форма с таким именем уже существует.


Интернет 

Вопрос: Я новичок в VFP 6.0 и меня интересуют возможности по использованию ADO и OLE объектов в Internet, как я могу воспользоваться VFP в ASP? 

Ответ: В ASP Вы можете использовать VFP-COM объекты точно также как и любые другие, подобно: var ob = Server.CreateObject ("VfpOleObject"); Однако я понял так, что Вас интересует использования данных из таблиц VFP базы данных. Это Вы можете делать создавая ADO RecordSet-ы из VFP источников данных: (var oc = Server.CreateObject ("ADODB.Connection"); oc.Open (sVfpODBCConnctStr) и т.д.). Если Вас интересует вопрос создания html-страниц средствами VFP, то VFP имеет большое множество Помощников (Wizards), классов, и FoxIsapi для этих целей. См. также http://www.west-wind.com/ ... и много другого относительно VFP в Internet. Для преобразования ADO RecordSet в VFP cursor Вы можете воспользоваться http://msdn.microsoft.com/vfoxpro/downloads/download.asp?ID=025 См. также http://support.microsoft.com/kb/q197861/, (VFPCOM Utility)http://msdn.microsoft.com/vfoxpro/downloads/updates.asp

Вопрос: В VFP 7.0 появились две функции CursorToXML() & XMLToCursor() используя первую из них я получаю xml-файл для таблицы, однако как бы его посмотреть в IE. 

Ответ: Для того, чтобы отобразить xml-файл в IE, необходимо определить формат отображения данных. Сделать это можно используя css или xls файлы. Попробуйте проделать следующее:
- открыть таблицу HOME(2)+"tastrade\data\setup.dbf"
- из окна команд выполнить: ?CursorToXML("Setup", "myXMLSetup.xml", 1, 8+48+512, 0, "myShmSetup.xsd")
- создав новый файл и записав в него следующий текст:
<?xml version="1.0"?>
<!-- File Name: myXMLSetup.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
<H2>List VFP table Setup</H2>
<xsl:for-each select="VFPData/setup">
<SPAN STYLE="font-style:italic">key_name: </SPAN>
<xsl:value-of select="key_name"/><BR />
<SPAN STYLE="font-style:italic">value: </SPAN>
<xsl:value-of select="value"/><P />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
сохранить его под названием myXMLSetup.xsl
- добавить второй строкой в файл myXMLSetup.xml строку:
<?xml-stylesheet type="text/xsl" href="myXMLSetup.xsl"?>
- наконец можно попробовать открыть файл myXMLSetup.xml из IE.
Другой способ заключается в том, чтобы написать для myXMLSetup.xml css-файл, например:
/* File Name: myXMLSetup.css */

key_name
{display:block;
margin-top:12pt;
font-size:10pt}

value
{display:block;
font-size:12pt;
font-weight:bold;
font-style:italic}
и использовать его вместо xsl показанного выше.
См. также:
- Q253732 HOWTO: Use FoxPro and MSXML to Return Information About an XML Document
- Q191758 Convert FoxPro Cursor into XML Data Format
- Q253713 Move Data From an XML Document into a FoxPro Table
в MSDN.
Нужно также иметь ввиду, что конструкция типа:
<SCRIPT LANGUAGE="JavaScript">
var doc = dsoData.XMLDocument;
...
<XML id="dsoData" src="Data.xml"></XML>
на самом деле в качестве значения переменной doc возвращает объект DOMDocument (т.е. "MSXML.DOMDocument"). Как говориться не верь глазам своим :-)

Вопрос: При попытки использовать VFP 8.0 OLEDB Provider из-под MS IIS 6.0 (OS Windows 2003 Server) получаю: System.Data.OleDb.OleDbException: No error information available: REGDB_E_CLASSNOTREG(0x80040154). Если создать обычное Windows-приложение (например на C# с доступом через System.Data.OleDb), то всё работает без вопросов. Есть ли способ борьбы с этим? 

Ответ: Мне известны несколько вариантов преодоления проблемы:
1) вместо VFP 8.0 OLEDB Provider использовать VFP OLEDB Provider для версии 7.0
2) в файле machine.config в секции processmodel подменить UserName с MACHINE на SYSTEM
3) Удалите VFP 8.0 OLEDB Provider версии 8.0.0.3006, и установите именно версию 8.0.0.3117 (SP1VFP8) или выше, взяв её с http://msdn.microsoft.com/vfoxpro/downloads/updates/default.aspx Обратите внимание, что для корректной регистрации OLE-компоненты вам нужны права именно локального администратора сервера (НЕ сетевого или контроллера домена администратора, а именно локального админа).
По моим представления, второй вариант не следует пытаться использовать из-за соображений безопасности.

Вопрос: Как программно из-под VFP скачать файл с internet-ресурса, имея его адрес? 

Ответ: У VFP непосредственно такой встроенной возможности нет, однако:
- во-первых, из-под VFP можно вызывать Win32API-функции
- во-вторых, в VFP можно использовать ActiveX компоненты, специализированные под подобные действия

Относительно во-первых:
Имеется по крайней мере два набора функций: MS WinSock и MS WinInet (см. в MSDN), использование первого затруднено из-за необходимости передавать структуры через параметры (по этому поводу можно поискать struct.zip by Christof Lange в internet), тогда как второе достаточно легко, см. например: на ftp://ftp.prolib.de/ tools/archive/internettools/ vfphttp.PRG, ftp_test.prg, ftp.PRG См. также статью MSDN - [Q174524] "HOWTO: How To Retrieve and Insert HTML Into Memo Field".
... относительно какого-нить примера с MS WinSock - ну например на http://vfpdev.narod.ru/util_r.html - getip.zip (1,69KB) - класс для получения IP адреса по любому имени хоста, с использованием Windows Sockets 2 API

Относительно во-вторых:
от MS мне известно два компонента в этом направлении: MsWinSck.ocx, MsInet.ocx
См. примеры в MSDN Knowledge Base:
- [Q311306] HOWTO: Use Visual FoxPro to Download a Web Page from the Internet
- [Q315124] SAMPLE: Use the Winsock ActiveX Control with Visual FoxPro
См. также примеры на http://vfpdev.narod.ru/util_r.html
- smtp.zip (20,8KB) - пример демонстрирует посылку сообщения через SMTP протокол, используя MS WinSock control.
- tcpSock.zip (12,5KB) - пример клиента и сервера с использованием MS Winsock control (MsWinSck.ocx) VFP6.0+SP3(VS6)/VFP7.0(SP1VFP7)
- srvwinsk.zip (100KB) - пример приложений клиента и сервера с использованием MS Winsock control (MsWinSck.ocx) VFP 8.0/VFP 9.0
Как дополнение, там же можно посмотреть: - wsDmur.zip (49,5KB)


Меню 

Вопрос: Почему во время интерпретации меню у меня возникает синтаксическая ошибка, хотя в тексте программы никакой ошибки нет? 

Ответ: Если верхняя линейка меню, содержит пункты, в которых переопределены клавиши быстрого вызова (ALT+…) на русские буквы, то во время интерпретации такого меню возникает синтаксическая ошибка. Можно предложить следующие способы устранения:
- Изменить, если возможно, содержание пунктов так, чтобы переопределямые клавиши быстрого вызова совпали с первыми буками самих пунктов, при этом
. не следует использовать символы выделения \<
. в ALT+... указать латинский аналог
- Переопределить функцию обработки ошибок, игнорируя данную ошибку (в setup меню переустановит функцию обработки ошибок с сохранением прежней функции, в cleanup восстановить прежнюю). В этом случае, при попытке воспользоваться клавишами быстрого вызова не происходит активизация пункта, в то время как система выдаёт звуковой сигнал.


ООП 

Вопрос: С чего начать и как практически использовать объектный подход при программировании в 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


Код программ 

Вопрос: Почему exe-модуль созданный в Visual FoxPro "не работает" при попытке запустить его на машине, где не установлена полная версия Visual FoxPro, а только RunTime-овская часть его? 

Ответ: Я больше чем уверен, что этот же модуль не будет работать и там, где установлена полная версия, если запустить его не из-под оболочки Visual FoxPro. Попробуйте, и если это действительно так, то причина проста. В этом модуле следует организовать бесконечный цикл для обработки событий, происходящих в нём, и прерываемый по команде завершения работы модуля. Делается это командами Read Events и Clear Events соответственно.

Вопрос: Я экспериментально установил, какой набор файлов следует поместить в каталог вместе с exe-модулем, созданным в Visual FoxPro, чтобы он корректно работал на клиенте без установки RunTime-овской части Visual FoxPro. Правильно ли я сделал, не будет ли у меня проблем в будущем? 

Ответ: Думаю, что у Вас будут проблемы, когда Вы понесёте ещё один, созданный Вами модуль тому же клиенту, а возможно и новую версию прежнего модуля, которая, к примеру, начнёт использовать ODBC-драйвер Visual FoxPro. Поэтому, лучший вариант переноса, - создание дискрибутива, используя Wizard Setup самого Visual FoxPro. Правда иногда он работает с ошибкой, которая заключается в том, что в информационных файлах дискрибутива регистрируются файлы, которые на самом деле не копируются на дискрибутивные диски. Для устранения этой проблемы следует вручную отредактировать эти файлы, приведя их в соответствие с тем, что на самом деле находится на дискрибутивных дисках.

Вопрос: Я создаю приложение VFP 6.0 используя Application Wizard. Почему теперь, когда я открываю созданный таким образом проект приложения из проводника Windows у меня недоступны все Помощники (Wizard) и Построители (Builders)? Tо же самое я имею, если создам иконку приложения на DeskTop Windows (Shortcut) и открою своё приложение через неё. 

Ответ: Откройте файл Config.fpw в каталоге Вашего проекта и на время разработки проекта закомментируйте строки _BROWSER = "", ... , _WIZARD = "". Возможно Вам также потребуется изменить установку RESOURCE = OFF.

Вопрос: Как "убить" Main Visual FoxPro Window? Создаю форму, устанавливаю ShowWindow=2 (As Top-Level Form)... 

Ответ: Поместите SCREEN=OFF в Ваш Config.fpw

Вопрос: Как решать проблему хранения паролей для доступа к программе. Имеется ли простой способ сделать это? Для паролей я имею dbf-файл (пользователь, пароль). Хотел бы быть иметь возможность шифровать/расшифровать поле пароль.  

Ответ: Используйте для хранения/проверки только результат функции SYS(2007, cExpression), самый простой способ, как надеюсь, можете немного "подфонить" (в VFP 6.0 по крайней мере).

Вопрос: Где найти информацию о проблемах 2000 года для FoxPro? 

Ответ: Проблемы K2Y описаны здесь http://www.microsoft.com/technet/year2k/product/product.asp (eng.) http://www.microsoft.com/technet/year2k/product/user_list.asp(eng.) или http://www.microsoft.com/rus/year2000/prodguide/product.asp (rus.) http://www.microsoft.com/rus/year2000/prodguide/user_list.asp (rus.) см. также Y2KFOX на http://www.y2kfox.com (eng.) или DateHound на http://www.digitalcoyote.com (eng.) и http://msdn.microsoft.com/vfoxpro/technical/tutorial/dates.asp (eng.)

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

Ответ: Попробуйте что-нибудь подобно следующему:
#DEFINE MY_APP_CAPTION "Application Window"
#DEFINE SW_SHOWNOACTIVATE	4
DECLARE INTEGER SetForegroundWindow IN Win32API ;
    LONG hWnd
DECLARE INTEGER IsIconic IN Win32API;
    LONG hWnd
DECLARE LONG FindWindow IN Win32API ;
    STRING lpClassName;
    ,STRING lpWindowName
DECLARE LONG ShowWindow  IN Win32API ;
     LONG hwnd ;
    ,LONG nCmdShow
LOCAL lhWnd
lhWnd = FindWindow(NULL, MY_APP_CAPTION)
IF lhWnd # 0
	IF IsIconic(lhWnd) # 0
    		ShowWindow(lhWnd, SW_SHOWNOACTIVATE)
	ENDIF
	SetForegroundWindow(lhWnd)
	CLEAR DLLS
	QUIT
ENDIF

Вопрос: Создаю VFP-приложение с одной формой (ShowWindow - 2 as Top-Level Form), делаю для него Setup, устанавливаю на клиенте Windows 95..., по завершению приложения задача виснит. Имеется ли способ избавиться от этого? 

Ответ: Попробуйте установит DCOM на машине клиента. Имейте ввиду, что существуют две версии Dcom95.exe и Dcom98.exe. См. также
Q193472 FIX: Exception Error Exiting Top-Level Form App in Windows 95
Q215362 HOWTO: Detect DCOM Installation Under VFP 6.0
http://fox.wikis.com/wc.dll?Wiki~C0000005ExError~VFP

Вопрос: Как из VFP отследить завершение процесса? 

Ответ: см. ID: Q191584 в MSDN "HOWTO: Determine When a 32-bit Process has Completed"

Вопрос: Нужно из кода переключать раскладку клавиатуры: Rus/Lat. Можно ли это сделать? 

Ответ: Попробуйте что-нибудь подобно следующему:
#DEFINE hklRus			"00000419" 	&& - name from 0x0419 Russian 
#DEFINE hklEng			"00000409" 	&& - name from 0x0409 English (US)
#define KLF_ACTIVATE	1			&& 0x00000001

DECLARE LONG LoadKeyboardLayout IN WIN32API ; 
	STRING pwszKLID, ; && input locale identifier
	INTEGER Flags      && input locale identifier options

LOCAL lnRetCode
lnRetCode = LoadKeyboardLayout(hklRus, KLF_ACTIVATE)
lnRetCode = LoadKeyboardLayout(hklEng, KLF_ACTIVATE)

CLEAR DLLS

Вопрос: Как поместить картинку в VFP main window? 

Ответ: Попробуйте что-нибудь подобно следующему:
WITH _SCREEN
	.AddObject( "MyPic", "Image" )
	.MyPic.Picture = HOME()+"Fox.bmp"
	.MyPic.Top     = 10
	.MyPic.Left    = 10
	.MyPic.Visible = .T.
ENDWITH
*
*... а для удаления:
*
 _screen.RemoveObject("MyPic") 

Вопрос: Есть ли библиотека локализации для VFP6 ? Такая как в пятом vfp5rus.dll. 

Ответ: в SP4 VS6 в файле VS6sp47.cab содержится vfp6rrus.dll (у меня, по крайней мере)...

Вопрос: Возможно ли как-нибудь работать с Си-структурами, получаемыми через Win32Api функций в VFP? 

Ответ: На http://www.universalthread.com/ (регистрация свободна) разделе Files лежит Struct.zip автор Christof Lange, который позволяет это делать.

Вопрос: У меня не закрывается приложение по [x]. Как это побороть? 

Ответ: см. ID: Q172455, Q110970, ON SHUTDOWN в MSDN. Как добавление, если Вы желаете, чтобы Ваше приложение нормально завершалось по закрытию OS [CTRL+ALT+DELETE], добавьте в конец Вашей процедуры MyCleanUp, вызывающейся по событию ON SHUTDOWN, что-нибудь типа:
PROCEDURE MyCleanUp 
ON SHUTDOWN
...
IF VERSION(2) = 0
	QUIT
ENDIF

Вопрос: Использую HOME()+'tools/GenDbc/GenDbc.prg' для генерации dbc-файлов, однако получаю безобразие, подобное:

...
DisplayStatus([Creating table СПРАВОЧНИК_МАРОК_РАЗЪЕДИНИТЕЛЕЙ...])
MakeTable________________________________()
...
можно ли это как-то побороть? 

Ответ: Попробуйте в GenDbc.prg в качестве PROCEDURE FixName(...) написать "жёстко", чего-нибудь типа:
PROCEDURE FixName(lcProcName) 
	lcProcName=ALLTRIM(lcProcName) 
	cbadchars = '/,-=:;!@#$%&*.<>()?[]'+; 
		'+'+CHR(34)+CHR(39)+" " 
	lcProcName = CHRTRANC(lcProcName,cbadchars ,REPL('_',LEN(cbadchars))) 
	RETURN lcProcName 
ENDPROC 

Вопрос: Попытка воспользоваться решением Q194702 "HOWTO: Locate Windows Special Folder Locations" из MSDN ни к чему не привела. Имеется ли альтернативное решение? 

Ответ: Попробуйте использовать вот это:
o = CreateObject('wscript.shell')
for i = 1 to 17
? o.SpecialFolders(i)
next

Вопрос: Имеется ли возможность перехватить события VFP main window (_SCREEN) из своей программы?  

Ответ: Один способ показан в Desktop.zip на стр. примеры, другой в создании своего класса подобно тому как показано ниже:
* ScreenMethods.PRG
*
* Author: Fred Taylor - 4/10/2001
*
* Use the following to modify _SCREEN methods:
*
* For VFP6 & 7:
*
*     _SCREEN.NewObject("oSH","ScreenHook","screenmethods.prg")
*
* For VFP3 & 5:
*
*     SET PROCEDURE TO screenmethods ADDITIVE
*     _SCREEN.AddObject("oSH","ScreenHook")
*
* Any of the main VFP screen methods can be hooked into in this manner.
*
DEFINE CLASS ScreenHook AS CUSTOM
oScr = _SCREEN
PROCEDURE oScr.Resize()
  *
  * Code to handle the main VFP screen being resized
  *
  WAIT WINDOW NOWAIT TRANSFORM(this.Width)+" "+TRANSFORM(this.Height)
ENDPROC
PROCEDURE oScr.RightClick
  *
  * Code to do a "shortcut" menu on main VFP screen RightClick
  *
  DO testmenu.mpr
ENDPROC
*
* Custom methods work, too.
*
PROCEDURE oScr.MyMethod
wait window "my method fired!"
ENDPROC
ENDDEFINE
Обратите внимание, что в последнем случае ряд событий недоступно, в частности QueryUnload()

Вопрос: Как определить високосный год? 

Ответ: !EMPTY(DATE(m.lnYear, 2, 29))

Вопрос: Как создать setup VFP-приложения в версии 7.0 и выше? Не могу найти пункт Tools/Wizards/Setup 

Ответ: Это действительно так: построитель setup перестал входить в среду разработки. Вместо этого, на установачном CD распростроняется InstallShield Express. Обратите внимание, он не устанавливается автоматично при установке VFP среды и требует отдельной/самостоятельной установки на машину разработчика. См. 'Where is the Setup Wizard?' and Other Annoying InstallShield Questions в MSDN.
  - об использовании InstallShield Express см. документацию, а также статьи в MSDN (в поиске по "InstallShield Express")
  - какие именно dll-библиетеки требуется устанавливать клиенту и какие требуют регистрации как COM-компоненты см. на
   http://fox.wikis.com/wc.dll?Wiki~VFP7RuntimeFiles~VFP,
   http://fox.wikis.com/wc.dll?Wiki~VFP8RuntimeFiles~VFP,
   http://fox.wikis.com/wc.dll?Wiki~VFP9RuntimeFiles~VFP
  - см. также в MSDN KB: [Q320151] How to determine dependencies with InstallShield Express
  - см. также вопросы связанные с установкой/запуском приложений на клиенте на http://forum.foxclub.ru/


Отчёты 

Вопрос: Можно ли из отчёта, полученного средствами создания отчёта в Visual FoxPro получить "полнофункциональный текстовый документ" (шрифты, и т.д.)? 

Ответ: Напрямую нет. Средствами отчёта можно получить только чисто текстовый документ (опция ANSI в команде REPORT). Если Вам необходимо получить "полнофункциональный документ" какого-либо продукта, например Word, Вам следует использовать OLE-механизм именно этого продукта для оформления своих результатов из Visual FoxPro.

Вопрос: Имеется ли возможность автоматически максимизировать окно предварительного просмотра отчёта? 

Ответ: Попробуйте:
#DEFINE WND_PRVREP "Report"
IF WEXIST(WND_PRVREP)
    ZOOM WINDOW (WND_PRVREP) MAX
ENDIF
где-нибудь в событии MyReport.DataEnvironment.Init(). Обратите внимание, что это не будет работать в приложении, построенном средствами Application Wizard в VFP 6.0, однако там и нет этих проблем.

Вопрос: Желаю из VFP напечатать текстовый файл, но у меня чего-то не получается :-( Это можно как-нибудь сделать? 

Ответ: Попробуйте что-нибудь подобное следующему
#DEFINE C_FILENAME	"D:\MyApp\Vfp\Picture.prg"
#DEFINE C_OUT_PORT	"\\Server\Printer1"
*
******************* Or:
*#DEFINE C_OUT_PORT	"PRN:"  &&<- default
*#DEFINE C_OUT_PORT	"LPT1:" &&<- LPT1
******************* 
*
DECLARE INTEGER CopyFile IN KERNEL32.DLL ;
	STRING lpExistingFileName,; && name of an existing file
	STRING lpNewFileName,;      && name of new file
	INTEGER bFailIfExists       && operation if file exists

LOCAL lbRetVal
IF CopyFile(C_FILENAME, C_OUT_PORT, 0) = 0
	ACTIVATE SCREEN
	?CHR(7)
	=MessageBox('Can not print file', 16, _SCREEN.Caption)
ELSE
	lbRetVal = .T.	
	WAIT WINDOW 'Ok!' NOWAIT
ENDIF
CLEAR DLLS
RETURN lbRetVal

Вопрос: При REPORT FORM ... TO PRINTER получаю ошибку: 1958 Error loading printer driver. Как побороть? 

Ответ:
- В Report designe воспользуйтесь пунктом меню: File->Page Setup, Print Setup..., OK, OK.
- См. также раздел "HOWTO: Control Printer Attributes for a Report at Run Time" в MSDN,
- наконец попробуйте вычистить поля TAG и TAG2 используя
USE Myreport.frx  && Open the FRX as a table
LOCATE FOR Objtype = 1 AND Objcode = 53  && find the record that holds
** the printer information. For more information on the Table Structure
** of an .FRX file, see 'Table Structures of Table Files' in Help.
REPLACE Tag WITH ""  && Remove any Printer codes that may be stored in
** the Tag memo
REPLACE Tag2 WITH ""  && Remove any Printer codes that may be stored in
** the Tag2 memo

Вопрос: Есть желание выводить на печать линии из VFP но не могу понять, как это можно сделать. Возможно ли такое?  

Ответ: Попробуйте посмотреть Q255744 раздел "HOWTO: Obtain a Device Context Handle for a Print Device" в MSDN

Вопрос: Не получается завершить сеанс после предварительного просмотра отчёта. Как побороть? 

Ответ: См. в MSDN:
Q179605 PRB: Cannot Quit Visual FoxPro When Previewing Report in a DESKTOP Window
Q156237 PRB: Report Designer/Preview Needs VFP Desktop to Display
Q190069 HOWTO: Showing Print Preview as MDI Child of Top-Level Form
Q188887 HOWTO: How to Display Print Preview in a Top-Level Form

Вопрос: Можно ли выбрать принтер при печати отчёта? 

Ответ: Выполните команды:
REPORT ... to printer PROMPT
SET PRINTER ON PROMPT
см. также SYS(1037), GETPRINTER(), APRINTERS(), ...


Прочие вопросы 

Вопрос: У меня разрушились Темы (Subset) в MSDN Library и у меня не получается их восстановить. Я даже переустановил MSDN Library, но через некоторое время они снова разрушились. Подскажите, как я могу их (пере)устанавливать. 

Ответ: Проделайте следующее: Выберите пункт меню View\Define Subset... в возникшем диалоге определите Вашу тему (subset), обратите внимание на текстовое поле "Save new subset as:" - именно оно позволяет дать название вашей теме подобно "*Visual FoxPro Documentation".
 
 
Hosted by uCoz