В данной статье описывается, как заменить стандартный контейнер предварительного просмотра отчетов вашим собственным компонентом, который может обеспечить функциональными возможностями просмотра отчетов всему вашему приложению, если у вас установлен SET REPORTBEHAVIOR 90.

Предварительно ознакомьтесь со статьями

Наилегчайший Просмотр

Самый легкий из возможных примеров вообще не использует API Контейнер Просмотра (Preview Container API), но использует методы объекта ReportListener для демонстрации внутреннего механизма API.

Нижеприведенный код определяет объект Shape как место вывода отчета, запускает отчет под управлением другого объекта, в данном случае это - Приемник Отчетов (ReportListener), и использует методы ReportListener для того, чтобы показать первую страницу отчета в заранее определенном месте вывода:

  Скопировать Код
* Определение места визуализации отчета:
_SCREEN.AddObject("canvas","Shape")
_SCREEN.canvas.Width  = 250
_SCREEN.canvas.Height = 300

* Создать объект базового Приемника Отчета (ReportListener) и задать буферизацию всего отчета:
rl = NEWOBJECT("ReportListener")
* устанавливаем: 
* "буферизация всех страниц", 
* "не запускать предварительный просмотр автоматически"
* с помощью задания значения свойству ListenerType
rl.ListenerType = 3 

* Обработка отчета:
REPORT FORM (_SAMPLES+"\solution\reports\colors.frx") OBJECT rl

* Показать 1-ую страницу в месте просмотра:
rl.OutputPage( 1, _SCREEN.canvas, 2 )

После выполнения этого кода в Командном Окне, вы увидите уменьшенное изображение отчета на экране Visual Foxpro.

Обратите внимание:

  • Третий параметр метода OutputPage() объекта ReportListener, равен 2, что означает, дескриптор места вывода, передаваемый вторым параметром, является ссылкой на Visual FoxPro объект, в данном случае - объект управления Shape.

  • Объект Shape не обязательно должен был быть видимым, чтобы показать отчет.

  • Объектами, используемыми в качестве места вывода могут быть либо Container, либо Shape

  • Протаскивание командного окна мышкой над изображением отчета стирает это изображение с экрана,  т.к. когда экран выполняет метод Paint(), он перерисовывает себя ничего не зная о визуализированном отчете.

Реализация Простого Контейнера Пред.Просмотра

Следующий шаг -  заключить выше описанный процесс в класс, который реализует API контейнер пред.просмотра, позволяющий вам повторно использовать логику пред.просмотра

Следующий пример программы, simplepreview.prg, содержит определение класса, который также использует объект Shape в качестве места формирования изображения, только на этот раз на форме и использует API контейнер пред.просмотра:

  Скопировать Код
*-----------------------------------------
* simplepreview.prg
*-----------------------------------------
DEFINE CLASS SimplePreview AS Form
    Caption     = "Щелкните для перехода на след. страницу"
    ListenerRef = .NULL.
    PageNo      = 1
    AllowOutput = .F.

    ADD OBJECT Canvas AS Shape WITH ;
        Top = 12, Left = 8, ;
        Height = 252, Width = 209, ;
        Name = "Canvas"

    PROCEDURE Canvas.Click
        WITH THISFORM
            IF .PageNo < .ListenerRef.OutputPageCount
                .PageNo = .PageNo + 1
                .Refresh()
            ENDIF
        ENDWITH
    ENDPROC

    PROCEDURE SetReport
        LPARAMETER oListenerRef
        THIS.ListenerRef = oListenerRef
    ENDPROC

    PROCEDURE QueryUnload
        IF NOT ISNULL( THIS.ListenerRef )
            THIS.ListenerRef.OnPreviewClose(.F.)
            THIS.ListenerRef = .NULL.
        ENDIF
        THIS.Hide()
        NODEFAULT
    ENDPROC

    PROCEDURE Paint
        IF NOT ISNULL( THIS.ListenerRef )
            THIS.ListenerRef.OutputPage( THIS.PageNo, THIS.Canvas, 2 )
        ENDIF
    ENDPROC

ENDDEFINE

Данный класс обеспечивает выполнение двух методов API контейнеров пред.просмотра: путем явного определения метода SetReport(); и использование метода Show(), уже имеющегося в форме, поскольку она является производной от базового класса Form, который содержит необходимый метод .

Обратите внимание:

  • Данный класс решает проблему перерисовки из предыдущего примера, с помощью добавления кода на событие формы Paint(), чтобы обеспечить перерисовку имиджа отчета, когда потребуется.

  • Объект Reportlistener вызовет метод SetReport(), передав ссылку на самого себя, когда ему потребуется, чтобы контейнер пред.просмотра осуществил основную, не связанную с каким-либо отчетом, инициализацию. SetReport() будет также вызван с пустым параметром (.NULL.), когда вызовется его метод OnPreviewClose().

  • Код в событии QueryUnload()  сообщает приемнику отчета (report listener), что вы завершили просмотр отчета и обнулит ссылку на Приемник Отчета, дабы избежать зависания, которое не позволит закрыться форме.

  • Код в методе Shape.Click() использует свойство OutputPageCount Приемника Отчета (report listener), чтобы застраховаться от попытки просмотра несуществующей страницы отчета.

