Интеграция в настройки Visual Studio




Эта статья устарела. Обновленную версию этой статьи вы можете прочитать здесь.

Аннотация

В данном разделе рассматривается расширение среды Visual Studio путём интеграции в её настройки пользовательских групп и страниц настроек. Показана регистрация и интеграция в IDE пользовательских страниц настроек с помощью различных видов подключаемых модулей расширений, способы отображения стандартных и пользовательских компонентов в них. Рассмотрены возможности программного доступа к настройкам среды через объектную модель автоматизации Visual Studio, а также механизм сохранения состояния настроек.

Введение

Visual Studio использует единое диалоговое окно для доступа к настройкам различных компонентов среды разработки. Это окно доступно через пункт главного меню IDE Tools -> Options. Базовым элементом настроек Visual Studio является страница настроек. Диалоговое окно Options располагает страницы настроек в древовидной структуре в соответствии с их принадлежностью к различным функциональным группам. Причём каждая из страниц может быть уникально идентифицирована по имени её группы и персональному имени, например страница настроек редактора Visual Basic кода: "Text Editor, Basic".

Модули-расширения имеют возможность читать и модифицировать значения отдельных настроек у зарегистрированных в IDE страниц. Также модули могут создавать и регистрировать собственные пользовательские страницы в среде с использованием объектной модели автоматизации и классов MPF (Managed Package Framework, доступно только для VSPackage модулей). Visual Studio имеет встроенный механизм для сохранения состояния объекта-страницы, который используется по умолчанию, а также может быть переопределён или отключен.

Создание и регистрация пользовательских страниц настроек

При разработке модуля-расширения Visual Studio может оказаться полезным сопоставить его с одной или несколькими пользовательскими страницами настроек в меню Tools -> Options. Подобный инструмент для конфигурации и управления расширением будет соответствовать UI парадигме среды разработки и удобен при работе с расширением непосредственно из IDE. Методы реализации пользовательской страницы настроек, её интеграции в IDE и регистрации будут различаться в зависимости от типа разрабатываемого модуля-расширения и используемой технологии (модель автоматизации или MPF).

Интеграция с использованием MPF классов

Managed Package Framework позволяет создавать пользовательские страниц настроек путём наследования от класса DialogPage. В связи с тем, что среда независимо подгружает страницу настроек при открытии соответствующего раздела в диалоге Tools -> Options, каждая пользовательская страница должна быть реализована в виде отдельного независимого объекта.

Объект, реализующий таким образом пользовательскую страницу настроек, должен быть связан с пакетом расширения Visual Studio (VSPackage) через атрибут ProvideOptionPage подкласса Package.

[ProvideOptionPageAttribute(typeof(OptionsPageRegistration),
"MyPackage", "MyOptionsPage", 113, 114, true)]

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

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<VsVersion>\
ToolsOptionsPages

Здесь <VsVersion> - номер версии Visual Studio, например 10.0. Данная запись будет автоматически создана при использовании атрибута ProvideOptionPage. Стоит также помнить, что для удаления пакета-расширения из Visual Studio потребуется очистить все созданные им записи в системном реестре, в том числе и записи, относящиеся к пользовательским страницам настроек. Начиная с версии Visual Studio 2010 VSPackage модули могут использовать VSIX пакеты для своего развёртывания и деинсталляции, при этом инсталлятор VSIX автоматически записывает или удаляет записи о модуле в реестре. Для более старых версий IDE очистку реестра потребуется провести вручную, например в standalone инсталляторе.

Шестой bool аргумент конструктора атрибута позволяет зарегистрировать пользовательскую страницу как объект автоматизации. Это позволит получить доступ к настройкам, определённым в данной странице через интерфейсы EnvDTE, из сторонних plug-in модулей. Регистрация объекта автоматизации требует создания записей в системном реестре (что происходит автоматически при использовании данных атрибутов) в следующих ветках:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<Version\Packages\
<PackageGUID>\Automation 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\<Version>\
AutomationProperties

Атрибут ProvideProfile позволяет зарегистрировать страницу настроек или любой другой независимый объект, реализующий интерфейс IProfileManager, для использования встроенного IDE механизм сохранения настроек.

Реализация наследования от MPF класса DialogPage

Минимальным требованием к подклассу DialogPage для реализации пользовательской страницы настроек является наличие в классе-наследнике открытых свойств (public properties). Подобная базовая реализация будет выглядеть следующим образом:

namespace MyPackage
{
  class MyOptionsPage : DialogPage
  {
    bool myOption = true;
    public bool MyOption
    {
        get { return this. myOption; }
        set { this. myOption = value; }
    }
  }
}

