Общие сведения о надстройках WPF

.NET Framework включает модель надстроек, которую можно использовать для создания приложений, поддерживающих расширяемость с помощью надстроек. Эта модель позволяет создавать надстройки, которые интегрируются с функциональностью приложения и расширяют ее. В некоторых ситуациях приложениям нужно отображать пользовательские интерфейсы, предоставляемые надстройками. В этом разделе показано, как WPF расширяет модель надстроек .NET Framework, позволяя реализовывать такие сценарии, рассказывается о ее базовой архитектуре, преимуществах и ограничениях.

Необходимые компоненты

Необходимо ознакомиться с моделью надстроек .NET Framework. Дополнительные сведения см. в разделе Надстройки и расширяемость.

Общие сведения о надстройках

Чтобы избежать сложностей повторной компиляции и развертывания приложений для включения новых функциональных возможностей, приложения реализуют механизмы расширяемости, которые позволяют разработчикам (как собственным, так и сторонним) создавать другие приложения с поддержкой интеграции. Наиболее распространенным способом поддержки этого типа расширяемости является использование надстроек (также называемых "подключаемыми модулями"). Примеры реальных приложений, которые обеспечивают расширяемость с помощью надстроек:

  • Надстройки Internet Explorer.

  • Надстройки проигрывателя Windows Media.

  • Надстройки Visual Studio.

Например, модель надстроек проигрывателя Windows Media позволяет сторонним разработчикам реализовать "подключаемые модули", расширяющие возможности проигрывателя Windows Media различными способами, включая создание декодеров и кодировщиков для форматов мультимедиа, изначально не поддерживаемых проигрывателем Windows Media (например: DVD, MP3), а также звуковых эффектов и обложек. Каждая модель надстройки создается для предоставления функций, уникальных для приложения, хотя существует несколько элементов и поведений, общих для всех моделей.

Тремя основными сущностями типичных решений расширяемости являются контракты, надстройки и ведущие приложения. Контракты определяют интеграцию надстроек с ведущими приложениями двумя способами:

  • Надстройки интегрируются с функциональными возможностями, реализуемыми ведущими приложениями.

  • Ведущие приложения предоставляют функциональные возможности для надстроек, с которыми интегрируются.

Чтобы использовать надстройки, ведущие приложения должны найти их и загрузить во время выполнения. Следовательно, приложения, поддерживающие надстройки, имеют следующие дополнительные обязанности:

  • Обнаружение: поиск надстроек, которые соответствуют контрактам, поддерживаемым ведущими приложениями.

  • Активация: загрузка, запуск и установка связи с надстройками.

  • Изоляция: использование доменов приложений или процессов для установления границ изоляции, защищающих приложения от потенциальных проблем безопасности и выполнения, связанных с надстройками.

  • Обмен данными: разрешение надстройкам и ведущим приложениям взаимодействовать друг с другом через границы изоляции путем вызова методов и передачи данных.

  • Управление жизненным циклом объекта: загрузка и выгрузка доменов приложений и процессов ясным, прогнозируемым способом (см. Домены приложений).

  • Управление версиями: гарантия возможности взаимодействия ведущих приложений и надстроек после создания их новых версий.

В конечном счете разработка надежной модели надстройки является нетривиальный задачей. По этой причине .NET Framework предлагает инфраструктуру для создания моделей надстроек.

Примечание.

Более подробные сведения о надстройках см. в разделе Надстройки и расширения среды.

Общие сведения о модели надстроек платформы .NET Framework

Модель надстроек .NET Framework в пространстве имен System.AddIn содержит набор типов, которые упрощают разработку расширений среды с использованием надстроек. Основной единицей в модели надстроек .NET Framework является контракт, который определяет, как ведущее приложение и надстройка взаимодействуют друг с другом. Контракт предоставляется ведущему приложению с помощью специфичного для ведущего приложения представления контракта. Аналогичным образом надстройке предоставляется специфичное для нее представление контракта. Адаптер позволяет ведущему приложению и надстройке обмениваться данными между соответствующими представлениями контракта. Контракты, представления и адаптеры называются сегментами, а набор связанных сегментов составляет конвейер. Конвейеры — это основа, благодаря которой модель надстроек .NET Framework поддерживает обнаружение, активацию, изоляцию в целях безопасности, изоляцию выполнения (используя как домены приложений, так и процессы), обмен данными, управление жизненным циклом и управление версиями.

