?

Log in

dotnet :: winforms :: finding the component container - [darxeth digital]
View:Свежие записи.
View:Архив.
View:Друзья.
View:Личная информация.
View:Website (RSS-Feed).
View:RSS трансляция. ATOM трансляция.

Security:
Subject:dotnet :: winforms :: finding the component container
Time:02:59 am
Настроение:tiredtired
В процессе подготовки к экзамену по матфизике задался интересным вопросом.
Пусть у нас есть компонент (Component). Мы хотим, чтобы он получал уведомления о событиях от всех контролов определённого типа (скажем, события TextChanged всех TextBox'ов), находящихся на той же форме.

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

private void AttachEvents()
{
    IContainer container = this.Site.Container;

    foreach(Component component in container.Components)
    {
        TextBox textBox = component as TextBox;
        if(textBox == null) continue;

        textBox.TextChanged += new EventHandler(textBox_TextChanged);
    }
}


Но это решает только часть проблемы. Представим себе, что на форму были добавлены новые TextBox'ы после вызова нашей функции. Естественно, их события не будут перехвачены.

К счастью, у форм (Form) есть событие, унаследованное от Control - ControlAdded.
Нам остаётся подключить к этому событию свой обработчик, и проблема решена.

Вот тут начинается самая сложная часть. Как из компонента получить форму, на которой он находится ?
Казалось бы, Component.Site.Container (типа IContainer) должен ей и соответствовать.
Вовсе нет.

Как выяснилось, класс Form даже не имплементирует интерфейс IContainer. А IContainer, который может получить компонент через свой Site - соответствует внутренней переменной, создаваемой дизайнером Visual Studio в коде формы:

private System.ComponentModel.IContainer components;

Тупик.

Но как-то же это можно сделать !
Дальнейшее исследование показало интересную вещь. Стандартному компоненту ErrorProvider тоже нужен доступ к объекту формы. И он каким-то образом добивается автоматиеского добавления следущей строчки в код процедуры InitializeComponent (автоматически создаваемой дизайнером форм):

this.errorProvider.ContainerControl = this;

что даёт ему ссылку на содержащую его форму.

Никаких специальных аттрибутов, которые бы позволяли добиться такого эффекта, мне обнаружить не удалось.
Но поиск в Google дал, всё-таки, интересные результаты. Как выяснилось, для этого нужно переопределить свойство Site в своём компоненте следующим образом:

public override ISite Site
{
  get { return base.Site; }
  set
  {
    base.Site = value;
    if (value == null)
    {
      return;
    }

    IDesignerHost host = value.GetService(
        typeof(IDesignerHost)) as IDesignerHost;

    IComponent componentHost = host.RootComponent;
    if (componentHost is ContainerControl)
    {
      ContainerControl = componentHost as ContainerControl;
    }
  }
}


В этом коде самым интересным является использование метода GetService интерфейса IServiceProvider, но об этом я не буду сейчас подробно говорить.

Вот пара ссылок на эту тему:
[1] Wired Prairie - Finding the Component Container
[2] Component/Control Partnership (google groups)

.netcomponent_modeldesign_timehowto
comments: Оставить комментарий Previous Entry Поделиться Next Entry


sluggard
Link:(Link)
Time:2004-06-29 01:30 am
Эта. Ты вообще публиковаться собираешься? Я думаю, что стоит. Так что давай приводи текст в божеский вид. Подробности в аське.
(Ответить) (Thread)


darxeth
Link:(Link)
Time:2004-06-30 03:39 pm
В аське я тебя вряд ли скоро увижу.
Где публиковаться ?
(Ответить) (Parent) (Thread)

dotnet :: winforms :: finding the component container - [darxeth digital]
View:Свежие записи.
View:Архив.
View:Друзья.
View:Личная информация.
View:Website (RSS-Feed).
View:RSS трансляция. ATOM трансляция.