上篇博客中,我们讲解了创建拓展模型和创建拓展模型对象,下面我们继续介绍,完成后面的步骤。
主界面为上下布局,上部分是一个MenuStrip,下部分是一个TabControl。下面我们将通过OSGi.NET的扩展机制,将其它插件注册的扩展信息转换成这个主界面的菜单项。
首先,我们先看一下如何获取扩展信息并监听事件。代码如下,在HandleExtension方法中,我们通过BundleContext插件上下文的GetExtensions来获取界面框架定义的扩展点的所有已有扩展信息,然后通过ApplicationContainer将扩展转换成WinShellApplication对象,并为其创建相应菜单,接着监听扩展变更事件。
/// <summary> /// 处理扩展信息。 /// </summary> private void HanldeExtension() { Initialize(); // 1 获取所有扩展信息 var extensions = BundleContext.GetExtensions(SimpleWinFormShellExtensionPoint); WinShellApplication application; foreach (var extension in extensions) { // 2 将扩展Extension对象转换成WinShellApplication对象 application = ApplicationContainer.AddApplicationForExtension(extension); // 3 为WinShellApplication对象创建顶层菜单和子菜单 CreateMenuStripForApplication(application); } // 4 监听扩展变更事件 BundleContext.ExtensionChanged += BundleContextExtensionChanged; }
private void BundleContextExtensionChanged(object sender, ExtensionEventArgs e) { if (e.ExtensionPoint.Equals(SimpleWinFormShellExtensionPoint)) { if (e.Action == CollectionChangedAction.Add) { // 新增扩展信息,说明有插件被启动,为其创建界面菜单 var app = ApplicationContainer.AddApplicationForExtension(e.Extension); CreateMenuStripForApplication(app); } else if (e.Action == CollectionChangedAction.Remove) { // 扩展信息被删除,有插件被停止,删除对应的界面菜单 var app = ApplicationContainer.RemoveApplicationForExtension(e.Extension); RemoveMenuStripForApplication(app); } } }
/// <summary> /// 扩展变更事件是一个异步事件,即是一个新的线程来处理事件。 /// 因此,需要将扩展变更事件对界面的变更通过代理发送到界面 /// 线程,由其来更新界面。 /// </summary> /// <param name="application">应用对象。</param> private delegate void CreateMenuStripForApplicationDelegate( WinShellApplication application); private void CreateMenuStripForApplication(WinShellApplication application) { CreateMenuStripForApplicationDelegate del = (WinShellApplication app) => { // 为WinShellApplication创建根菜单节点 var topToolStripItem = TopMenuStrip.Items.Add(app.Title, LoadImage(app.Bundle, app.Icon)) as ToolStripMenuItem; topToolStripItem.ToolTipText = app.ToolTip; // 创建子菜单节点 CreateChildMenu(app.Menus, topToolStripItem.DropDownItems); // 添加到WinShellApplication和顶层菜单节点对应表 ApplicationMenuStripMap.Add(app, topToolStripItem); }; // 如果当前线程不是界面线程,则通过Invoke来处理界面变更 if (InvokeRequired) { Invoke(del, application); } else // 否则,直接操作界面 { del(application); } }
/// <summary> /// 扩展变更事件是一个异步事件,即是一个新的线程来处理事件。 /// 因此,需要将扩展变更事件对界面的变更通过代理发送到界面 /// 线程,由其来更新界面。 /// </summary> /// <param name="application">应用对象。</param> private delegate void RemoveMenuStripForApplicationDelegate(WinShellApplication application); private void RemoveMenuStripForApplication(WinShellApplication application) { RemoveMenuStripForApplicationDelegate del = (WinShellApplication app) => { if (ApplicationMenuStripMap.ContainsKey(app)) { // 删除菜单、Map、显示的TabPage TopMenuStrip.Items.Remove(ApplicationMenuStripMap[app]); ApplicationMenuStripMap.Remove(app); foreach (TabPage tab in WorkspaceTabControl.TabPages) { var menu = tab.Tag as WinShellMenu; if (menu.Application.Equals(app)) // 清除Selection { var index = WorkspaceTabControl.SelectedIndex; if (index > 0 && tab.Equals(WorkspaceTabControl.SelectedTab)) { WorkspaceTabControl.SelectedIndex = index - 1; } WorkspaceTabControl.TabPages.Remove(tab); } } } }; // 如果当前线程不是界面线程,则通过Invoke来处理界面变更 if (InvokeRequired) { Invoke(del, application); } else // 否则,直接操作界面 { del(application); } } }
总结:
下面总结一下如何做界面插件:
首先创建一个插件程序---将ExtensionModel文件夹中的3个基础类添加进来——添加界面窗体——编写界面窗体代码。用代码展示为:
创建一个空windows窗体或高级windows窗体,在program.cs文件中加入以下代码,作为程序的入口点;
再添加一个Windows窗体插件程序,以下代码操作都是该类库中的操作。
通过Run(formInstance)找到插件的MainForm窗体,实例化,因为是界面插件所以要处理节点(即之前提过的扫描Manifest文件,通过其内容动态加载菜单)。
看一下HanldeExtension()方法,步骤很清楚先实例化在获取拓展信息;
不管是windows或web或其他应用程序,代码变动不大,只需要把相应的控件名称、控件加载的个别属性更改一下即可,用起来很方便。
界面插件完成之后,我们将插件发布之后,真正主程序的目录如下截图:
很清晰,明了,通过Program.cs文件,加载插件,通过界面插件加载其他菜单,主程序只是一个容器,各个功能都是一个个的积木,搭起来就是一个多功能自定义的简单程序。通过这张图可以看出该程序的健壮性很好,即使某个插件不能用了,但是不会影响其他功能插件的使用。
需要注意的是:我们做的界面插件的基础是iopenwork提供的UIShell.PageFlowService插件。我们通过引用该插件才能做响应的操作。
别忘了把这句代码放在该插件的Manifest文件中,否则插件界面不会显示加载。
至此,界面插件制作介绍完成,不足之处敬请指正~~~~~~