技术开发 频道

深入观察 .NET组件模型及继承


    描述符有一个方法AddValueChanged,此方法AddValueChanged允许我们补充一个处理程序,无论什么时候,当属性被改变时,可以调用此处理程序。为了运行序列化过程,这正是我们所要做的事情。如果我们早已附加了我们的处理程序,我们也可以为ViewInfo类型补充一个IsHooked 标记。

//Verify that we are trapping the changes.
if (!info.IsHooked)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(info);
props["ControlProperty"].AddValueChanged(info,
new EventHandler(RaiseViewInfoChanged));
props["Model"].AddValueChanged(info,
new EventHandler(RaiseViewInfoChanged));
props["ModelProperty"].AddValueChanged(info,
new EventHandler(RaiseViewInfoChanged));
info.IsHooked = true;
}
   我们使用TypeDescriptor类型,以此来得到PropertyDescriptor对象集合,此PropertyDescriptor对象集合可以应用到我们的对象。这个类型有许多静态方法,以此来处理值得考虑的构件。看一下中的MSDN对成员表以及它们的使用的帮助。

    RaiseViewInfoChanged处理程序通知宿主:通过使用来自于集成开发环境的另一个服务IComponentChangeService,映射发生了改变

internal void RaiseViewInfoChanged(object sender, EventArgs e)
{
IComponentChangeService svc = (IComponentChangeService)
GetService(typeof(IComponentChangeService));
if (svc != null)
{
svc.OnComponentChanged(this,
TypeDescriptor.GetProperties(this)["ConfiguredViews"],
null,
this.ConfiguredViews);
}
}
   通过利用这个服务,集成开发环境将采用正确的方法再一次保存我们的构件

   第二个问题就是:使用同样的服务,可以完成追踪命名和删除控件改变(追踪命名和删除控件改变早已被映射了),但是在这种情况下,这是一种全局追踪,我们不可能像上面所完成的任务一样来完成这种全局追踪。完成这种全局追踪的方法就是为我们的构件创建一个设计器,聆听改变。一个设计器允许我们为构件补充更多设计时期功能,马上会看到这一结果。此设计器,就像RootDesigner,是通过一个属性连接起来的:
[Designer(typeof(ControllerDesigner))] 
public class BaseController : Component, IContainer, IExtenderProvider
   ControllerDesigner类型是从ComponentDesigner继承来的,ControllerDesigner类型是一个与IComponent接口相连接的默认设计器。一个设计器有更多的控制能力,并且与集成开发环境交流的更多,当根组件 例如:一个 Form 或者Page)被设计时,它一直存在,而且它被永远地连接到它正在 “设计”构件,在我们的情况中,是一个具体的控制器。设计器中的关键点就是Initialize方法:

internal class Controller Designer : ComponentDesigner 
{
public override void Initialize(IComponent component)
{
//Always call base.Initialize!
base.Initialize(component);
IComponentChangeService ccs = (IComponentChangeService)
GetService(typeof(IComponentChangeService));
if (ccs != null)
{
ccs.ComponentRemoving +=
new ComponentEventHandler(OnComponentRemoving);
ccs.ComponentRename +=
new ComponentRenameEventHandler(OnComponentRename);
}
}

Base Controller Current Controller
{
get { return (Base Controller) this.Component; }
}
}
   正如在代码注释中所提到的,在Initialize方法中必须做的第一件事情就是调用基本类型实现。这允许设计器正确地与集成开发环境连接起来。设计器包括一个Component属性,这个功能引入的IComponent参数中设置有此Component属性。运行之后,使用通用GetService方法,我们请求服务,在基本ComponentDesigner类型中,直接实现了此过程。此实现只是将调用传递到设计器Component.Site属性,这仅仅是一条捷径。

   使用现有的服务,我们增加兴趣的两个事件:ComponentRemoving 以及ComponentRename。首先看一下重命名处理程序。

void OnComponentRename(object sender, ComponentRenameEventArgs e) 
{
if (CurrentController.ConfiguredViews.ContainsKey(e.OldName))
{
ViewInfo info = (ViewInfo) CurrentController.ConfiguredViews[e.OldName];
info.ControlID = e.NewName;
CurrentController.ConfiguredViews.Remove(e.OldName);
CurrentController.ConfiguredViews.Add(e.NewName, info);
//Notify environment of the change.
RaiseComponentChanging(
TypeDescriptor.GetProperties(CurrentController)["ConfiguredViews"]);
}
}
   我们简单地删除了映射,使用新的构件名称,重新补充此映射作为键。最后必须通知环境再一次引发代码生成。通过一个基本ComponentDesigner类型,RaiseComponentChanging就完成了这一过程。删除处理程序有一点点不同:

void OnComponentRemoving(object sender, ComponentEventArgs e) 
{
IReferenceService svc = (IReferenceService)
GetService(typeof(IReferenceService));
string id = svc.GetName(sender);
if (id != null && CurrentController.ConfiguredViews.Contains键(id))
{
CurrentController.ConfiguredViews.Remove(id);
//Notify environment of the change.
RaiseComponentChanging(
TypeDescriptor.GetProperties(CurrentController)["ConfiguredViews"]);
}
}
   在这里使用另一个服务IReferenceService,IReferenceService允许我们重新找回构件名称,一个特定类型的构件集合,此特定类型的构件集合存在在宿主内部。通过使用它,我们知道被删除的构件的名称,相应地我们删除映射,通知环境,就像我们先前所做的一样。
0
相关文章