Главная » C#, Компоненты » Сервисы

0

Когда компонент помещается в контейнер, он получает доступ к сервисам, предоставляемым этим контейнером с помощью интерфейса iserviceprovider:

public interface IServiceProvider I

object GetService (Type serviceType)

\

Из определения интерфейса видно, что доступ к сервисам осуществляется по типу. Нужный сервис можно получить с помощью метода GetService О класса Component Designer, как, например, мы это делали при создании смарт-тега:

designerActionUIService = GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;

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

режима разработки

Вот список наиболее важных сервисов времени разработки:

О System. ComponentModel. Design. I Designer Host — возвращает интерфейс хоста (см. разд. 4.4)\

О System.ComponentModel.Design.IComponentChangeService — позволяет уведомлять контейнер (и всех, кто подписался на данные события) и получать уведомления об изменении компонентов и их свойств;

?    System.ComponentModel. Design. ISelectionService — позволяет получать информацию о выделенных компонентах и управлять выделением;

?    System.ComponentModel. Design. IMenuCommandService — позволяет расширять контекстное меню Visual Studio, открываемое при нажатии на форму, или просто создавать команды дизайнера, привязанные, например, к комбинации клавиш;

?    System.ComponentModel. Design. IReferenceService — позволяет получить имена компонентов по ссылкам на них и, наоборот, ссылки по именам. Также можно получить список всех компонентов формы;

П System.ComponentModel.Design.Serialization.INameCreationService — позволяет сгенерировать уникальное имя для компонента или проверить имеющееся на уникальность и правильность;

О System. ComponentModel. Design. IEventBindingServi.ee — обеспечивает сер- вис управления событиями компонента. Например, позволяет программно создать обработчик события;

?    System.ComponentModel.Design.IDesignerOptionService — предоставляет доступ к параметрам дизайнера, настраиваемым с помощью меню Tools | Options | Windows Form Designer;

?    System.ComponentModel.Design.IExtenderListService И IExtenderProvider-

service — предоставляют сервисы, позволяющие создавать глобальные свойства, добавляющиеся ко всем компонентам. Эти сервисы мы будем обсуждать отдельно в разд. 72.7;

?    System.ComponentModel.Design.IDesignerEventService— позволяет получать нотификации о смене дизайнера. В частности, это нотификации о переключении из дизайнера в код и обратно;

?    System.ComponentModel. Design. IDictionaryService — Предоставляет доступ к таблице пользовательских данных компонента времени разработки;

?    System. ComponentModel. Design. IHelpService— Обеспечивает ДОСТуп К КОН- текстной помощи Visual Studio;

О System.ComponentModel.Design.ITypeDescriptorFilterService — Позволяет фильтровать или добавлять описания свойств, событий или атрибутов компонентов;

?    System. Drawing. Design. IToolboxService——– ПОЗВОЛЯет взаимодействовать

с палитрой компонентов и программно добавлять компоненты на форму;

П System. Component Model. Design. IResourceService — позволяет получить доступ к ресурсам проекта, например, для компонентов, поддерживающих переключение языков;

П System. Component Model. Design. DesignerActionService — Предоставляет сервис для организации смарт-тегов (см. разд. 6.4)\

П System. Windows. Forms. Design. ILJIService— позволяет создавать диалоговые окна и управлять окнами Visual Studio; П System. Windows. Forms. Design. IWindowsFormsEditorService — Предоставляет возможность показать собственный диалог при редактировании свойства. Этот сервис я буду рассматривать в разд. 9.3 при обсуждении редакторов типа;

П System.ComponentModel.Design.ITypeDiscoveryService — Предоставляет сервис для получения доступных типов. Работу с этим сервисом я покажу на примере в разд. "Редактирование списка объектов нескольких типов" главы 9\

П System. Drawing. Design. IPropertyValueUIService — Позволяет отобразить небольшой значок рядом с именем свойства и обработать двойной щелчок на этом значке. Может использоваться для отображения ошибок значения свойства;

П System. Windows. Forms. Design. ISplitWindowService — недокументированный сервис, позволяющий добавить в редактор формы специальное окно, как, например, для невизуальных компонентов. Описывать каждый сервис сам по себе, как мне кажется, смысла не имеет. Мы пойдем другим путем. Я буду описывать некоторые стандартные задачи, а уже от задачи будет зависеть, какой из сервисов надо будет использовать. Кроме того, про некоторые сервисы я буду рассказывать в других главах, там, где необходимость их использования вызвана реальной задачей. Кроме приведенного выше списка, я расскажу о классе Projectitem и интерфейсе INestedContainer. С одной стороны, они не являются сервисами, но с другой, их экземпляры можно получить с помощью вызова метода GetService (). Класс projectitem позволяет получить информацию о Visual Studio, проекте и форме, в которую вставлен компонент. Эта функциональность не документирована в MSDN, но требуется достаточно часто. Интерфейс INestedContainer позволяет создавать и использовать компоненты, содержащиеся внутри других компонентов.

Информацию О сервисах Designer Ac tionUI Service (см. разд. 7.2) И Beheviorservice (см. разд. 7.3) я добавил для полноты картины, хотя мы уже рассматривали эти сервисы в разд. 6.4 и 6.14.

