插件系统:如何处理主机应用程序和插件之间的事件 [英] Plugin-system: How to handle events between host application and plugin

查看:91
本文介绍了插件系统:如何处理主机应用程序和插件之间的事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:我需要使用 .NET 3.5

Note: I need to use .NET 3.5

我的测试应用程序分为三部分(不同的项目和不同的dll);主机应用程序、插件 sdk 和测试插件.

My test application is in three parts (different projects and different dlls); host-application, plugin-sdk and test-plugin.

宿主应用程序是一个 WPF 应用程序,有一个区域可以从插件的视图中加载.插件通过反射加载.这部分按预期工作.

The host application is a WPF application with an area to load in views from the plugins. Plugins is loaded through Reflection. This part work as supposed.

SDK 包含插件可以实现的通用接口.我的一个接口有一个插件必须实现的事件,当它被触发时,主应用程序需要监听它,以执行基于自定义 EventArgs 的操作.SDK 是从主机应用程序和插件中引用的.

The SDK contains common interfaces that a plugin can implement. One of my interfaces has an event that the plugin must implement and when it's triggered, the main application need to listen for it, to perform an action based on the a custom EventArgs. The SDK is referenced from both the host-application and the plugin.

首先是我的通用插件加载器:

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;
}

这里的主要问题是,当我从插件引发事件时,它给了我一个空引用异常.我认为这可能是因为宿主应用程序知道插件事件,但插件不知道有什么东西在监听它——我说得对吗?;) 但是我怎样才能做到这一点?

The main problem here is that when I raise the event from my plugin it gives me a null-reference exception. I think that this may be because the host application knows about the plugin event but the plugin does not know that anything is listening to it - am I right? ;) But how can I make this work?

---- 更新 2015-05-26 15:08 ----

我暂时通过绑定到 WPF 按钮的命令调用它:

I'm for the time being calling it through a Command that's bound to a WPF button:

public ICommand JournalLineCommand
{
    get
    {
        var eventArgs = new JournalLineEventArgs()
        {
            LineNumber = TextField
        };             
        return new RelayCommand(() => LineAdding(this, eventArgs));
    }
}

当我尝试引发它时,事件为空.

When I try to raise it the event is null.

推荐答案

我建议您重新考虑应用程序的架构,以使用标准解决方案来解决您的问题.

I would advise you to rethink the architecture of your application to use standard solutions to your problem.

正如 Aron 所指出的,您可以使用 EventAggregator 来发布和订阅整个应用程序中的事件.您可以使用现有的众多实现中的任何一种,这里是一个使用轻量级 Caliburn.Micro.EventAggregator:

As Aron points, you can make use of an EventAggregator to publish and subscribe to events within your whole application. You can make use of any of the many implementations that are out there, here is a simple example using the lightweight 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 导出/导入它们.

As for loading the plugins, I would recommend you to use MEF to export/import them.

您可以在 .NET 3.5 中同时使用它们

You can use both of them with .NET 3.5

这篇关于插件系统:如何处理主机应用程序和插件之间的事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