Эта поддержка в целом позволяет разработчикам создавать надстройки, которые интегрируются с функциональностью ведущего приложения. Однако в некоторых случаях требуется, чтобы ведущее приложение отображало пользовательский интерфейс, предоставляемый надстройками. Так как каждая технология отображения в .NET Framework имеет собственную модель для реализации пользовательских интерфейсов, модель надстроек .NET Framework не поддерживает какую-либо конкретную технологию. Вместо этого поддержка пользовательских интерфейсов в модели надстроек .NET Framework обеспечивается благодаря платформе WPF.

Надстройки WPF

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

  1. Надстройка возвращает пользовательский интерфейс. Надстройка возвращает ведущему приложению пользовательский интерфейс с помощью вызова метода, как это определено контрактом. Этот сценарий используется в следующих случаях.

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

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

    • Надстройка в основном выполняет службу для ведущего приложения и сообщает ему о статусе через пользовательский интерфейс.

  2. Надстройка — это пользовательский интерфейс. Надстройка является пользовательским интерфейсом, как определено в контракте. Этот сценарий используется в следующих случаях.

    • Надстройка не предоставляет службы помимо отображения, например рекламного объявления.

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

Эти сценарии требуют возможности передачи объектов пользовательского интерфейса между доменом ведущего приложения и доменом приложения надстройки. Поскольку модель надстроек .NET Framework основана на удаленном взаимодействии между доменами приложений, передаваемые между ними объекты должны поддерживать удаленную обработку.

Объект, поддерживающий удаленную обработку, является экземпляром класса, который выполняет одну или несколько из следующих функций:

Примечание.

Дополнительные сведения о создании объектов .NET Framework, поддерживающих удаленную обработку, см. в разделе Обеспечение поддержки удаленного взаимодействия объектами.

Типы пользовательского интерфейса WPF не поддерживают удаленную обработку. Для решения этой проблемы модель надстроек .NET Framework расширяется с помощью платформы WPF, которая позволяет отображать в ведущих приложениях пользовательский интерфейс, предоставляемый надстройками. WPF обеспечивает эту поддержку двумя способами: с помощью интерфейса INativeHandleContract и двух статических методов, реализованных классом FrameworkElementAdapters: ContractToViewAdapter и ViewToContractAdapter. На высоком уровне эти типы и методы используются следующим образом:

  1. WPF требует, чтобы пользовательские интерфейсы, предоставляемые надстройками, были классами, которые прямо или косвенно наследуются от FrameworkElement, например формами, элементами управления, пользовательскими элементами управления, панелями макета и страницами.

  2. Если в контракте определяется, что пользовательский интерфейс будет передаваться между надстройкой и ведущим приложением, он должен определяться как INativeHandleContract (а не FrameworkElement). INativeHandleContract — это поддерживающее удаленную обработку представление пользовательского интерфейса надстройки, которое можно передавать через границы изолированных доменов.

  3. Перед передачей из домена приложения надстройки элемент FrameworkElement упаковывается в виде INativeHandleContract с помощью вызова метода ViewToContractAdapter.

  4. После передачи в домен ведущего приложения INativeHandleContract нужно перепаковать в виде FrameworkElement с помощью вызова метода ContractToViewAdapter.

Способ использования INativeHandleContract, ContractToViewAdapter и ViewToContractAdapter зависит от конкретной ситуации. В следующих разделах содержатся сведения о каждой модели программирования.

Надстройка возвращает пользовательский интерфейс

Чтобы надстройка возвращала пользовательский интерфейс в ведущее приложение, необходимо выполнить следующие условия.

  1. Должны быть созданы ведущее приложение, надстройка и конвейер, как описывается в документации .NET Framework Надстройки и расширения среды.

  2. В контракте должен реализовываться интерфейс IContract, а для возвращения пользовательского интерфейса в нем должен объявляться метод с возвращаемым значением типа INativeHandleContract.

  3. Пользовательский интерфейс, передаваемый между надстройкой и ведущим приложением, должен прямо или косвенно наследоваться от FrameworkElement.

  4. Пользовательский интерфейс, предоставляемый надстройкой, необходимо преобразовать из FrameworkElement в INativeHandleContract до его передачи через границу изоляции.

  5. Возвращаемый пользовательский интерфейс необходимо преобразовать из INativeHandleContract в FrameworkElement после его передачи через границу изоляции.

  6. В ведущем приложении отображается возвращенный объект FrameworkElement.

