Visual FoxPro 9.0 позволяет применять расширенные атрибуты метаданных для членов класса в Конструкторе классов, используя XML в формате  документов, определяемом  схемой VFP данных.  Система отчетов Visual FoxPro не используется совместно с Конструктором Классов, однако она использует схемы данных для аналогичных целей. Использование расширений  данных в Отчетах позволяет:

Этот раздел описывает компоненты схемы данных, используемые для отчетов, приводятся примеры использования данных XML документов при создании и выполнении отчетов.

Построение отчета данных XML документа

Данные отчета, как и класс Design MemberData, содержат последовательность элементов корневого узла VFPData. Чтобы заполнить список схемы совместно используемых данных (.xsd), смотри MemberData Extensibility. Приведенная ниже таблица описывает атрибуты, используемые при работе с отчетами.

Note Замечание

Схема позволяет пользователю добавлять атрибуты, не определенные явно. Любые создаваемые инструменты  разбора или создания  XML документа должны давать возможность чтобы дополнительные или неизвестные атрибуты присутствовали для одного или нескольких элементов в последовательности. Эти инструменты не должны иметь обязательных атрибутов, схема должна иметь возможность пропускать один или более элементов . Пример, приведенный в этом разделе, предусматривает именно такой способ действия.

Колонка Style отчета и  файла описания этикетки (.frx and .lbx tables) зарезервирована для хранения  Report MemberData. Для дополнительной информации по структуре отчетов и этикеток, смотри Понимание структуры отчетов.

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

Название узла Тип узла Родитель- ский узел Замечения по использованию

VFPData

Элемент

(обязательный)

Нет

Корневой узел VFP данных документа

reportdata

Элемент

(обязательный)

VFPData

Элемент содержит метаданные для конкретной записи таблицы описания отчета или  этикетки.

name

Атрибут

(обязательный)

reportdata

Взаимодействует с данными Конструктора Классов. Может быть использован с типом атрибута для управления записями данных отчета в базах данных, например в FoxCode.dbf. Для дополнительной информации см. Системная переменная _FOXCODE.

NoteЗамечание

Хотя атрибут обязательный, допускается нулевое(пустое) значение атрибута.

type

Атрибут

reportdata

Используется с данными Конструктора Классов. Может быть использован с именем атрибута для управления записями в базах данных.

NoteЗамечание

Когда Построитель Отчетов создает отчет данных, он устанавливает значение типа атрибута "R", чтобы различать эти записи от других  типов данных.

script

Атрибут

reportdata

Используется с данными Конструктора Классов, так как это делается  при создании классов,  на этапе создания расширений отчетов.

class

Атрибут

reportdata

Определяет имена классов при использовании в следующих случаях:

  • Шаблон при конструировании, из которого расширения Построителя Отчетов могут использовать атрибуты. Расширения Построителя Отчетов, которые используют этот метод,  передают атрибуты из шаблонного класса элементу этикетки или отчета.

  • Класс helper для конструирования элементов Отчета или Этикетки. Расширения Построителя Отчетов используют этот метод, чтобы установить нужное взаимодействие этих классов и  активизировать требуемый метод для соответствующего события при конструировании. Класс helper осуществляет доступ к  частной сессии данных объекта Report Builder и к копии таблицы отчета или этикетки.

  • Класс helper может взаимодействовать со скриптами при конструировании отчета. Построитель Отчетов, использующий такой подход, может отправить скрипт в класс helper на обработку, или он может выполнять скрипт непосредственно, передав ссылку объекту класса helper в качестве параметра скрипта.

  • Шаблонный класс задается при выполнении отчета с помощью ReportListener.  ReportListener может передавать атрибуты динамически из шаблонного класса , описывая набор атрибутов, которые должны быть переданы. Такой подход позволяет изменять все элементы макета, во всех отчетах,  изменяя соответствующие элементы шаблонного класса, эмулируя таким образом наследуемый класс .

  • Класс helper может быть активизирован с помощью ReportListener при выполнении определенного элемента макета отчета и связан с выполнением скрипта.

classlib

Атрибут

reportdata