Данная базовая реализация подкласса DialogPage будет использовать для отображения клиентской области пользовательского окна настроек стандартный PropertyGrid, в котором будут показаны все открытые свойства класса-наследника. Это может оказаться полезным, когда конфигурационные параметры плагина достаточно просты для их отображения и редактирования через стандартные PropertyGrid редакторы. Использование родного средства отображения среды позволит также избежать и распространённых проблем с корректным отображением и масштабированием пользовательских интерфейсных компонентов (например, на различных системных DPI) непосредственно в самом диалоговом окне Visual Studio.

Для отображения в окне настроек пользовательского интерфейса необходимо переопределить свойство Window класса DialogPage:

[BrowsableAttribute(false)]
protected override IWin32Window Window
{
  get
  {
    return MyUserControl;
  }
}

В него необходимо передать ссылку на пользовательский объект IWin32Window, реализующий интерфейс клиентской области окна. Также стоит помнить, что Visual Studio требует от таких окон быть постоянными, т.е. они не должны пересоздаваться в последующих вызовах. Т.к. такие объекты, как Windows Forms, могут в течение своего существования самостоятельно пересоздавать свой оконный handle, желательно использовать здесь ссылку, полученную через объект типа UserControl.

Через свойство AutomationObject пользовательской страницы настроек, унаследованной от класса DialogPage, определяются те открытые свойства, которые будут отображены через стандартные компоненты в окне настроек и к которым будет применён стандартный IDE механизм сохранения состояния. По умолчанию AutomationObject возвращает ссылку на сам подкласс DialogPage, но если он вернёт ссылку на какой-либо другой объект, то открытые свойства именно этого объекта будут использованы для отображения и сохранения состояния настроек. В стандартной реализации системный реестр используется как локальное хранилище настроек. Переопределение метода подкласса DialogPage.SaveSettingsToStorage позволяет изменить способ работы механизма сохранения состояния настроек среды (по аналогии, переопределение метода LoadSettingsFromStorage позволяет изменить работу механизма восстановления настроек).

public override void SaveSettingsToStorage() { ... }

Зарегистрированные как объекты автоматизации страницы могут сохранять своё состояние, по аналогии с сохранением состояния всех остальных страниц настроек, во внешнем XML файле при использовании команд среды Tools -> Import/Export Settings через стандартную реализацию метода SaveSettingsToXml, который при необходимости также может быть переопределён.

Заметим, что интеграция в диалог настроек Visual Studio не является единственным и обязательным методом создания интерфейса для конфигурации IDE плагина. И если стандартного PropertyGrid компонента оказывается недостаточно для управления настройками, а встроенный механизм сохранения настроек по каким-то причинам не планируется использовать, то вполне разумным может оказаться создание полностью независимого от среды диалога. К преимуществам подобного подхода можно отнести высокую переносимость данного решения (например, плагин имеет несколько версий для различных IDE с едиными настройками), а также полный контроль непосредственно над самим диалоговым окном, что в свою очередь существенно облегчает поддержку различных пользовательских конфигураций по разрешению, DPI и т.п. Однако стоит помнить, что настройки из такого диалогового окна становятся недоступными для других разработчиков через объектную модель автоматизации.

В модуле-расширении PVS-Studio используется собственный механизм сохранения состояния настроек через внешний xml-файл, а настройки, интегрируемые в IDE, используются только для отображения\модификации части из этих внутренних настроек плагина. Наличие встроенного механизма сохранения состояния у Visual Studio очень часто приводило к конфликтам с внутренними настройками PVS-Studio, вызывая десинхронизацию. Поэтому, использование в модуле-расширении независимого механизма настроек также может потребовать переопределения стандартных механизмов Visual Studio (например, отключением их через пустые методы) для исключения подобных конфликтов.

Интеграция через xml описание Add-In модуля

Пользовательская страница настроек может быть интегрирована в IDE с помощью независимого XML файла описания AddIn. При этом содержимое интегрируемой пользовательской страницы должно быть реализовано в виде пользовательского компонента, например System.Windows.Forms.UserControl. Данный компонент никак не связан непосредственно с самим Add-In модулем, и поэтому может быть реализован как в самой библиотеке модуля, так и в виде отдельной независимой библиотеки. Возможно создание addin файла, описывающего только такой независимый пользовательский компонент. Приведём пример xml файла описания Add-In модуля, содержащего определение для пользовательской страницы настроек.

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility
  xmlns="http://schemas.microsoft.com/AutomationExtensibility">
  <HostApplication>
    <Name>Microsoft Visual Studio Macros</Name>
    <Version>10.0</Version>
  </HostApplication>
  <HostApplication>
    <Name>Microsoft Visual Studio</Name>
    <Version>10.0</Version>
  </HostApplication>
  <Addin>
    <FriendlyName>My Add in</FriendlyName>
    <Description>My Addin 1</Description>
    <Assembly>c:\MyAddIn1\MyAddin1.dll</Assembly>
    <FullClassName>MyAddin1.Connect</FullClassName>
    <LoadBehavior>0</LoadBehavior>
    <CommandPreload>1</CommandPreload>
    <CommandLineSafe>0</CommandLineSafe>
  </Addin>
  <ToolsOptionsPage>
    <Category Name="MyAddIn1">
      <SubCategory Name="My Tools Options Page">
      <Assembly> c:\MyAddIn1\MyAddin1.dll</Assembly>
      <FullClassName>MyAddin1.UserControl1</FullClassName>
      </SubCategory>
    </Category>
  </ToolsOptionsPage>
