一个窗口在运行时,是这样的:

  

  但是,在设计时,却远比这复杂的多,它需要一个设计器对象:它仅存在于设计时,并连接到运行时存在的对象。

  

   宿主容器

  我们可以看到每个窗体和按钮均有与之相关的设计器。这两个对象也连接到拥有这两个对象的宿主容器。宿主容器还提供以下服务:选择服务(选择界面上的控件)、显示消息的UI服务、调用帮助、与开发环境交互等。

  另外,宿主容器还承担许多职责。它创建组件,将它们绑定到设计器,并为其维护的组件和设计器提供服务。它从某种持久性状态加载设计器,并将它们保存回该状态。宿主容器提供撤销逻辑、剪贴板功能、以及其他服务 — 设计器需要以其为基础来提供一个健壮的设计时环境。

  利用服务提高可扩展性

  .NET Framework 设计器体系结构是可扩展的。可扩展性的关键在于,服务能够增强各种设计器的可用功能。

  服务是一种对象,可根据类型进行查询。通常,您定义一些代表服务的抽象类或接口,然后提供对该服务的实现。您可以将服务添加到调用服务容器的对象,也可以从该对象中删除服务。

  IDesignerHost — 设计器的主要宿主接口,它是一个服务容器。服务是一种功能,可在由不同方编写的组件之间进行共享。因为这个原因,您必须在使用和创建服务时沿袭某些规则。

  DesignSurface 和DesignSurfaceManager

  .NET Framework 2.0 引入的两个类,用于宿主设计器并为设计器提供服务:DesignSurface 和 DesignSurfaceManager。DesignSurface 是用户眼中的设计器;它是用户进行操作以更改设计时功能的 UI。DesignSurface 可作为一个单独的设计器使用,或者可与 DesignSurfaceManager 联合使用以提供宿主多个 DesignSurfaces 的应用程序的一个公共实现。

  DesignSurface 自动提供一些设计时服务(参见MSDN)。其中的大部分服务可在服务容器中重写。替换不可替换的服务是非法的,原因是这些服务的实现均相互依赖。注意,添加到服务容器中(实现 IDisposable)的所有服务将在处置设计表面时进行处置。

  DesignSurfaceManager 旨在成为设计器的容器。它提供常规服务,用于处理设计器、属性窗口和其他全局对象之间的事件路由。DesignSurfaceManager 的使用是可选的,但建议在有若干设计器窗口的情况下使用它。

一个DesignSurface 的例子

// 创建一个Form的设计面
DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof(Form)); //获得这个设计面的试图,并在一个窗体中显示出来
Control c = ds.View as Control;
Form f = new Form();
c.Parent = f;
c.Dock = DockStyle.Fill;
f.Show();

  同样,您也能够利用任意具有可用根设计器的组件加载 DesignSurface。例如,可以加载一个 UserControl 或一个 Component。

  IDesignerHost (设计宿主)

  由 DesignSurface 提供的一个主要服务是 IDesignerHost。它是用于提供设计器和对类型、服务和事务进行访问的主要接口。它还可用于创建和销毁组件。要向我之前已创建的 Windows 窗体设计器添加一个按钮,只需从 DesignSurface 获得 IDesignerHost,然后用它创建如图 7 所示的按钮。

IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));
Button b = (Button)idh.CreateComponent(typeof(Button));// 创建组件
// Set the Parent of this Button to the RootComponent (the Form)
b.Parent = (Form)idh.RootComponent;

  工具箱

  工具箱需要实现 IToolboxService — 该服务添加到服务容器,并且可由任何需要使用它的用户访问。

  DesignerLoader(设计器加载器)

  持久保持设计器,设计器加载器用于从某些持久状态载入设计器。

  除了加载窗体设计,设计器加载器还可以保存设计。因为保存是可选操作,所以设计器加载器要进行侦听以改变设计器宿主的事件,然后自动保存与这些事件相关的状态。

  .NET Framework 2.0 引入两个新类,用于编写自定义的加载器:BasicDesignerLoader 和 CodeDomDesignerLoader。

  1.上面,我们已经演示过通过传递组件的类型来加载 DesignSurface 的根组件。2.然而,如果您使用加载器,则它可用于加载设计表面。当使用加载器时,将使用如下所示的 BeginLoad 代码片断:

