上篇博客中,我们讲解了创建拓展模型和创建拓展模型对象,下面我们继续介绍,完成后面的步骤。
1.创建主界面:
主界面为上下布局,上部分是一个MenuStrip,下部分是一个TabControl。下面我们将通过OSGi.NET的扩展机制,将其它插件注册的扩展信息转换成这个主界面的菜单项。
2.处理扩展点,将扩展信息转换成界面元素
首先,我们先看一下如何获取扩展信息并监听事件。代码如下,在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;
}
其次,是扩展变更事件处理的代码,当插件启动时,其扩展信息注册到OSGi.NET内核,当插件卸载时,其扩展信息会被卸载掉,因此,当扩展信息新增时,我们需要为其创建菜单项;反之,需要删除菜单项和已经显示的内容。
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);
}
}
}
下面我们看一下为WinShellApplication创建相应界面元素的代码,这里我们利用该对象创建了一个顶层菜单项和所有子菜单项。
/// <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()方法,步骤很清楚先实例化在获取拓展信息;
1.解析拓展点的名称、初始化基本工作:
2.将一个扩展转换成WinShellApplication对象。
3.为WinshellApplication对象创建顶层菜单和子菜单
4.拓展变更处理事件:
不管是windows或web或其他应用程序,代码变动不大,只需要把相应的控件名称、控件加载的个别属性更改一下即可,用起来很方便。
界面插件完成之后,我们将插件发布之后,真正主程序的目录如下截图:
很清晰,明了,通过Program.cs文件,加载插件,通过界面插件加载其他菜单,主程序只是一个容器,各个功能都是一个个的积木,搭起来就是一个多功能自定义的简单程序。通过这张图可以看出该程序的健壮性很好,即使某个插件不能用了,但是不会影响其他功能插件的使用。
Notice:
需要注意的是:我们做的界面插件的基础是iopenwork提供的UIShell.PageFlowService插件。我们通过引用该插件才能做响应的操作。
别忘了把这句代码放在该插件的Manifest文件中,否则插件界面不会显示加载。
至此,界面插件制作介绍完成,不足之处敬请指正~~~~~~