</Extensibility>

Описание пользовательской страницы настроек расположено в узле <ToolsOptionsPage>. Здесь узел <Assembly> указывает на библиотеку, содержащую пользовательский компонент, который будет использован для отображения клиентской области окна натроек. Узел <FullClassName> указывает на полное имя пользовательского компонента в формате Namespace.ClassName. Узлы <Category> и <SubCategory> определяют положение пользовательской страницы в древовидной структуре настроек диалога Tools -> Options, задавая группу и персональное имя страницы. В качестве <Category> может быть указана как уже существующая, так и новая группа. Как видно из примера, в данном случае пользовательский компонент MyAddin1.UserControl1 находится в одной библиотеке с самим модулем, но это не является обязательным требованием.

Visual Studio подгружает страницу настроек после первого обращения к ней через диалог Options. В отличие от интеграции страницы настроек через Managed Package Framework, описание страницы содержится только в xml файле описании addin, и, соответственно, страница будет загружена только при обнаружении средой такого файла. Visual Studio читает доступные ей addin файлы непосредственно после своей загрузки. Поиск addin файлов производится в директориях, задаваемых на странице настроек Environment -> Add-In/Macross Security. В отличие от страниц настроек, реализованных через MPF, подобный высокоуровневый метод интеграции не регистрирует страницу, как объект автоматизации, и соответственно, не предоставляет возможностей для использования механизмов доступа к её содержимому через объектную модель автоматизации и встроенных IDE механизмов сохранения состояния страницы.

Доступ к окнам настроек с помощью модели автоматизации

Объектная модель автоматизации Visual Studio предоставляет возможность получить доступ ко всем системным настройкам среды диалога Tools -> Options, кроме страниц Dynamic Help и Fonts and Colors (для них существуют отдельные API). Пользовательские страницы настроек будут доступны через модель автоматизации в случае, если они были зарегистрированы как объекты автоматизации, как было описано в предыдущей главе.

Для получения настроек можно воспользоваться методом get_Properties интерфейса EnvDTE.DTE:

Properties propertiesList = PVSStudio.DTE.get_Properties("MyPackage", 
"MyOptionsPage");

Страница настроек здесь идентифицируется по её имени и имени её группы. Для получения конкретного значения свойства:

Property MyProp1 = propertiesList.Item("MyOption1");

Теперь значение свойства можно получить и модифицировать через поле MyProp1.Value.

Для открытия страницы пользовательской настроек в диалоге Options можно воспользоваться методом ShowOptionPage MPF класса Package:

MyPackage.ShowOptionPage(typeof(MyOptionsPage));

Данные метод принимает тип (результат typeof) пользовательского класса наследника от DialogPage. Если необходимо открыть страницу, определённую вне разрабатываемого VSPackage (например, стандартную страницу IDE или страницу, определённую в другом модуле), то можно найти её по GUID идентификатору, который задан в следующей ветке реестра:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\
ToolsOptionsPages\<OptionsPageNme>\

Где <OptionsPageName> — имя страницы в диалоге Tools -> Options. Приведём пример открытия стандартной страницы IDE TextEditor -> General с помощью глобальной службы IMenuCommandService:

string targetGUID = "734A5DE2-DEBA-11d0-A6D0-00C04FB67F6A";
var command = new CommandID(VSConstants.GUID_VSStandardCommandSet97,
  VSConstants.cmdidToolsOptions);
var mcs = GetService(typeof(IMenuCommandService)) 
  as MenuCommandService;
mcs.GlobalInvoke(command, targetGUID);

На самом деле, данный код фактически эквивалентен выполнению команды среды Tools.Options. Её можно также вызвать с помощью метода ExecuteCommand объекта EnvDTE.DTE:

dte.ExecuteCommand("Tools.Options", 
"734A5DE2-DEBA-11d0-A6D0-00C04FB67F6A").

Рекомендуемые ссылки

Другие статьи этого цикла



Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

goto PVS-Studio;


Найденные ошибки

Проверено проектов
364
Собрано ошибок
13 504

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;