插件系统:如何处理主机应用程序和插件之间的事件
Plugin-system: How to handle events between host application and plugin
注意:我需要使用.NET 3.5
我的测试应用程序分为三个部分(不同的项目和不同的dll); host-application, plugin-sdk and test-plugin.
宿主应用程序是一个 WPF 应用程序,有一个区域可以从插件加载视图。插件通过反射加载。这部分工作正常。
SDK 包含插件可以实现的通用接口。我的一个接口有一个插件必须实现的事件,当它被触发时,主应用程序需要监听它,以根据自定义 EventArgs 执行操作。
SDK 从主机应用程序和插件中引用。
首先是我的通用插件加载器:
public static class GenericPluginLoader<T>
{
public static ICollection<T> LoadPlugins(string path)
{
string[] dllFileNames = null;
if (Directory.Exists(path))
{
dllFileNames = Directory.GetFiles(path, "*.dll");
var assemblies = new List<Assembly>(dllFileNames.Length);
foreach (var dllFile in dllFileNames)
{
var an = AssemblyName.GetAssemblyName(dllFile);
var assembly = Assembly.Load(an);
assemblies.Add(assembly);
}
var pluginType = typeof(T);
var pluginTypes = new List<Type>();
foreach (var assembly in assemblies)
{
if (assembly != null)
{
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsInterface || type.IsAbstract)
{
continue;
}
else
{
if (type.GetInterface(pluginType.FullName) != null)
{
pluginTypes.Add(type);
}
}
}
}
}
var plugins = new List<T>(pluginTypes.Count);
foreach (var type in pluginTypes)
{
var plugin = (T)Activator.CreateInstance(type);
plugins.Add(plugin);
}
return plugins;
}
return null;
}
}
带有事件的接口我想要我的插件直到实现和自定义事件参数
public class JournalLineEventArgs : EventArgs
{
public string LineNumber { get; set; }
}
public interface IJournalPlugin
{
event EventHandler<JournalLineEventArgs> LineAdding;
}
来自我的插件的事件处理程序
public event EventHandler<JournalLineEventArgs> LineAdding;
在我的主机应用程序中,我将插件加载到列表中,然后添加事件处理程序
private void InitializeIJournalPlugins()
{
foreach (var plugin in journalPlugins)
plugin.LineAdding += OnJournalAdd;
}
这里的主要问题是,当我从我的插件中引发事件时,它给了我一个空引用异常。我 认为 这可能是因为主机应用程序知道插件事件,但插件不知道有任何东西正在监听它 - 我说得对吗? ;) 但是我怎样才能让它工作呢?
---- 更新 2015-05-26 15:08 ----
我暂时通过绑定到 WPF 按钮的命令调用它:
public ICommand JournalLineCommand
{
get
{
var eventArgs = new JournalLineEventArgs()
{
LineNumber = TextField
};
return new RelayCommand(() => LineAdding(this, eventArgs));
}
}
当我尝试引发它时,事件为空。
你不应该重新发明轮子。 Microsoft Patterns 团队已经有了一个用于编写具有插件架构的 WPF 应用程序的库。
Install-Package Prism.PubSubEvents
https://msdn.microsoft.com/en-us/library/ff921122.aspx
编辑:对于那些还在使用.net 2.0的人来说,当然还有Prism 2.x
我建议您重新考虑应用程序的体系结构,以便使用标准解决方案来解决您的问题。
正如 Aron 所指出的,您可以使用 EventAggregator 在整个应用程序中发布和订阅事件。您可以使用现有的许多实现中的任何一个,这里是一个使用轻量级 Caliburn.Micro.EventAggregator:
的简单示例
示例事件:
public class SampleEvent
{
public SampleEvent(int lineNumber)
{
LineNumber = lineNumber;
}
public int LineNumber { get; private set; }
}
示例发布者:
public class Publisher
{
public Publisher(IEventAggregator eventAggregator)
{
EventAggregator = eventAggregator;
}
public void PublishLineNumber(int lineNumber)
{
EventAggregator.Publish(new SampleEvent(lineNumber));
}
public IEventAggregator EventAggregator { get; private set; }
}
示例订阅者:
public class Subscriber : IHandle<SampleEvent>
{
public Subscriber(IEventAggregator eventAggregator)
{
EventAggregator = eventAggregator;
EventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; private set; }
public void Handle(SampleEvent message)
{
Console.WriteLine(message.LineNumber);
}
}
主要方法:
static void Main(string[] args)
{
EventAggregator eventAggregator = new EventAggregator();
Publisher publisher = new Publisher(eventAggregator);
Subscriber subscriber = new Subscriber(eventAggregator);
publisher.PublishLineNumber(5);
Console.ReadLine();
}
关于加载插件,我建议你使用
MEF 到 export/import 他们。
您可以将它们与 .NET 3.5 一起使用
注意:我需要使用.NET 3.5
我的测试应用程序分为三个部分(不同的项目和不同的dll); host-application, plugin-sdk and test-plugin.
宿主应用程序是一个 WPF 应用程序,有一个区域可以从插件加载视图。插件通过反射加载。这部分工作正常。
SDK 包含插件可以实现的通用接口。我的一个接口有一个插件必须实现的事件,当它被触发时,主应用程序需要监听它,以根据自定义 EventArgs 执行操作。 SDK 从主机应用程序和插件中引用。
首先是我的通用插件加载器:
public static class GenericPluginLoader<T>
{
public static ICollection<T> LoadPlugins(string path)
{
string[] dllFileNames = null;
if (Directory.Exists(path))
{
dllFileNames = Directory.GetFiles(path, "*.dll");
var assemblies = new List<Assembly>(dllFileNames.Length);
foreach (var dllFile in dllFileNames)
{
var an = AssemblyName.GetAssemblyName(dllFile);
var assembly = Assembly.Load(an);
assemblies.Add(assembly);
}
var pluginType = typeof(T);
var pluginTypes = new List<Type>();
foreach (var assembly in assemblies)
{
if (assembly != null)
{
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsInterface || type.IsAbstract)
{
continue;
}
else
{
if (type.GetInterface(pluginType.FullName) != null)
{
pluginTypes.Add(type);
}
}
}
}
}
var plugins = new List<T>(pluginTypes.Count);
foreach (var type in pluginTypes)
{
var plugin = (T)Activator.CreateInstance(type);
plugins.Add(plugin);
}
return plugins;
}
return null;
}
}
带有事件的接口我想要我的插件直到实现和自定义事件参数
public class JournalLineEventArgs : EventArgs
{
public string LineNumber { get; set; }
}
public interface IJournalPlugin
{
event EventHandler<JournalLineEventArgs> LineAdding;
}
来自我的插件的事件处理程序
public event EventHandler<JournalLineEventArgs> LineAdding;
在我的主机应用程序中,我将插件加载到列表中,然后添加事件处理程序
private void InitializeIJournalPlugins()
{
foreach (var plugin in journalPlugins)
plugin.LineAdding += OnJournalAdd;
}
这里的主要问题是,当我从我的插件中引发事件时,它给了我一个空引用异常。我 认为 这可能是因为主机应用程序知道插件事件,但插件不知道有任何东西正在监听它 - 我说得对吗? ;) 但是我怎样才能让它工作呢?
---- 更新 2015-05-26 15:08 ----
我暂时通过绑定到 WPF 按钮的命令调用它:
public ICommand JournalLineCommand
{
get
{
var eventArgs = new JournalLineEventArgs()
{
LineNumber = TextField
};
return new RelayCommand(() => LineAdding(this, eventArgs));
}
}
当我尝试引发它时,事件为空。
你不应该重新发明轮子。 Microsoft Patterns 团队已经有了一个用于编写具有插件架构的 WPF 应用程序的库。
Install-Package Prism.PubSubEvents
https://msdn.microsoft.com/en-us/library/ff921122.aspx
编辑:对于那些还在使用.net 2.0的人来说,当然还有Prism 2.x
我建议您重新考虑应用程序的体系结构,以便使用标准解决方案来解决您的问题。
正如 Aron 所指出的,您可以使用 EventAggregator 在整个应用程序中发布和订阅事件。您可以使用现有的许多实现中的任何一个,这里是一个使用轻量级 Caliburn.Micro.EventAggregator:
的简单示例示例事件:
public class SampleEvent
{
public SampleEvent(int lineNumber)
{
LineNumber = lineNumber;
}
public int LineNumber { get; private set; }
}
示例发布者:
public class Publisher
{
public Publisher(IEventAggregator eventAggregator)
{
EventAggregator = eventAggregator;
}
public void PublishLineNumber(int lineNumber)
{
EventAggregator.Publish(new SampleEvent(lineNumber));
}
public IEventAggregator EventAggregator { get; private set; }
}
示例订阅者:
public class Subscriber : IHandle<SampleEvent>
{
public Subscriber(IEventAggregator eventAggregator)
{
EventAggregator = eventAggregator;
EventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; private set; }
public void Handle(SampleEvent message)
{
Console.WriteLine(message.LineNumber);
}
}
主要方法:
static void Main(string[] args)
{
EventAggregator eventAggregator = new EventAggregator();
Publisher publisher = new Publisher(eventAggregator);
Subscriber subscriber = new Subscriber(eventAggregator);
publisher.PublishLineNumber(5);
Console.ReadLine();
}
关于加载插件,我建议你使用 MEF 到 export/import 他们。
您可以将它们与 .NET 3.5 一起使用