Определяет имя библиотеки классов или файла процедуры (.vcx or .prg), из которых  helper или шаблонный класс устанавливает значения. Расширение принимает значение  vcx, если атрибут не включен. Следует отметить, что библиотека классов или файл процедуры должны иметь соответствующее расширение. Для общего понимания того, как Visual FoxPro находит определение класса, обратитесь к примечаниям раздела SET CLASSLIB Command.

declass

Атрибут

reportdata

Определяет как хранить имя  класса DataEnvironment в visual class library(.vcx), используемого как шаблон для Курсора и связанных записей в отчете или этикетках при реализации Построителем Отчетов события  Load DataEnvironment. Построитель Отчетов записывает код, связанный с  экземпляром этого класса, в событие DataEnvironment  отчета. Для дополнительной информации, смотри Как:Загрузитьd Data Environment sдля отчетов.

NoteЗамечание

 Построитель Отчетов использует запись заголовка (первую запись) в отчете или таблице описания этикетки для хранения информации класса DataEnvironment , так как эта информация является общей для отчета. Следует придерживаться этого правила, т.е. хранить общие (глобальные) данные в первых записях  документа XML документа. В примере, приведенном в этом разделе, a run-time reporting extension opts проверяет эту запись  скриптом, соответствующим событиям BeforeReport and AfterReport. Эти события  являются глобальными по своей сути и не связаны с каким-либо конкретным отчетом или этикеткой.

declasslib

Атрибут

reportdata

Предназначен для хранения имени файла библиотеки классов или процедуры, из которых происходит обращение к классу DataEnvironment, если атрибут declass содержит имя класса   DataEnvironment, как описано выше. В качестве альтернативы, Построитель Отчетов дает возможность использовать записи  DataEnvironment из других отчетов или этикеток (файлы .frx or .lbx) в  отчете. Если выбран этот метод, Построитель Отчетов сохраняет имя отчета или этикетки, используемых в качестве шаблона DataEnvironment, в этом атрибуте.

execute

Атрибут

reportdata

Определяет скрипт, используемый при выполнении отчета.

execwhen

Атрибут

reportdata

Определяет условия, при которых  скрипт атрибута execute должен быть выполнен.

NoteПодсказка

Как показано в приведенном примере программного кода в этом  разделе,условия  execwhen могут быть вычислены гибко, а не как условие Print When  в отчете, когда они должны выполняться напрямую а зависимости от логического результата.

Конструирование отчета XML данных

В соответствии с вышеприведенной таблицей, Построитель Отчетов использует атрибуты  declass и declasslib Отчета XML данных, хранящиеся в заголовке отчета или файле описания этикетки. По умолчанию, он не использует какие-либо другие данные, хранящиеся в отчете или этикетке. Однако, можно воспользоваться расширенной архитектурой Построителя Отчетов, чтобы читать и обрабатывать  XML документы.

Представленный класс реализует механизм Построителя Отчетов, называемый  exit handler . Этот класс получает информацию о событии Конструктора Отчетов, устанавливающего, произведены ли какие либо изменения текущего  элемента макета отчета. Если элемент макета содержит текст, и изменения произведены, обработчик объекта проверяет наличие шаблонного класса в  XML данных. Если таковой существует, и имеет свойство с именем  Fontnameобработчик запрашивает пользователя, должен ли быть фонт шаблонного класса применен к элементу макета отчета.

Note Замечание

Для дополнительной информации о регистрации пользовательских обработчиков и фильтров в таблице регистрации Приложенияя Построителя Отчета и и установки требуемого API, смотри Таблица регистрации обработчика событий Построителя Отчетов. Использование механизма exit handler очень удобно, но иллюстрирует только часть возможностей, которыми вы можете воспользоваться, применяя взаимодействие события hooks Конструктора Отчета и Отчет XML данных.

  CopyCode imageCopy Code
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 может выбрать нужные столбцы в отдельный собственный курсор с дополнительными столбцами для динамических изменений, которые ему понадобятся при выполнении для собственных целей.

NoteЗамечание

Чтобы определить соответствующий курсор, созданный  FXMemberData или аналогичным объектом, другие объекты могут обратиться к FRXDataSession для поиска курсора с корректной структурой, или разобрать XML документ самостоятельно, если таковой не найден. Как вариант, следует соблюдать простое условие, представленное свойством   MemberDataAlias объекта ReportListener, содержащим соответствующий алиас. Обратите внимание, что FXMemberData использует метод AddProperty Method , чтобы присоединить это свойство к любому объекту  ReportListener.