// Load it using a Loader
ds.BeginLoad(new MyLoader());

  DesignerLoader 用于加载 DesignSurface 中的根组件,以及创建任意组件。创建一个新窗体或任意其他根组件时,只载入加载器。对比一下,当从代码文件或其他存储进行加载时,加载器用于分析文件或存储,重新创建根组件以及任何其他需要的组件。

  .NET Framework 定义一个名为 DesignerLoader 的抽象基类,它用于加载和保存持久存储的设计器。该基类是抽象的,因此可使用任意类型的持久性模型。然而,它也增加了该类实现的复杂性。

  BasicDesignerLoade

  CodeDomDesignerLoader

  BasicDesignerLoader 提供一个完整且通用的设计器加载器实现,但不包括与持久性格式相关的信息。象 DesignerLoader 一样,它是抽象的,不表示任意有关持久性格式的信息。然而,BasicDesignerLoader 所作的就是处理一些标准工作,如了解何时进行保存,了解如何重新加载,以及跟踪设计器的更改通知。它的功能还包括,支持多个加载依赖项,跟踪修改过的位以指示需要保存变更,延缓重新加载支持的空闲时间。

  .NET Framework 定义一个名为代码文档对象模型(Code Document Object Model,CodeDOM)的对象模型。所有源代码基本上均可拆分为基元元素,并且 CodeDOM 是这些元素的一个对象模型。当代码符合 CodeDOM 时,生成的对象模型可以稍后发送到特殊语言的代码生成器,以呈现适当的代码。

  尤其强大的是,我们可以将一个窗体保存为xml(使用BasicDesignerLoader加载器),这样可以在设计窗体后反序列化。

.NET创建宿主设计器的总结(一)

  根据以上《.NET创建宿主设计器--DesignHost、DesignSurface.》可以知道宿主容器在其中扮演着重要的角色。而DesignSurface就是宿主容器:他不仅仅是一个设计面,还提供了很多的服务,设计面+这些服务=宿主容器。

  作为设计面

  将他作为一个设计面是一个最重要的功能,可以通过下面的代码:

// 创建一个Form的设计面
DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof(Form));
//获得这个设计面的视图,并在一个窗体中显示出来(设计面也是一个Control)
Control c = ds.View as Control;
Form f = new Form();
c.Parent = f;
c.Dock = DockStyle.Fill;
f.Show();

  如果我们想给设计面加菜单,或者工具条,可以通过加载服务的方式:

IServiceContainer container = ds .GetService(typeof(IServiceContainer)) as
IServiceContainer;
menuCommandService = new MenuCommandService(surface);
if (container != null)
{
container.AddService(typeof(IToolboxService), toolBoxService);
container.AddService(typeof(IMenuCommandService), menuCommandService);
}

  这样,我们将ToolBox加到某种容器中(Panel)以后,便能够将工具箱和设计面关联起来了。

  以某种状态加载设计器/保存设计器

  我们可以将设计的页面保存为XML/C#/VB代码等,并且可以从这些代码文件中重新加载,并显示相应的定义设计器。这就是保存/加载设计器。

MS提供的主要有两个基础的设计器加载器(DesignerLoader):BasicDesignerLoade 和 CodeDomDesignerLoader

  这样我们就能通过某种加载器,序列化定义为相应的类型。(XML/C#/VB)

  1.使用某种加载器:

DesignSurface ds = new DesignSurface();
BasicDesignerLoaderbasicHostLoader = new BasicDesignerLoader(typeof(Form));
hostSurface.BeginLoad(basicHostLoader);
hostSurface.Loader = basicHostLoader; //获得这个设计面的视图,并在一个窗体中显示出来(设计面也是一个Control)
Control c = ds.View as Control;
Form f = new Form();
c.Parent = f;
c.Dock = DockStyle.Fill;
f.Show();

  设计面上的创建组件

  作为宿主容器还应该有创建组件的责任。并能够将这些组件绑定到设计面。这通过IDesignerHost 来实现,他提供设计器和对类型、服务和事务进行访问的主要接口,它还可用于创建和销毁组件。

  如下代码所示:

IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));
Button b = (Button)idh.CreateComponent(typeof(Button));// 创建组件
// Set the Parent of this Button to the RootComponent (the Form)
b.Parent = (Form)idh.RootComponent;

  .NET创建宿主设计器的总结(二)

  通过查看DesignSurface的View的代码:

通过查看DesignSurface的View的代码:
IComponent rootComponent = ((IDesignerHost) this._host).RootComponent;
........
IRootDesigner designer = ((IDesignerHost)
this._host).GetDesigner(rootComponent) as IRootDesigner;
.........
ViewTechnology[] supportedTechnologies =
designer.SupportedTechnologies;
int index = ;
while (index < supportedTechnologies.Length)
{
ViewTechnology technology = supportedTechnologies[index];
return designer.GetView(technology);
}

  (ViewTechnology 枚举 ,定义设计器宿主所支持的技术集的标识符。一般我们使用Default, 指定默认的视图技术支持就可以了。关于IComponent 可以参考下面的两篇文档)

  1.

IComponent rootComponent = ((IDesignerHost) this._host).RootComponent;

我们可以看到,他先通过host(设计宿主)获得根组件,(比如我们设计一个顺序工作流,那么根组件就是一个SequentialActivity,如果设计一个Form,那么根组件就是一个Form)

  2.

IRootDesigner designer = ((IDesignerHost) this._host).GetDesigner(rootComponent) as IRootDesigner;

这里通过host(设计宿主)的GetDesigner方法,获得了根组件的根设计器(对比前面的例子,也就是一个SequentialWorkflowDesigner或者FormDocumentDesigner)

  3.这里我们做个例子,一路追踪SequentialWorkflowDesigner,最后找到了它的基类,ActivityDesigner,实现了IRootDesigner 接口,查看他的GetView代码:

object IRootDesigner.GetView(ViewTechnology technology)
{
DesignSurface service = this.GetService(typeof(DesignSurface)) as DesignSurface;
IDesignerHost host = this.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (((this.workflowView == null) && (service != null)) && ((host != null) && (host.RootComponent == this.Activity)))
{
this.workflowView = this.CreateView(technology);
}
return this.workflowView;
}

  或者FormDocumentDesigner的父类DocumentDesigner的GetView:

object IRootDesigner.GetView(ViewTechnology technology)
{
if ((technology != ViewTechnology.Default) && (technology != ViewTechnology.WindowsForms))
{
throw new ArgumentException();
}
return this.frame;
}

  我们可以发现,他们返回的是WorkflowView的实例或者DesignerFrame的实例,前者继承了UserControl,或者继承了Control,所以他们都能被添加到Panle子类的容器中。

  关于Host:

  我们可以发现他在DesignSurface构造的时候获得:

public DesignSurface() : this((IServiceProvider) null)
{
}
public DesignSurface(IServiceProvider parentProvider)
{
this._parentProvider = parentProvider;
this._serviceContainer = new
DesignSurfaceServiceContainer(this._parentProvider);
ServiceCreatorCallback callback = new
ServiceCreatorCallback(this.OnCreateService);
this.ServiceContainer.AddService(typeof(ISelectionService),
callback);
this.ServiceContainer.AddService(
typeof(IExtenderProviderService), callback);
this.ServiceContainer.AddService(
typeof(IExtenderListService), callback);
this.ServiceContainer.AddService(
typeof(ITypeDescriptorFilterService), callback);
this.ServiceContainer.AddService(
typeof(IReferenceService), callback);
this.ServiceContainer.AddService(typeof(DesignSurface), this);
this._host = new DesignerHost(this);
}

  其他的战且不管,发现Host直接被new成了DesignerHost,他是IDesignerHost的默认实现。这个我们只能查看代码,也没有相关的文档,太难分析了。通过其他方法:

  设计加载器

  我们知道我们是可以自定义设计加载器的,通常在这里面我们会自己加一些服务,比如工具条,右键菜单等。代码如下:

