【问题标题】:How to bind data from host to plugin如何将数据从主机绑定到插件
【发布时间】:2015-10-01 17:10:48
【问题描述】:

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

所以事情是这样的:

  • 我使用 MVVM,所以我有我的模型、视图模型和视图。
  • 我想使用 MEF 来扩展我的应用程序。
  • 我想将所有数据存储在MainViewModel 中,这样每个插件都可以使用实际数据。
  • 插件是UserControl,在MainViewModel 中将显示为ContentControl

到目前为止我所拥有的:

  • MainViewModel
  • 型号
  • MainViewModel 到 View 的数据绑定。
  • 从文件夹 X 导入插件

我需要什么: - 插件需要将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,但没有走那么远..

有没有人有一个非常好的教程或者可以告诉我如何做到这一点? 我会很感激“如何”,而不仅仅是“只使用INotifyPropertyChanged”。

这可能吗?

【问题讨论】:

    标签: c# wpf mvvm data-binding mef


    【解决方案1】:

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

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

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

    【讨论】:

    • 你能给我一个简短的例子来说明如何做到这一点吗?以前从未使用过 ResouceDirectorys.. :c
    • 我找到了 6 年前的 SO 帖子,我最初是从那里学到的,它是 the upvoted answer to this question。你只需要记住在构造函数中做InitializeComponent();,否则导入时字典会显示为空。
    • 谢谢。我会试试看。 :)
    • 我想在我的应用程序中添加一个 UserControl。这个 UserControl 将被添加到一个整体的“视图”中,并从视图中绑定数据。并且当主机中的数据发生变化时,用户控件也必须发生变化。我如何创建它?
    • @bene_rawr 您可能需要将此作为第二个问题提出,并提供有关您的确切案例的更具体细节,因为有许多不同的方法可以完成。一般来说,您要么将ContentControlDataTemplate 一起使用,要么将Style 与数据触发器一起使用。
    【解决方案2】:

    为了与 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);
    }
    

    我需要什么: - 插件需要绑定来自 MainViewModel 到插件 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() 是将插件的可视元素插入容器的公共方法。

    【讨论】:

    • 嗯..我没有得到注册的部分..“注册”方法来自哪里,它是做什么的?很高兴知道如何解决这个问题。 :c
    • Register 是我添加到 IPlugin 接口声明的方法。我在答案中添加了一个示例。
    • 好的。我现在通过插件注册得到这个。但是我在哪里使用 IPluginHost 接口呢?我有一个用于 IPluginHost 的 ModelView 并组成所有插件。但是当我将 IPluginHost 添加到 ModelView 时,我必须在 MyModelView 中设置 IPluginHost.MyModelView.. 这让我很困惑..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多