Второй класс, производный от FXMemberData, называется FXProcessMemberDataScript и демонстрирует методику использования атрибутов  Execute и ExecWhen Отчета XML данных. Этот класс оценивает состояние ExecWhen и определяет момент запуска скрипта в Execute. Если определено, что следует начать выполнение скрипта, проверяется, является ли первой строкой скрипта строка  PARAMETERS или LPARAMETERS. Если нет, оператор  LPARAMETERS  записывается в начало скрипта, чтобы позволить пропуск всех параметров, полученных с помощью метода  ApplyFX  класса effect API, который позволяет объектам класса  effect обрабатывать и настраивать все параметры событий отчета. Оператор LPARAMETERS создает включения и ссылки на объект FX в качестве первого параметра, до того как все параметры получены в методе ApplyFx. При выполнении этих настроек используется EXECSCRIPT( ) Function для запуска скрипта, не передавая при этом параметры скрипту.

  CopyCode imageCopy Code
* при использовании этого класса
* необходимо следовать образцу ,
* иллюстрирующего использование класса 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).

Note Подсказка

Обратите внимание, что атрибут ExecWhen  указывает на необходимость выполнения скрипта в этих двух случаях  специальными разделительными знаками ("|EvaluateContents|Render|"). Это один из нескольких возможных способов определения условий ExecWhen, предусмотренных в классе FXProcessMemberDataScript.

  CopyCode imageCopy Code
<VFPData>
<reportdata name="" type="R" script="" 
execwhen="|EvaluateContents|Render|"
execute=
"DO CASE 
 CASE ATC(&quot;Render&quot;,tcProgram) &gt; 0
  * 7-й параметр Render - cContentstoBeRendered
  * обратите внимание на преобразование из  Unicode в DBCS
  tP7 = VAL(STRCONV(tp7,6))
  IF tp7 &lt; 0
     tP7 =  &quot;(&quot;+TRANS(ABS(tp7)) + &quot;)&quot;
  ELSE
     tP7 = TRANS(tP7)
  ENDIF
  * преобразование обратно в Unicode для использования в ReportListener:
  tP7 = STRCONV(tp7,5)
OTHERWISE
  * второй параметр EvaluateContents - objProperties
    IF VARTYPE(tP2.value) = "N" AND ;
      tP2.value &lt; 0
      tP2.penred = 255
      tP2.penblue = 0
      tP2.pengreen = 0
      tP2.reload = .T.
   ENDIF
ENDCASE"
class="" classlib="" declass="" declasslib=""/>
</VFPData>
Note Подсказка

Формально корректный  XML документ, приведенный выше, содержит набор заменяемых объектных ссылок ( escaped character references) в скрипте; например, вместо символа  < должна быть использована объектная ссылка &lt; если этот символ является частью значения атрибута или корневого элемента XML. Когда вы создаете скрипт, используя окно для ввода текста интерфейса Run-time Extensions Построителя Отчетов, можно вводить эти символы как обычно, без использования объектных ссылок. Построитель Отчетов сохраняет документ должным образом, преобразуя символы так, как это необходимо, когда вы сохраняете XML документ.

Класс FXMemberDataAware является следующим классом, соответствующим FX API. В отличие от класса FXMemberData и его производных классов, FXMemberDataAware не понимает XML и не может читать XML данные напрямую. Вместо этого он работает с курсором, созданным классом FXMemberData и понимает его структуру. Если класс FXMemberDataAware находит курсор, он может работать с ним, добавляя нужные столбцы. Если нет, он обеспечивает создание временного экземпляра класса FXMemberData при отработке метода BeforeReport, чтобы этот временный объект мог создать курсор.

FXMemberDataAware является абстрактным классом, не выполняющим никаких функций при выполнении отчета. Однако, вы можете создать множество производных FX-классов на основе класса FXMemberDataAware, каждый для специфических целей. Если вы добавляете экземпляры FX-класса в набор FXListener, все они могут использовать общий курсор данных при выполнении отчета. Они также могут создавать собственные курсоры расширения данных, связанные с общим курсором.

  CopyCode imageCopy Code
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

См. также