如何将数据从主机绑定到插件

How to bind data from host to plugin

如何使用 MEF 将数据从主机绑定到插件?

所以事情是:

我目前拥有的:

我需要的: - 插件需要将数据从 MainViewModel 绑定到插件 UI。 - 更改插件 UI 中的 属性 必须更新 MainViewModel 中的数据并更新所有其他插件中的 UI。

插件接口:

public interface IPlugin
{

}
   public interface IPluginData
{
   string Name { get; }
}

MainViewModel:(部分)

private MyModel myfirstmodel; 
private DirectoryCatalog catalog;
private CompositionContainer container;

[ImportMany] 
IEnumerable<Lazy<IPlugin, IPluginData>> Plugins;

public MainWindowViewModel()
{
    string pluginPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    pluginPath = Path.Combine(pluginPath, "plugins");
    if (!Directory.Exists(pluginPath))
    Directory.CreateDirectory(pluginPath);
    catalog = new DirectoryCatalog(pluginPath, "*.dll");
    container = new CompositionContainer(catalog);

    try
    {
        this.container.ComposeParts(this);
    }
    catch (CompositionException compositionException)
    {
        Console.WriteLine(compositionException.ToString());
    }
}

模特

public class MyModel
{
    private string message;
    private int number;
    private DateTime date;

    public string Message { get { return message; } set { message = value; } }
    public int Number { get { return number; } set { number = value; } }
    public DateTime Date { get { return date; } set { date = value; } }
}

插件

[Export(typeof(IPlugin))]
[ExportMetadata("Name", "MyFirstPlugin")]
public partial class MyFirstPlugin : UserControl, IPlugin
{

    public MyFirstPlugin()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //Change the message in MainWindowViewModel and the date when it gets changed.
    }
}

我试过使用 INotifyPropertyChanged 但没达到那么远..

是否有人为此提供了非常好的教程,或者可以告诉我如何操作? 我希望 "how to" 而不仅仅是 "just use INotifyPropertyChanged".

这可能吗?

要与 ModelView 交换数据,您需要一个表示主机的接口。您还需要一个容器或方法来插入表示插件的可视控件。

 interface IPluginHost 
 {
      MyModelView ModelView {get;}
      MyMainWindow View {get;}
 }

 interface IPlugin
 {
    void Register(IPluginHost host);
    void Unregister();
 }

组合完成后,您向主机注册插件,这将使他们能够访问模型视图:

this.container.ComposeParts(this);
foreach(var plugin in Plugins)
{
     plugin.Value.Register(this);
}

What I need: - the plugins need to bind the data from the MainViewModel to the plugin UI.

插件可以在它的 Register 方法中访问 MainViewModel,它可以执行所有必要的绑定。这是可以完成的多种方法之一:

 public partial class MyFirstPlugin : UserControl, IPlugin
 {
      ...

     void Register(IPluginHost host)
     {
          _host = host;

           // Attach main model view as data context
           this.DataContext = host.ModelView; 

           // Add control to the host's container
           var mainWindow = host.View;

           mainWindow.AddPluginControl((UserControl)this);
     }

     void Unregister()
     {
          if (_host == null)
          { 
              return;
          }

          this.DataContext = null;
          _host.View.RemovePluginControl(this);
          _host = null;
     }

     IPluginHost _host;

 }

AddPluginControl() ,RemovePluginControl() 是 public 将插件的可视元素插入容器的方法。

就我个人而言,我认为您的做法是错误的,MEF 是专门为执行此类管道而设计的,因此您不必这样做。

在适当的 MVVM 应用程序中,视图通常是通过数据模板创建的,即使您不这样做,通常也需要导入其他全局数据,例如画笔和行为等。如果您这样做使用模板(你真的应该这样做)然后你不需要显式导出你的控件;简单地通过模板引用它们将导致它们也被导入。

您可以通过导入程序的另一遍在实践中实现这一点。给每个插件一个自定义的 ResourceDictionary 并将所有全局 data/templates/UI 资源等放在那里(所以它实际上是插件版本的 App.xaml)。诀窍是为那些 ResourceDictionaries 提供它们自己的代码隐藏文件,并用 Export 装饰 those classes。然后,您所有的主要应用程序所要做的就是导入 ResourceDictionary 类型的所有 classes(或者最好是派生的 class 或 returns 字典的接口)并将每一个添加到 Application.Current.Resources.MergedDictionaries.从那时起,您的插件开发就好像它们在您以通常方式静态链接的 DLL 项目中一样。