internal sealed class WorkflowLoader : WorkflowDesignerLoader
{
#region Overrides from WorkflowDesignerLoader
protected override void Initialize()
{
base.Initialize();
// Add all the services to the loaderhost
IDesignerLoaderHost host = LoaderHost;
if (host != null)
{
this.SetBaseComponentClassName("foo.Workflow1");
host.AddService(typeof(IMenuCommandService), new
WorkflowMenuCommandService(host));
host.AddService(typeof(IToolboxService), new
ToolboxService(host));
}
}
}

  我们前面刚刚说过,我们可以通过给设计面加服务来实现类似的操作,怎么这里有可以呢?而且,使用自定义设计器加载器的代码方式也很让人迷糊:

DesignSurface designSurface = new DesignSurface();
WorkflowLoader loader = new WorkflowLoader();
designSurface.BeginLoad(loader);

  难道在调用ds的BeginLoad以后,就有某种方式调用了DesignLoader的Initialize?并且,将服务加到了ds中?

  查看designSurface.BeginLoad(loader);的代码:

internal void BeginLoad(DesignerLoader loader)
{
this._loader = loader;
try
{
this._loader.BeginLoad(this);
}
.......
}

  我们可以发现关键的代码是他调用了DesignerLoader 的BeginLoad,并且把自己(DesignSurface)给传递进去,通过查看WorkflowDesignerLoader 父类BasicDesignerLoader的BeginLoad:

public override void BeginLoad(IDesignerLoaderHost host)
{
if (this._host == null)
{
this._host = host;
..............
this.Initialize();
host.Activated += new EventHandler(this.OnDesignerActivate);
host.Deactivated += new EventHandler(this.OnDesignerDeactivate);
}
}

  哈哈,关键的代码发现了,我们发现BeginLoad的参数正是IDesignerLoaderHost ,并且将传进来的DesignSurface赋给了它的一个实例变量, private IDesignerLoaderHost _host; 并且调用了Initialize()方法。

  再回头看我们自定义加载器的Initialize()方法就很清楚了,我们获得了IDesignerLoaderHost (其实就是DesignSurface,并且将服务加给了DesignSurface)。

  现在再来看一下IDesignerLoaderHost 的定义:提供一个接口,该接口可扩展设计器宿主以支持从序列化状态加载。

  从字面的意思来说,IDesignerLoaderHost 是”设计器加载器的宿主“,搞不明白为什么他是DesignSurface??

  1.我们的设计器宿主(DesignHost)被包含进了设计面(DesignSurace)中。可以通过设计器宿主的GetDesigner(设计的类型(Form/Button))方法获得设计器。

IDesigner designer = ((IDesignerHost)  this._host).GetDesigner(rootComponent);

  2.获得设计器以后就能通过设计器的GetView返回该设计器的视图显示。(就是我们可以看到的设计的样子)

  3.可以通过设计宿主的 designerHost.Container.Add(activity, rootSiteName); 加入向我们的设计面中加入控件。

  4.有时候也需要保存/加载设计器,所以就有了自定义的DesignLoader(设计器加载器)

