12

プラグインアーキテクチャを使用して機能を拡張するアプリケーションに取り組んでいます。プラグインからWPFUIを読み込むための最良の方法は何ですか?

利用可能なすべてのプラグインをリストしたリストボックスがあります。プラグインを選択すると、プラグインで定義されているWPFUIがに表示されますContentControl。私が考えたオプションは次のとおりです。

  • UserControl特定のインターフェースを実装するを作成する必要があります。これでプラグインの作成が簡単になると思います。インターフェイスを実装すれば、準備は完了です。このメソッドに関する私の質問は、を動的にロードする方法UserControlですContentControl。また、MVVMデザインパターンを使用しているので、aよりもaのDataTemplate方が好ましいようUserControlです。
  • DataTemplateプラグインからaをロードできるようにします。これには、プラグインに特定の名前のXAMLファイルが含まれている必要があると思います。私のアプリケーションは、この質問にDataTemplate示されているように、をリソースディクショナリに読み込みます。 私はこれに似た質問をかなり多く見ましたが、通常、そこから取得するために追加の事前定義されたアセンブリを1つロードするだけで済みます。この問題では、不明なアセンブリをいくつでも検索する必要があります。DataTemplatesDataTemplates

2番目のオプションを選択した場合、この回答の説明DataTemplateと同様に選択できると思います。

どちらの方法が良いと思いますか?それとも、これを達成するためのより良い方法がありますか?

4

3 に答える 3

13

私はで述べたようなことをしましたDataTemplatesMEFを使用してプラグインをロードし、起動時にとDictionaryへの参照を含むをロードしました。プラグインは、3つの主要コンポーネントを使用して構築されています。ViewModelView

IBasePlugin.cs

このシンプルなインターフェースにより、プラグインのスケルトンを作成できます。これは、を使用しImportてメインアプリケーションへのプラグインに使用するものであるため、非常に基本的なもののみが含まれますMEF

public interface IBasePlugin
{
    WorkspaceViewModel ViewModel { get; }
    ResourceDictionary View{ get; }
}

Plugin.cs

次の部分はPlugin.csファイルです。プラグインのすべてのプロパティと、必要なすべての参照が含まれています。View&などViewModel

[Export(typeof(IBasePlugin))]
public class Plugin : IBasePlugin
{
    [Import]
    private MyPluginViewModel _viewModel { get; set; }
    private ResourceDictionary _viewDictionary = new ResourceDictionary();

    [ImportingConstructor]
    public Plugin()
    {
        // First we need to set up the View components.
        _viewDictionary.Source =
            new Uri("/Extension.MyPlugin;component/View.xaml",
            UriKind.RelativeOrAbsolute);
    }

    ....Properties...

}

View.xaml

これにはDataTemplate、プラグインViewとへの参照が含まれていますViewModel。これはPlugin.cs、メインアプリケーションにロードするために使用するものです。これにより、アプリケーションは、WPFすべてをバインドする方法を知ることができます。

<DataTemplate DataType="{x:Type vm:MyPluginViewModel}">
    <vw:MyPluginView/>

次に、MEFを使用してすべてのプラグインをロードし、プラグインの処理を担当するワークスペースにフィードして、使用可能なすべてのプラグインを表示するために使用されるにViewModel保存します。ObservableCollection

プラグインのロードに使用するコードは、次のようになります。

var plugins = Plugins.OrderBy(p => p.Value.ViewModel.HeaderText);
foreach (var app in plugins)
{
    // Take the View from the Plugin and Merge it with,
    // our Applications Resource Dictionary.
    Application.Current.Resources.MergedDictionaries.Add(app.Value.View)

    // THen add the ViewModel of our plugin to our collection of ViewModels.
    var vm = app.Value.ViewModel;
    Workspaces.Add(vm);
}

との両方がプラグインからアプリケーションにロードされるDictinoaryViewModel、たとえばを使用してコレクションを表示できますTabControl

<TabControl ItemsSource="{Binding Workspaces}"/>

私もここで同様の答えを出しましたが、あなたが面白いと思うかもしれないいくつかの追加の詳細があります。

于 2012-08-30T20:15:52.473 に答える
2

あなたが探しているものはすでにPrismで達成されているようです。リージョンを定義すると、実行時にモジュールがロードされ、それらのリージョンのビューがある場合とない場合があります。これは、すべてのアプリケーションがPrismから派生したモジュール性の概念に基づいて構築されている場合に機能します。他にもありますが、Prismはこれをかなり広範囲に処理しています。

于 2012-08-30T20:16:25.873 に答える
2

富士と同じようなアプローチをしています。ViewModel唯一の違いは、とResourceDictionaryを互いに独立してエクスポートするため、それらはまだ緩く結合されていることです。

[Export(typeof(IPlugin)]//i use metadata too
public class Viewmodel1 {}

[Export(typeof(IPlugin)]//i use metadata too
public class Viewmodel2 {}

[ResourceDictionaryExport]
public partial class MyResourceDictionary 
{
    public MyResourceDictionary ()
    {
        InitializeComponent();
    }
}

プラグインアプリケーションで、すべてを追加しますResourceDictionaries

app.xaml.cs

 [ImportMany("Resourcen", typeof(ResourceDictionary))]
 private IEnumerable<ResourceDictionary> _importResourcen;

 foreach (var resourceDictionary in _importResourcen)
 {
     this.Resources.MergedDictionaries.Add(resourceDictionary);
 }

完全を期すために

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ResourceDictionaryExportAttribute : ExportAttribute
{
    public ResourceDictionaryExportAttribute() : base("Resourcen", typeof(ResourceDictionary))
    {

    }
}
于 2012-08-31T08:06:18.923 に答える