Пример, в котором демонстрируется реализация надстройки, возвращающей пользовательский интерфейс, см. в разделе Создание надстройки, возвращающей пользовательский интерфейс.

Надстройка является пользовательским интерфейсом

Если надстройка является пользовательским интерфейсом, должны выполняться следующие условия.

  1. Должны быть созданы ведущее приложение, надстройка и конвейер, как описывается в документации .NET Framework Надстройки и расширения среды.

  2. Интерфейс контракта для надстройки должен реализовывать интерфейс INativeHandleContract.

  3. Надстройка, которая передается в ведущее приложение, должна прямо или косвенно наследоваться от FrameworkElement.

  4. Надстройка должна преобразовываться из FrameworkElement в INativeHandleContract перед пересечением границы изоляции.

  5. Надстройка должна преобразовываться из INativeHandleContract в FrameworkElement после пересечения границы изоляции.

  6. В ведущем приложении отображается возвращенный объект FrameworkElement.

Пример реализации надстройки, которая является пользовательским интерфейсом, см. в разделе Создание надстройки, являющейся пользовательским интерфейсом.

Возвращение нескольких пользовательских интерфейсов из надстройки

Надстройки часто предоставляют несколько пользовательских интерфейсов для отображения в ведущих приложениях. Например, рассмотрим надстройку, которая является пользовательским интерфейсом и предоставляет ведущему приложению сведения о состоянии также в виде пользовательского интерфейса. Такие надстройки можно реализовать с помощью сочетания методов из моделей Надстройка возвращает пользовательский интерфейс и Надстройка является пользовательским интерфейсом.

Надстройки и приложения браузера XAML

В приведенных примерах ведущее приложение было установленным автономным приложением. Приложения обозревателя XAML (XBAP) также могут размещать надстройки, однако при этом применяются следующие дополнительные требования к сборке и реализации.

  • В манифесте приложения XBAP должна быть специально настроена загрузка конвейера (папок и сборок) и сборки надстройки в кэш приложения ClickOnce на клиентской машине в ту же папку, где находится XBAP.

  • В коде XBAP для обнаружения и загрузки надстроек в качестве расположения конвейера и надстроек должен использоваться кэш приложения ClickOnce для XBAP.

  • Приложение XBAP должно загружать надстройку в специальный контекст безопасности, если она ссылается на свободные файлы, расположенные на исходном узле. Если надстройки размещаются в приложении XBAP, они могут ссылаться только на свободные файлы, расположенные на исходном узле ведущего приложения.

Эти задачи подробно описаны в следующих подразделах.

Настройка конвейера и надстройки для развертывания ClickOnce

Приложения XBAP загружаются в безопасную папку в кэше развертывания ClickOnce и запускаются из нее. Для размещения надстройки в приложении XBAP сборка конвейера и надстройки также должна загружаться в безопасную папку. Для этого нужно настроить манифест приложения для включения и конвейера и сборки надстройки для загрузки. Проще всего это сделать в Visual Studio, хотя сборка конвейера и надстройки должна находиться в корневой папке ведущего приложения XBAP, чтобы среда Visual Studio могла обнаружить сборки конвейера.

Следовательно, в начале нужно создать сборку конвейера и надстройки в корневой папке приложения XBAP, указав выходной путь сборки для каждой сборки конвейера и сборки надстройки. В следующей таблице показаны выходные пути сборки для сборок конвейера и сборки надстройки, которые находятся в том же решении и корневой папке, что и ведущее приложение XBAP.

Таблица 1. Создание выходных путей построения для сборок конвейера, размещаемых в XBAP

Проект сборки конвейера Выходной путь сборки
Контракт ..\HostXBAP\Contracts\
Представление надстройки ..\HostXBAP\AddInViews\
Адаптер надстройки ..\HostXBAP\AddInSideAdapters\
Адаптер приложения ..\HostXBAP\HostSideAdapters\
Надстройка ..\HostXBAP\AddIns\WPFAddIn1

Затем нужно указать сборки конвейера и сборку надстройки в качестве файлов содержимого XBAP в среде VIsual Studio следующим образом.

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

  2. Установка для Действия при построении для каждой сборки конвейера и надстройки значения Содержимое в окне Свойства.