Управление окном смарт-тега

Если выполнение каких-то операций в окне смарт-тега приводит к изменению значения полей компонента, нужно обновить само окно смарт-тега. Для ЭТОГО надо получить ссылку на сервис DesignerActionUIService:

designerActionUIService = GetService(typeof(DesignerActionUIService))

А затем вызвать метод Refresh:

designerActionUIService.Refresh(glabel);

Мы использовали этот сервис в разд. 6.4.

Управление слоями и маркерами

В разд. 6.14 мы применяли сервис Behaviorservice для добавления нового слоя. Кратко опишу основные моменты использования этого сервиса.

Ссылка на этот сервис в классе ControiDesigner уже имеется, поэтому получать его с помощью вызова GetService {) не нужно:

Adorner zoomAdorner = new Adorner(); BehaviorService.Adorners.Add(zoomAdorner);

zoomAdorner.Glyphs.Add(new ZoomGlyph(BehaviorService, Control}); Для удаления слоя мы использовали метод Remove {}: BehaviorService.Adorners.Remove(zoomAdorner);

Для изменения состояния слоя нужно сначала получить его индекс, а затем установить свойство Enabled в нужное значение:

int index = BehaviorService.Adorners.IndexOf{zoomAdorner};

if (index > 0)

{

BehaviorService.Adorners[index].Enabled =

(selectionType != GlyphSelectionType.NotSelected);

}

После изменения свойств компонента может потребоваться обновить маркеры компонента. Для этого нужно вызвать метод SyncSeiectionO :

behaviorservice.SyncSelection(};

При обновлении маркеров (вызванное, например, изменением размеров компонента пользователем) вызывается событие Synchronize.

Для преобразования координат экрана и слоя служат методы

AdornerWindowPointToScreen (), AdornerWindowToScreen (), ScreenToAdornerWindow () кcontroiToAdornerwindow(). Так, например, мы вычисляли центр компонента:

Point edge = behaviorService.ControiToAdornerwindow(control); center = new Pointtedge.X + (control.Size.Width / 2), edge.Y + (control.Size.Height / 2));

Соответствующий код можно найти в разд. 6.14.

Обработка изменений компонентов и их свойств

Рассмотрим ситуацию, когда к компоненту Button привязан компонент imageList (список картинок), и, соответственно, кнопка отображает одну из картинок, содержащихся в imageList. Все работает замечательно до тех пор, пока кто-нибудь не удалит с формы компонент ImageList. Если кнопка не узнает об этом событии и попытается получить свою картинку, в приложении возникнет исключительная ситуация и, вероятнее всего, система Visual Studio будет аварийно закрыта.

Для того чтобы одни компоненты могли узнавать об изменении других, служит сервис System. ComponentModel. Design. IComponentChangeService, имеющий следующий набор событий:

?    event Component Event Handle r ComponentAdding вызывается перед добавлением компонента;

О event Component Event Handle r Component Added вызывается ПОСЛе Добавления компонента;

О event ComponentChangingEventHandler ComponentChanging вызывается перед изменением свойства компонента;

?    event Componen t Chang edEvent Handle r Component Changed вызывается после изменения свойства компонента;

О event Component EventHandler Component Removing вызывается перед удалением компонента;

?    event ComponentEventHandler Component Removed ВЫЗЫВаеТСЯ ПОСЛе удаления компонента:

?    event Componen tRenameEvent Handle r Component Rename вызывается после переименования компонента.

В листинге 7.1 показан пример компонента componentciass, который обрабатывает все события сервиса IComponentChangeService и отображает соответствующие сообщения в специальном списке сообщений logListBox. При создании компонента (правильнее сказать, при привязке компонента к контейнеру в свойстве Site) нужно зарегистрировать соответствующие обработчики событий (метод RegisterChangeNotificationsO), а при уничтожении компонента— освободить ИХ (метод ClearChangeNotificationsO). Как ЭТО ВЫГЛЯДИТ в Visual Studio, показано на рис. 7.1.

При необходимости уведомить сервис об изменениях г. свойствах компонента используются методы OnComponentChanging () И OnComponentChanged (), например, так:

i<inMode I

{

IComponentChangeService srv =

GetService{typeof(IComponentChangeService)) as IComponentChangeService;

if (srv != null) {

srv.OnCoinponentChanged(this, null, null, null) ;

}

}

Событие component Rename может оказаться важным, если в некотором списке хранится не только ссылка на компонент, но и его имя.

Листинг 7.1 Пример обработки событии гсрниса If* .ninnrnrChAnjcticrviro

using System;

using System.Data;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.ComponentModel.Design;

using System.Windows.Forms;