.NET创建宿主设计器--DesignHost、DesignSurface.的更多相关文章

  1. 通过用 .NET 生成自定义窗体设计器来定制应用程序

    通过用 .NET 生成自定义窗体设计器来定制应用程序 https://www.microsoft.com/china/MSDN/library/netFramework/netframework/Cu ...

  2. C#用DesignSurface实现一个简单的窗体设计器

    System.ComponentModel.Design.DesignSurface是为设计组件提供一个用户界面,通过它可以实现一个简单的窗体设计器. 在构建之前,我们需要引入System.Desig ...

  3. 创建您的 ActiveReports Web端在线报表设计器

    概述 ActiveReports Web端在线报表设计器已经正式上线!看到它这么帅气.实用,你是不是也想自己动手创建一个? 现在我们就来教您,如何创建一个简单的 ActiveReports Web端在 ...

  4. How to: Create a Business Model in the XPO Data Model Designer 如何:在 XPO 数据模型设计器中创建业务模型

    This topic provides step-by-step instructions on how to use the XPO Data Model Designer in XAF appli ...

  5. 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

    企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...

  6. YbSoftwareFactory 代码生成插件【十六】:Web 下灵活、强大的审批流程实现(含流程控制组件、流程设计器和表单设计器)

    程序=数据结构+算法,而企业级的软件=数据+流程,流程往往千差万别,客户自身有时都搞不清楚,随时变化的情况更是家常便饭,抛开功能等不谈,需求变化很大程度上就是流程的变化,流程的变化会给开发工作造成很大 ...

  7. 如何在CRM系统中集成ActiveReports最终报表设计器

    有时候,将ActiveReports设计器集成到业务系统中,为用户提供一些自定义的数据表,用户不需要了解如何底层的逻辑关系和后台代码,只需要选择几张关联的数据表,我们会根据用户的选择生成可供用户直接使 ...

  8. ActiveReports 9 新功能:可视化查询设计器(VQD)介绍

    在最新发布的ActiveReports 9报表控件中添加了多项新功能,以帮助你在更短的时间里创建外观绚丽.功能强大的报表系统,本文将重点介绍可视化数据查询设计器,无需手动编写任何SQL语句,主要内容如 ...

  9. C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)

    既然是一个窗体设计器,那就应该能够设置控件的属性,设置属性最好的当然是PropertyGrid了,我们仅仅需要使用一个PropertyGrid.SelectedObject = Control就可以搞 ...

随机推荐

  1. 第六篇:python基础_6 内置函数与常用模块(一)

    本篇内容 内置函数 匿名函数 re模块 time模块 random模块 os模块 sys模块 json与pickle模块 shelve模块 一. 内置函数 1.定义 内置函数又被称为工厂函数. 2.常 ...

  2. web标准,可用性和可访问性

    web标准,简单的说,是指html,css,JavaScript三者的分离. 网页由三部分组成:结构,表现和行为.对应的标准分为三方面: 1.结构化标准语言XHTML和XML2.表现标准语言主要包括c ...

  3. windows server 2008 密码破解

    个人是不太喜欢windows 系统的,但有时候没办法  业务已经用在Windows系统上面,今天碰到管理员密码还忘记了的情况,在此记录下 破解密码的过程 1.下载小白菜装机版u盘制作PE启动,详情参考 ...

  4. jmeter作接口测试入门的简单使用说明

    一.添加接口信息 1.添加线程组 (1)路径如下图: (2)部分内容解释 a.  Number of Threads(users):线程数 b.  Ramp-Up Period(in seconds) ...

  5. cocoaPod的Podfile文件的创建和内容格式

    Podfile创建: 1.终端中,cd到项目总目录(cd +路径名) cd/........./......../...../项目名 2.终端中继续建立Podfile(配置文件) touch Podf ...

  6. L-Gap Substrings(uva 10829)

    题意:有一种形如uvu形式的字符串,其中u是非空字符串,且V的长度正好为L,那么称这个字符串为L-Gap字符串 给出一个字符串S,以及一个正整数L,问S中有多少个L-Gap子串. /* 这道题用到一个 ...

  7. 多表的时候怎样在MVC VIEW中显示

    原文发布时间为:2011-04-01 -- 来源于本人的百度文章 [由搬家工具导入] Linq join query displayed in MVC view Instead of returnin ...

  8. User Experience Collection

    about a data driven system front end: 1. about succeeded requests: they do not want to see alerts ab ...

  9. AC日记——传染病控制 洛谷 P1041

    传染病控制 思路: 题目想问的是: 有一棵树: 对于除1外每个深度可以剪掉一棵子树: 问最后剩下多少节点: 题目意思一简单,这个题立马就变水了: 搜索就能ac: 数据有为链的情况,按深度为层次搜索的话 ...

  10. 提升开发效率的一款mybatis开发神器

    文末附有完整案例的代码内容!! 以前在开发的时候,使用mybatis的时候,经常都需要先配置xml映射文件,然后每条sql操作都需要自己进行手动编写,对于一些复杂的sql这么来操作确实有必要,但是如果 ...