Следующий код демонстрирует, как повторно использовать класс контейнера просмотра отчетов, определив класс SimplePreview и присвоив его свойству PreviewContainer объекта ReportListener. Запустите этот код в командном окне:

  Скопировать Код
pc = NEWOBJECT("SimplePreview", "simplepreview.prg")
rl = NEWOBJECT("ReportListener")
rl.ListenerType     = 1 && Буферизация всех страниц, использовать контейнер просмотра
rl.PreviewContainer = pc
REPORT FORM (_SAMPLES+"\solution\reports\colors.frx") OBJECT rl
REPORT FORM (HOME()+"Tools\Filespec\60frx2.frx") OBJECT rl

После запуска данного кода, вы увидите, что форма SimplePreview появилась автоматически . Это произошло потому, что объект приемника отчета запустил метод PreviewContainer.Show() после того, как отчет завершил обработку.

Если вы в последнюю строку вышеприведенного примера включите оператор NOWAIT, вы увидите, что форма просмотра больше не является модальной . Объект приемника отчета распознает оператор NOWAIT и вызывает Show(0) вместо Show(1).

Hide() или Release()?

  • В выше приведенной программе SimplePreview.prg, событие QueryUnload() подавляет свое стандартное выполнение командой NODEFAULT и принудительно прячет форму, вызывая метод Hide().

Для этого есть две причины:

  • Все еще открытая ссылка на форму хранится в свойстве PreviewContainer приемника отчета, а это означает, что без дополнительного кода форма не закроется, когда вы нажмете на кнопку Close. Эта кнопка будет недоступной и форма останется видимой.

  • Метод Hide() позволяет контейнеру просмотра  быть повторно использованным при очередных просмотрах отчета, как это показывает предыдущий пример кода.

Если вы не хотите, чтобы ваш контейнер оставался доступным для будущих просмотров отчета, вы можете изменит код метода QueryUnload как показано ниже:

  Скопировать Коде
    PROCEDURE QueryUnload
        IF NOT ISNULL( THIS.ListenerRef )
            THIS.ListenerRef.PreviewContainer = .NULL.
            THIS.ListenerRef.OnPreviewClose(.F.)
            THIS.ListenerRef = .NULL.
        ENDIF
    ENDPROC

Как следствие данного альтернативного выполнения, контейнер выгружается из памяти после одного просмотра. Запустите повторно предыдущий код в командном окне с модифицированным классом контейнера просмотра, и вы увидите, что только первый просмотр использует форму SimplePreview. Второй запуск отчета возвращается к стандартному окну просмотра отчета, потому что объект ReportListener определил, что у него больше нет ссылки в его свойстве PreviewContainer и запрашивает новую через _REPORTPREVIEW.

Масштабирование и Печать

Масштабирование

В предыдущем примере, размеры изображения страницы определялись исключительно размерами объекта Shape. Если вы измените свойство Width объекта Shape и сделаете его в два раза меньше, то тогда вы заметите, что просматриваемый имидж также деформировался.

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

  Скопировать Код
    PROCEDURE Show
        LPARAMETER iMode
        IF NOT ISNULL( THIS.ListenerRef )
            LOCAL nWidthInches, nHeightInches
            nWidthInches  = THIS.ListenerRef.GetPageWidth()/960
            nHeightInches = THIS.ListenerRef.GetPageHeight()/960
            * Допустим: Определяем масштаб как 50% на экране с 96 DPI :
            THIS.Canvas.Height = INT( nHeightInches * 96 * 0.5 )
            THIS.Canvas.Width  = INT( nWidthInches * 96 * 0.5 )
        ENDIF
        DODEFAULT( iMode )
    ENDPROC

Это самое подходящее место для данного кода, потому что ReportListener гарантированно запустит метод контейнера просмотра Show(), в тот момент, когда методы GetPageHeight() и GetPageWidth() вернут корректные значения для заданного отчета, т.к. метод SetReport() будет вызван раньше.

Печать

Вы можете указать, что отчет должен быть распечатан, с помощью вызова метода приемника отчета  OnPreviewClose() с параметром .Т. в момент закрытия формы.

Устанавливаем по Умолчанию

Вы можете подменить стандартный контейнер просмотра на просмотр под управлением объекта. Для этого, добавьте несколько строк в начале программы описания класса SimplePreview:

  Скопировать Код
*-----------------------------------------
* simplepreview.prg
*-----------------------------------------
LPARAMETER loPreviewContainerRef
* передача параметра по ссылке (т.е. косвенно)
loPreviewContainerRef = CREATEOBJECT("SimplePreview")
RETURN

DEFINE CLASS SimplePreview AS Form
:

Теперь эта программа пригодна для определения  _REPORTPREVIEW, и определяет контейнер просмотра, когда бы он ни запрашивался объектом  ReportListener выполняя команду REPORT… PREVIEW в режиме под управлением объекта (т.е. с оператором OBJECT).

Вы можете испытать это в командном окне:

  Скопировать Код
_REPORTPREVIEW = "simplepreview.prg"
SET REPORTBEHAVIOR 90
report form (HOME()+"Tools\Filespec\60frx2.frx") preview

Едем Дальше ...

Смотри Также