namespace IComponentChangeServiceExample {

public class ComponentClass : UserControl

{

// Буфер для отображения сообщений private ListBox logListBox;

// Конструктор public ComponentClass{)

t

InitializeComponent() ;

}

private void InitializeComponent()

{

this.logListBox = new System.Windows.Forms.ListBox (); this.SuspendLayout();

//

// logListBox

//

this.logListBox.Dock = System.Windows.Forms.Dockstyle.Fill; this.logListBox.Location = new System.Drawing.Point(0, 0); this.logListBox.Name = "logListBox";

this.logListBox.Size = new System.Drawing.Size{200, 95); this.logListBox.Tablndex = 0;

II

// ComponentClass

//

this.Controls.Add(this.logListBox); this.Name = "ComponentClass";

this.Size = new System.Drawing.Size(200, 100); this.ResumeLayout(false);

}

// Свойство Site устанавливается дизайнером только // в режиме разработки в момент установки связи между // дизайнером и компонентом. Именно в этот момент мы // будем регистрировать обработчики событий сервиса

public override ISite Site

{

get

{

return base.Site;

}

set

{

// Очистить предыдущие обработчики, II если они были установлены ClearChangeNotifications();

II Сохраняем значение Site base.Site = value;

// Регистрируем свои обработчики уведомлений //об изменении состояния компонентов RegisterChangeNotifications();

f

!

// Удаление обработчиков событий private void ClearChangeNotifications()

{

// Получаем ссылку на сервис IComponentChangeService changeService =

(IComponentChangeService)GetService(

typeof(IComponentChangeService));

// Если сервис не null, значит, это режим разработки if (changeService != null)

{

changeService .CoraMtitotChanoed

new ComponentChangedEventHandler(OnComponentChanged); changeService.ComponentChanging -=

new ComponentChangingEventHandler(OnComponentChanging); changeService.CompQnentAdded -=

new ComponentEventHandler(OnComponentAdded); changeService.ComponentAdding -=

new ComponentEventHandler(OnComponentAdding); changeService.ComponentRemoved -=

new ComponentEventHandler(OnComponentRemoved); changeService.ComponentRemoving —

new ComponentEventHandler(OnComponentRemoving); changeService.ComponentRename

new ComponentRenameEventHandler(OnComponentRename);

}

!

// Регистрируем обработчики событий private void RegisterChangeNotifications() (

// Получаем ссылку на сервис IComponentChangeService changeService =

(IComponentChangeService)GetService(

typeof(IComponentChangeService)};

if (changeService )= null) {

changeService.ComponentChanged +=

new ComponentChangedEventHandler(OnComponentChanged); changeService.ComponentChanging +—

new ComponentChangingEventHandler(OnComponentChanging); changeService.ComponentAdded +=

new ComponentEventHandler(OnComponentAdded); changeService.ComponentAdding +=

new ComponentEventHandler(OnComponentAdding); changeService.ComponentRemoved +=

new ComponentEventHandler(OnComponentRemoved); changeService.ComponentRemoving +=

new ComponentEventHandler(OnComponentRemoving); changeService.ComponentRename +=

new ComponentRenameEventHandler (OnConiponentRenaine) ;

}

t

// Вызывается после изменения свойств компонента private void OnComponentChanged(

object sender, ComponentChangedEventArgs ce)

{

if (ce.Component !— null

((ICompOnent)ce.Component).Site != null && ce.Member != null) DoLog(string.Format("Поле (0} компонента {1} изменено", ce.Member.Name, {(IComponent)ce.Component).Site.Name));

}

// Вызывается до изменения свойств компонента private void OnComponentChanging{object sender, ComponentChangingEventArgs ce)

{

if (ce.Component !- null &&

({IComponent)ce.Component).Site 1= null && ce.Member != null) DoLog{string.Format{"Поле {0} компонента (1} будет изменено", се.Member.Name,

{(IComponent)ce.Component).Site.Name));

t

// Вызывается до добавления компонента

private void OnComponentAdded{object sender, ComponentEventArgs ce) {

DoLog(string.Format("Добавлен компонент {0}", ce.Component.Site.Name});

// Вызывается после добавления компонента

private void OnComponentAdding(object sender, ComponentEventArgs ce) {

DoLog(string.Format{"Будет добавлен компонент типа (0}", се.Component.GetType().FullName));

}

// Вызывается после удаления компонента

private void OnComponentRemoved(object sender, ComponentEventArgs ce) {

DoLog(string.Format("Удален компонент {0}",

ce.Component.Site.Name));

}

// Вызывается до удаления компонента

private void OnComponentRemoving(object sender,

ComponentEventArgs ce)

{

DoLog(string.Format("Компонент (0) будет удален", ce.Component.Site.Name));

}

// Вызывается при переименовании компонента private void OnComponentRename(object sender,

ComponentRenameEventArgs ce)

{

DoLog{string.Format("Компонент {0} переименован в (1}", ce.OldName, ce.NewName));

}

// Добавляем строку в журнал

private void DoLog(string text) {

logListBox.Iterns.Add(text};

logListBox.Selectedlndex — logListBox.Items.Count — 1;

1

// Сброс обработчиков при освобождении компонента

protected override void Dispose(bool disposing) {

if (disposing)

{

ClearChangeNotifications();

Рис. 7.1. Вид компонента ComponentClass

Литература:

Агуров П. В. C#. Разработка компонентов в MS Visual Studio 2005/2008. – СПб.: БХВ-Петербург, 2008. — 480 е.: ил.

По теме:

  • Комментарии