Последним шагом является настройка манифеста приложения для включения файлов сборки конвейера и файла сборки надстройки для загрузки. Файлы должны располагаться в папках в корне кэша ClickOnce, где находится приложение XBAP. Эту конфигурацию можно реализовать в Visual Studio следующим образом:

  1. Щелкните правой кнопкой мыши проект XBAP, выберите Свойства > Публиковать, а затем нажмите кнопку Файлы приложения.

  2. В диалоговом окне Файлы приложения установите для параметра Состояния публикации каждой DLL конвейера и надстройки значение Включить (авто), а для Группы загрузки для каждой DLL конвейера и надстройки — значение (обязательно).

Использование конвейера и надстройки из базовой папки приложения

Если конвейер и надстройка настроены для развертывания ClickOnce, они загружаются в ту же папку кэша ClickOnce, что и приложение XBAP. Чтобы использовать конвейер и надстройку из XBAP, код XBAP должен получать их из базовой папки приложения. Этот сценарий поддерживается благодаря различным типам и элементам из модели надстроек .NET Framework для использования конвейеров и надстроек. Во-первых, путь определяется значением перечисления ApplicationBase. Это значение используется при перегрузках соответствующих элементов надстройки для использования конвейеров, которые включают следующее:

Доступ к исходному узлу ведущего приложения

Чтобы надстройка могла ссылаться на файлы с исходного узла, она должна быть загружена с изоляцией безопасности, эквивалентной ведущему приложению. Этот уровень безопасности определяется значением перечисления AddInSecurityLevel.Host и передается в метод Activate при активации надстройки.

Архитектура надстроек WPF

На самом общем уровне WPF позволяет реализовывать в надстройках .NET Framework пользовательские интерфейсы (которые прямо или косвенно наследуются от FrameworkElement) с помощью интерфейса INativeHandleContract и методов ViewToContractAdapter и ContractToViewAdapter. В результате ведущему приложению возвращается объект FrameworkElement, который отображается в пользовательском интерфейсе ведущего приложения.

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

По существу, WPF не передает пользовательский интерфейс из надстройки в ведущее приложение. Вместо этого с помощью взаимодействия WPF передается дескриптор окна Win32 для пользовательского интерфейса. Таким образом, когда пользовательский интерфейс из надстройки отправляется в ведущее приложение, происходит следующее.

  • На стороне надстройки WPF получает дескриптор окна для пользовательского интерфейса, который будет отображаться ведущим приложением. Этот дескриптор инкапсулируется внутренним классом WPF, который наследуется от HwndSource и реализует интерфейс INativeHandleContract. Экземпляр этого класса возвращается методом ViewToContractAdapter и маршалируется из домена приложения надстройки в домен ведущего приложения.

  • На стороне ведущего приложения WPF переупаковывает HwndSource в виде внутреннего класса WPF, который наследуется от HwndHost и использует INativeHandleContract. Экземпляр этого класса возвращается методом ContractToViewAdapter в ведущее приложение.

HwndHost отображает пользовательские интерфейсы, определенные дескрипторами окон, из пользовательских интерфейсов WPF. Более подробную информацию см. в разделе Взаимодействие WPF и Win32.

Таким образом, INativeHandleContract, ViewToContractAdapter и ContractToViewAdapter позволяют передавать дескриптор окна пользовательского интерфейса WPF из надстройки в ведущее приложение, где он инкапсулируется с помощью HwndHost и отображается в пользовательском интерфейсе ведущего приложения.

Примечание.

Так как ведущее приложение получает HwndHost, оно не может преобразовать возвращаемый методом ContractToViewAdapter объект в тип, в котором он реализуется в надстройке (например, UserControl).

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

Преимущества надстройки WPF

