Visual FoxPro 9.0 позволяет применять расширенные атрибуты метаданных для членов класса в Конструкторе классов, используя XML в формате документов, определяемом схемой VFP данных. Система отчетов Visual FoxPro не используется совместно с Конструктором Классов, однако она использует схемы данных для аналогичных целей. Использование расширений данных в Отчетах позволяет:
-
Определять пользовательские инструкции на этапе проектирования для элементов макета отчета, чтобы использовать их посредством события hooks, доступного в Конструкторах Отчетов и Этикеток.
-
Обеспечить динамические инструкции элементов макета отчета, которые могут быть выполнены с помощью объекта, порожденного классом ReportListener во время выполнения программы.
Этот раздел описывает компоненты схемы данных, используемые для отчетов, приводятся примеры использования данных XML документов при создании и выполнении отчетов.
Построение отчета данных XML документа
Данные отчета, как и класс Design MemberData, содержат последовательность элементов корневого узла VFPData
. Чтобы заполнить список схемы совместно используемых данных (.xsd), смотри MemberData Extensibility. Приведенная ниже таблица описывает атрибуты, используемые при работе с отчетами.
![]() |
---|
Схема позволяет пользователю добавлять атрибуты, не определенные явно. Любые создаваемые инструменты разбора или создания XML документа должны давать возможность чтобы дополнительные или неизвестные атрибуты присутствовали для одного или нескольких элементов в последовательности. Эти инструменты не должны иметь обязательных атрибутов, схема должна иметь возможность пропускать один или более элементов . Пример, приведенный в этом разделе, предусматривает именно такой способ действия. |
Колонка Style отчета и файла описания этикетки (.frx and .lbx tables) зарезервирована для хранения Report MemberData. Для дополнительной информации по структуре отчетов и этикеток, смотри Понимание структуры отчетов.
Вы можете использовать имеющийся Построитель Отчетов, чтобы вставить XML данные требуемой структуры в записи отчета и этикетки. Можно проверить результат, используя примеры отчета XML данных. Для дополнительной информации см. Как: Назначить структурные метаданные для элементов управления отчета.
Название узла | Тип узла | Родитель- ский узел | Замечения по использованию | ||
---|---|---|---|---|---|
|
Элемент (обязательный) |
Нет |
Корневой узел VFP данных документа | ||
|
Элемент (обязательный) |
|
Элемент содержит метаданные для конкретной записи таблицы описания отчета или этикетки. | ||
|
Атрибут (обязательный) |
|
Взаимодействует с данными Конструктора Классов. Может быть использован с типом атрибута для управления записями данных отчета в базах данных, например в FoxCode.dbf. Для дополнительной информации см. Системная переменная _FOXCODE.
| ||
|
Атрибут |
|
Используется с данными Конструктора Классов. Может быть использован с именем атрибута для управления записями в базах данных.
| ||
|
Атрибут |
|
Используется с данными Конструктора Классов, так как это делается при создании классов, на этапе создания расширений отчетов. | ||
|
Атрибут |
|
Определяет имена классов при использовании в следующих случаях:
| ||
|
Атрибут |
|
Определяет имя библиотеки классов или файла процедуры (.vcx or .prg), из которых helper или шаблонный класс устанавливает значения. Расширение принимает значение | ||
|
Атрибут |
|
Определяет как хранить имя класса DataEnvironment в visual class library(.vcx), используемого как шаблон для Курсора и связанных записей в отчете или этикетках при реализации Построителем Отчетов события Load DataEnvironment. Построитель Отчетов записывает код, связанный с экземпляром этого класса, в событие DataEnvironment отчета. Для дополнительной информации, смотри Как:Загрузитьd Data Environment sдля отчетов.
| ||
|
Атрибут |
|
Предназначен для хранения имени файла библиотеки классов или процедуры, из которых происходит обращение к классу DataEnvironment, если атрибут | ||
|
Атрибут |
|
Определяет скрипт, используемый при выполнении отчета. | ||
|
Атрибут |
|
Определяет условия, при которых скрипт атрибута
|
Конструирование отчета XML данных
В соответствии с вышеприведенной таблицей, Построитель Отчетов использует атрибуты declass
и declasslib
Отчета XML данных, хранящиеся в заголовке отчета или файле описания этикетки. По умолчанию, он не использует какие-либо другие данные, хранящиеся в отчете или этикетке. Однако, можно воспользоваться расширенной архитектурой Построителя Отчетов, чтобы читать и обрабатывать XML документы.
Представленный класс реализует механизм Построителя Отчетов, называемый exit handler . Этот класс получает информацию о событии Конструктора Отчетов, устанавливающего, произведены ли какие либо изменения текущего элемента макета отчета. Если элемент макета содержит текст, и изменения произведены, обработчик объекта проверяет наличие шаблонного класса в XML данных. Если таковой существует, и имеет свойство с именем Fontname
, обработчик запрашивает пользователя, должен ли быть фонт шаблонного класса применен к элементу макета отчета.
![]() |
---|
Для дополнительной информации о регистрации пользовательских обработчиков и фильтров в таблице регистрации Приложенияя Построителя Отчета и и установки требуемого API, смотри Таблица регистрации обработчика событий Построителя Отчетов. Использование механизма exit handler очень удобно, но иллюстрирует только часть возможностей, которыми вы можете воспользоваться, применяя взаимодействие события hooks Конструктора Отчета и Отчет XML данных. |
![]() | |
---|---|
DEFINE CLASS TemplateObjectHandler AS Custom * определить в качестве exit handler PROCEDURE Execute( oEvent ) IF BITTEST(oEvent.ReturnFlags,1) AND ; INLIST(FRX.ObjType,5,8) AND NOT EMPTY(FRX.STYLE) LOCAL lcAlias, loX lcAlias = "T"+SYS(2015) TRY XMLTOCURSOR(FRX.Style,lcAlias) SELECT (lcAlias) IF NOT EMPTY(Class) AND ; MESSAGEBOX("(Re)apply Font from Template Object?",4) = 6 IF EMPTY(ClassLib) loX = CREATEOBJECT(ALLTRIM(Class)) ELSE loX = NEWOBJECT(ALLTRIM(Class),ALLTRIM(Classlib)) ENDIF IF VARTYPE(loX) = "O" AND ; PEMSTATUS(loX,"Fontname",5) REPLACE Fontface WITH loX.Fontname IN FRX ELSE MESSAGEBOX("Could not apply template.") ENDIF ENDIF CATCH WHEN .T. * некорректный XML документ * или иная ошибка FINALLY IF USED(lcAlias) USE IN (lcAlias) ENDIF SELECT FRX ENDTRY ENDIF RETURN .T. ENDPROC ENDDEFINE |
Выполнение отчета XML данных
Следующий пример демонстрирует эффективность работы системы, использующей способ построения, предложенный в Соглашения по созданию новых типов вывода отчета.
В этом примере суперкласс FXMemberData представляет собой пользовательский класс, реализующий простейший FX API, удобный для вызова из экземпляра класса FXListener , как это описано в данном разделе. FXMemberData является эффективным объектом, способным читать и разбирать Отчет XML данных, размещая результат в курсоре, проиндексированном по колонке FRXRecno
, связывающей каждую запись с элементом отчета или таблицей описания этикетки.
FXMemberData реализует свои функции в самом начале выполнения отчета. Далее при выполнении отчета, если значение элемента набора данных ApplyMemberData
установлено True (.T.
), FXMemberData устанавливает указатель курсора в соответствии с текущим событием отчета, но не производит каких либо иных действий.
Наличие такого рода объекта позволяет избежать необходимости каждому объекту ReportListener запрашивать доступ к метаданным для собственного разбора XML дкумента. Наряду с простым доступом к метаданным в этом общем курсоре, любой объект, использующий данные, посредством SELECT может выбрать нужные столбцы в отдельный собственный курсор с дополнительными столбцами для динамических изменений, которые ему понадобятся при выполнении для собственных целей.
![]() |
---|
Чтобы определить соответствующий курсор, созданный FXMemberData или аналогичным объектом, другие объекты могут обратиться к FRXDataSession для поиска курсора с корректной структурой, или разобрать XML документ самостоятельно, если таковой не найден. Как вариант, следует соблюдать простое условие, представленное свойством |
Второй класс, производный от FXMemberData, называется FXProcessMemberDataScript и демонстрирует методику использования атрибутов Execute
и ExecWhen
Отчета XML данных. Этот класс оценивает состояние ExecWhen
и определяет момент запуска скрипта в Execute
. Если определено, что следует начать выполнение скрипта, проверяется, является ли первой строкой скрипта строка PARAMETERS
или LPARAMETERS
. Если нет, оператор LPARAMETERS
записывается в начало скрипта, чтобы позволить пропуск всех параметров, полученных с помощью метода ApplyFX класса effect API, который позволяет объектам класса effect обрабатывать и настраивать все параметры событий отчета. Оператор LPARAMETERS
создает включения и ссылки на объект FX в качестве первого параметра, до того как все параметры получены в методе ApplyFx. При выполнении этих настроек используется EXECSCRIPT( ) Function для запуска скрипта, не передавая при этом параметры скрипту.
![]() | |
---|---|
* при использовании этого класса * необходимо следовать образцу , * иллюстрирующего использование класса FXListener : LOCAL loPrimaryRL *сдедующие строки описывают возможности * определения класса для FXListener, приведенные в * примере кода в этом разделе * Соглашения по созданию Новых Типов Выводов Отчета : loPrimaryRL = CREATEOBJECT("FXListener") * добавляются наследуемые объекты ReportListeners, если требуется loPrimaryRL.FXs.Add("FXProcessMemberDataScript") * или используется его суперкласс, если не выполняется * скрипт,но вы хотите разобрать XML данные: * loPrimaryRL.FXs.Add("FXMemberData") * добавляются другие объекты класса еffect в набор, * если это требуется * класс effect удобно вызывать * из класса example в FXListener * так как он работает с неизвестными атрибутами * и неизвестными запрашиваемыми данными, * FXMemberData требует, чтобы значения, назначенные вами * для всех пользовательских атрибутов были представлены * XMLTOCURSOR() как строковые. Значения, * не представленные как строковые будут считаться ошибочными. * Такой способ делает возможным * для вас и других пользователей работать со сложными типами данных * с теми же пользовательскими атрибутами * в различных записях FRX. * Когда вы используете не строковое значение, необходимо * переопределить его должным образом для применения в вашем коде. * (Используйте EVAL() или другим способом преобразуйте тип данных * как это необходимо.) DEFINE CLASS FXMemberData AS Custom MemberDataAlias = "" ApplyMemberData = .F. PROCEDURE ApplyFX(toListener, tcProgram,; tP1, tP2, tP3, tP4, tP5, tP6, ; tP7, tP8, tP9, tP10, tP11, tP12) LOCAL liSession, liSelect, llInBeforeReport llInBeforeReport = (ATC("BeforeReport", tcProgram) > 0) IF (llInBeforeReport OR THIS.ApplyMemberData) AND ; (TYPE("toListener.FRXDataSession") = "N" AND ; toListener.FRXDataSession > -1) liSession = SET("DATASESSION") SET DATASESSION TO (toListener.FRXDataSession) liSelect = SELECT() IF llInBeforeReport * вытаскиваем данные из FRX для дальнейшего использования THIS.PullMemberData(toListener) ENDIF IF THIS.ApplyMemberData * этот FX объект * может использовать * полученные данные * или он может сделать их * доступными для других FX объектов * после прочтения SELECT (THIS.MemberDataAlias) THIS.UseMemberData(; toListener, tcProgram,; @tP1, @tP2, @tP3, @tP4, @tP5, @tP6, ; @tP7, @tP8, @tP9, @tP10, @tP11, @tP12) ENDIF SELECT (liSelect) SET DATASESSION TO (liSession) ENDIF ENDPROC PROTECTED PROCEDURE UseMemberData(toListener, tcProgram,; tP1, tP2, tP3, tP4, tP5, tP6, ; tP7, tP8, tP9, tP10, tP11, tP12) LOCAL lnFRXRecno lnFRXRecno = -1 DO CASE CASE ATC(".Before",tcProgram) > 0 OR ATC(".After",tcProgram) > 0 DO CASE CASE RAT("REPORT",UPPER(tcProgram)) = (LEN(tcProgram)-5) lnFRXRecNo = 1 * взять общие(глобальные) данные CASE VARTYPE(tP2) = "N" && Band events lnFRXRecNo = tP2 OTHERWISE * некорректный вызов ENDCASE CASE VARTYPE(tP1) = "N" && Render, другие события lnFRXRecno = tP1 OTHERWISE * некорректный вызов ENDCASE IF NOT SEEK(lnFRXRecno,THIS.MemberDataAlias,"FRXRecno") lnFRXRecno = -1 ENDIF RETURN (lnFRXRecno # -1) ENDPROC PROTECTED PROCEDURE PullMemberData(toListener) LOCAL lcAlias, lcTempAlias, lcAttributes, liIndex, loAttr IF TYPE("toListener.MemberDataAlias") = "C" AND ; NOT EMPTY(toListener.MemberDataAlias) lcAlias = toListener.MemberDataAlias ELSE lcAlias = "M"+SYS(2015) toListener.AddProperty("MemberDataAlias", lcAlias) * "publish" this for others in case they want it ENDIF THIS.MemberDataAlias = lcAlias lcTempAlias = "T" + SYS(2015) CREATE CURSOR (lcAlias) ; (FRXRecno I, Name M, Type M, ; ExecWhen M, Execute M, Class M, ; ClassLib M, DEClass M, DEClassLib M) * мы собираемся взять каждый атрибут независимо от того, * понимаем ли мы назначение столбца или нет, * but we'll start off with the * core set minus script since script attribute is * officially reserved for design-time use lcAttributes = ; "|FRXRecno|ExecWhen|Execute|Class|" + ; "Classlib|Name|Type|DEClass|DEClassLib|" SELECT FRX SCAN FOR NOT EMPTY(Style) TRY XMLTOCURSOR(Style,lcTempAlias) CATCH WHEN .T. * некорректный XML FINALLY IF USED(lcTempAlias) IF RECCOUNT(lcTempAlias) > 0 SELECT (lcTempAlias) FOR liIndex = 1 TO FCOUNT() IF ATC("|"+FIELD(liIndex)+"|",lcAttributes) = 0 ALTER TABLE (lcAlias) ; ADD COLUMN (FIELD(liIndex)) M lcAttributes = lcAttributes + ; FIELD(liIndex) + "|" ENDIF ENDFOR SCATTER MEMO NAME loAttr INSERT INTO (lcAlias) FROM NAME loAttr REPLACE FRXRecno WITH RECNO("FRX") IN (lcAlias) ENDIF USE IN (lcTempAlias) ENDIF ENDTRY loAttr = NULL ENDSCAN SELECT (lcAlias) INDEX ON FRXRecno TAG FRXRecno ENDPROC ENDDEFINE DEFINE CLASS FXProcessMemberDataScript AS FXMemberData ApplyMemberData = .T. PROTECTED PROCEDURE UseMemberData(toListener, tcProgram,; tP1, tP2, tP3, tP4, tP5, tP6, ; tP7, tP8, tP9, tP10, tP11, tP12) IF DODEFAULT(toListener, tcProgram,; @tP1, @tP2, @tP3, @tP4, @tP5, @tP6, ; @tP7, @tP8, @tP9, @tP10, @tP11, @tP12) * Теперь мы позиционированы в корректной * записи родительского класса, * и можем производить действия, базирующиеся на содержимом данных. * Например, если это BeforeReport, * мы можем назначить набор соответствующих * стандартных объектов каждой записи метки или текста, * имеющих доступ к классу или библиотеке классов. * Для каждого события EvaluateContents или Render * мы можем вызвать методы класса * или применить атрибуты фонтов при выполнении отчета. LOCAL loMemberdata, llExecute SCATTER MEMO NAME loMemberdata IF NOT EMPTY(loMemberdata.Execute) IF EMPTY(loMemberdata.ExecWhen) llExecute = .T. ELSE DO CASE CASE ATC(loMemberData.ExecWhen,tcProgram) > 0 * простая обработка события * ExecWhen содержит имя события * Обратите внимание, что каждое событие, присутствующее в скрипте, * потенциально может изменять содержимое * ExecWhen на другое значение ( следующее * событие, во время которого этот скрипт * должен быть обработан ) llExecute = .T. CASE (TYPE(loMemberdata.ExecWhen) = "L") AND ; EVALUATE(loMemberdata.ExecWhen) * ExecWhen содержит логическое выражение * для обработки llExecute = .T. CASE ATC(SUBSTR(tcProgram,RAT(".",tcProgram) + 1),; loMemberData.ExecWhen) > 0 * ExecWhen содержит разделенные строки или события llExecute = .T. ENDCASE ENDIF IF llExecute IF NOT (BETWEEN(ATC("PARAM", ; ALLTRIM(CHRTRAN(loMemberData.Execute,; CHR(10)+CHR(13), ; SPACE(2)))),1,2)) * добавляем оператор параметров, * это необходимо только в начале * обработки FRX записи. loMemberData.Execute = ; "LPARAMETERS toFX, toListener, tcProgram,;"+ ; CHR(13) + CHR(10) + ; "tP1, tP2, tP3, tP4, tP5, tP6,"+; "tP7, tP8, tP9, tP10, tP11, tP12" + ; CHR(13) + CHR(10) + ; loMemberData.Execute REPLACE Execute WITH loMemberData.Execute ENDIF ExecScript(loMemberData.Execute,; THIS, toListener, tcProgram,; @tP1, @tP2, @tP3, @tP4, @tP5, @tP6, ; @tP7, @tP8, @tP9, @tP10, @tP11, @tP12) ENDIF ENDIF ENDIF ENDPROC ENDDEFINE |
Чтобы эффективно использовать скрипт этой сущности, необходимо добавить следующий XML документ в поле или выражение макета отчета, содержащих числовое значение. Этот пример обеспечивает автоматическое изменение цвета для числовых значений меньше 0, используя событие EvaluateContents, и отображает отрицательные числа в круглых скобках, используя Рендеринг (Render method).
![]() |
---|
Обратите внимание, что атрибут |
![]() | |
---|---|
<VFPData> <reportdata name="" type="R" script="" execwhen="|EvaluateContents|Render|" execute= "DO CASE CASE ATC("Render",tcProgram) > 0 * 7-й параметр Render - cContentstoBeRendered * обратите внимание на преобразование из Unicode в DBCS tP7 = VAL(STRCONV(tp7,6)) IF tp7 < 0 tP7 = "("+TRANS(ABS(tp7)) + ")" ELSE tP7 = TRANS(tP7) ENDIF * преобразование обратно в Unicode для использования в ReportListener: tP7 = STRCONV(tp7,5) OTHERWISE * второй параметр EvaluateContents - objProperties IF VARTYPE(tP2.value) = "N" AND ; tP2.value < 0 tP2.penred = 255 tP2.penblue = 0 tP2.pengreen = 0 tP2.reload = .T. ENDIF ENDCASE" class="" classlib="" declass="" declasslib=""/> </VFPData> |
![]() |
---|
Формально корректный XML документ, приведенный выше, содержит набор заменяемых объектных ссылок ( escaped character references) в скрипте; например, вместо символа |
Класс FXMemberDataAware является следующим классом, соответствующим FX API. В отличие от класса FXMemberData и его производных классов, FXMemberDataAware не понимает XML и не может читать XML данные напрямую. Вместо этого он работает с курсором, созданным классом FXMemberData и понимает его структуру. Если класс FXMemberDataAware находит курсор, он может работать с ним, добавляя нужные столбцы. Если нет, он обеспечивает создание временного экземпляра класса FXMemberData при отработке метода BeforeReport, чтобы этот временный объект мог создать курсор.
FXMemberDataAware является абстрактным классом, не выполняющим никаких функций при выполнении отчета. Однако, вы можете создать множество производных FX-классов на основе класса FXMemberDataAware, каждый для специфических целей. Если вы добавляете экземпляры FX-класса в набор FXListener, все они могут использовать общий курсор данных при выполнении отчета. Они также могут создавать собственные курсоры расширения данных, связанные с общим курсором.
![]() | |
---|---|
DEFINE CLASS FXMemberDataAware AS Custom MemberDataAlias = "" HasMemberData = .F. PROCEDURE ApplyFX(toListener, tcProgram,; tP1, tP2, tP3, tP4, tP5, tP6, ; tP7, tP8, tP9, tP10, tP11, tP12) IF ATC("BeforeReport",tcProgram) > 0 THIS.VerifyMemberData(toListener) ENDIF IF ATC("AfterReport",tcProgram) > 0 THIS.DetachMemberData(toListener, .T.) ENDIF ENDPROC PROCEDURE VerifyMemberData(toListener) IF toListener.FRXDataSession = -1 RETURN .F. ENDIF LOCAL loMemberData, liSelect, liSession liSession = SET("DATASESSION") SET DATASESSION TO (toListener.FRXDataSession) liSelect = SELECT() IF (EMPTY(THIS.MemberDataAlias) OR ; (NOT USED(THIS.MemberDataAlias))) * может быть определено в Listener * с помощью FX объектов, * но может быть нет, в этом случае * необходимо использовать временный объект IF (NOT PEMSTATUS(toListener,"MemberDataAlias",5)) OR ; EMPTY(toListener.MemberDataAlias) THIS.MemberDataAlias = "M" + SYS(2015) toListener.AddProperty("MemberDataAlias", ; THIS.MemberDataAlias) ELSE THIS.MemberDataAlias = toListener.MemberDataAlias ENDIF IF NOT USED(THIS.MemberDataAlias) * должен быть общий доступ loMemberData = NEWOBJECT("FXMemberData") loMemberData.MemberDataAlias = THIS.MemberDataAlias loMemberData.ApplyMemberData = .F. loMemberData.ApplyFX(toListener, "BeforeReport") SET DATASESSION TO (toListener.FRXDataSession) IF USED(THIS.MemberDataAlias) * можно продолжать... THIS.AlterMemberDataInfo() ENDIF ENDIF ENDIF THIS.HasMemberData = USED(THIS.MemberDataAlias) SELECT (liSelect) loMemberData = NULL SET DATASESSION TO (liSession) RETURN THIS.HasMemberData ENDPROC PROTECTED PROCEDURE AlterMemberDataInfo() * Hook для производных классов * для добавления собственных столбцов, * или создания собственных курсоров * в FRX Data session , * связанных с общим курсором данных ENDPROC PROCEDURE DetachMemberData(toListener, tlCloseMemberDataTable) IF tlCloseMemberDataTable AND toListener.FRXDataSession > -1 LOCAL liSession liSession = SET("DATASESSION") SET DATASESSION TO (toListener.FRXDataSession) IF USED(THIS.MemberDataAlias) USE IN (THIS.MemberDataAlias) IF PEMSTATUS(toListener,"MemberDataAlias",5) toListener.MemberDataAlias = "" ENDIF ENDIF SET DATASESSION TO (liSession) ENDIF THIS.MemberDataAlias = "" THIS.HasMemberData = .F. ENDPROC ENDDEFINE |