Поскольку пользовательские интерфейсы надстроек WPF отображаются в ведущих приложениях с помощью внутреннего класса, производного от HwndHost, такие интерфейсы ограничиваются возможностями HwndHost, которые связаны со службами пользовательского интерфейса WPF, в том числе для работы с макетом, стилями, шаблонами и ресурсам, отрисовки и привязки данных. При этом внутренний подкласс HwndHost в WPF имеет дополнительные возможности, в том числе следующие.

  • Переключение между пользовательским интерфейсом ведущего приложения и надстройки. Обратите внимание, что в программной модели "надстройка является пользовательским интерфейсом" адаптер надстройки должен переопределять QueryContract для поддержки переключения между приложениями независимо от того, имеет ли надстройка полное или частичное доверие.

  • Соблюдение требований по поддержке специальных возможностей для пользовательских интерфейсов надстроек, которые отображаются в пользовательском интерфейсе ведущего приложения.

  • Поддержка безопасного выполнения приложений WPF в разных вариантах использования домена приложений.

  • Предотвращение незаконного доступа к дескрипторам окон пользовательского интерфейса надстройки, когда надстройки выполняются в режиме изоляции безопасности (то есть находятся в изолированной среде безопасности с частичным доверием). Такую безопасность обеспечивает вызов метода ViewToContractAdapter:

    • Для программной модели "надстройка возвращает пользовательский интерфейс" единственным способом передать дескриптор окна для пользовательского интерфейса надстройки через границу изоляции является вызов метода ViewToContractAdapter.

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

  • Предоставление защиты выполнения нескольких доменов приложений. Из-за ограничений, связанных с доменами приложений, необработанные исключения, которые появляются в доменах приложений надстроек, вызывают сбой всего приложения, несмотря на наличие границы изоляции. Однако WPF и модель надстроек .NET Framework предоставляют простой способ обойти эту проблему и повысить стабильность работы приложения. Надстройка WPF, отображающая пользовательский интерфейс, создает класс Dispatcher для потока, в котором выполняется домен приложения, если ведущее приложение является приложением WPF. Все необработанные исключения, которые возникают в домене приложения, можно обнаружить с помощью обработки события UnhandledException из объекта Dispatcher надстройки WPF. Получить объект Dispatcher можно из свойства CurrentDispatcher.

Ограничения надстройки WPF

Помимо преимуществ, которые WPF вносит в поведение по умолчанию объектов HwndSource и HwndHost и дескрипторов окон, для пользовательского интерфейса надстроек, отображаемого в ведущем приложении, также действует ряд ограничений.

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

  • Концепция свободного пространства в сценариях взаимодействия также применяется к надстройкам (см. Общие сведения о технологических областях).

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

  • Пользовательский интерфейс надстройки нельзя поворачивать, масштабировать, наклонять или преобразовывать иным образом (см. раздел Общие сведения о преобразованиях).

  • Содержимое в пользовательском интерфейсе надстройки, которое визуализируется посредством операций рисования из пространства имен System.Drawing, может включать альфа-смешение. Однако как пользовательский интерфейс надстройки, так и пользовательский интерфейс ведущего приложения, которое его содержит, должны быть полностью непрозрачными. Другими словами, для их свойства Opacity должно быть установлено значение 1.

  • Если для свойства AllowsTransparency окна в ведущем приложении, которое содержит пользовательский интерфейс надстройки, установлено значение true, то надстройка будет не видна. Это верно даже в том случае, если пользовательский интерфейс надстройки полностью непрозрачен (то есть свойство Opacity имеет значение 1).

  • Пользовательский интерфейс надстройки должен располагаться поверх других элементов WPF в том же окне верхнего уровня.

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

  • Файлы мультимедиа нельзя воспроизводить в объекте MediaElement в пользовательском интерфейсе надстройки.

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

  • Когда между элементами управления в пользовательском интерфейсе надстройки смещается фокус, ведущее приложение не получает и не вызывает события GotFocus и LostFocus.

  • Часть ведущего приложения, содержащая пользовательский интерфейс надстройки, выглядит пустой при печати.

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

  • Если пользовательский интерфейс надстройки является объектом InkCanvas или содержит InkCanvas, выгрузить надстройку нельзя.

Оптимизация производительности

По умолчанию при использовании нескольких доменов приложений все сборки .NET Framework, необходимые для каждого приложения, загружаются в домен приложения. В результате время, необходимое для создания новых доменов приложений и запуска приложений в них, может повлиять на производительность. Однако .NET Framework предоставляет способ сократить время запуска, требуя от приложений использовать общие сборки, имеющиеся во всех доменах приложений, если они уже загружены. Для этого используется атрибут LoaderOptimizationAttribute, который необходимо применить к методу точки входа (Main). В данном случае необходимо использовать только код для реализации определения приложения (см. Общие сведения об управлении приложением).

См. также