├── src
├── SamplePlugin
│ ├── Properties
│ │ ├── Resources.de.Designer.cs
│ │ ├── Settings.settings
│ │ ├── AssemblyInfo.cs
│ │ ├── Settings.Designer.cs
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.de.resx
│ │ └── Resources.resx
│ ├── Applications
│ │ ├── ISampleView.cs
│ │ ├── IDialogView.cs
│ │ ├── DialogViewModel.cs
│ │ ├── SampleViewModel.cs
│ │ └── SampleController.cs
│ ├── Presentation
│ │ ├── SampleView.xaml.cs
│ │ ├── DialogWindow.xaml.cs
│ │ ├── DialogWindow.xaml
│ │ └── SampleView.xaml
│ ├── LogServiceExtensions.cs
│ ├── packages.config
│ ├── App.config
│ ├── PluginController.cs
│ └── SamplePlugin.csproj
├── Shell
│ ├── Applications
│ │ ├── Views
│ │ │ ├── ITaskView.cs
│ │ │ ├── IShellView.cs
│ │ │ └── ILogView.cs
│ │ ├── Services
│ │ │ ├── PluginInfo.cs
│ │ │ ├── PluginManager.cs
│ │ │ ├── PluginHostProxy.cs
│ │ │ └── PluginMetadataReader.cs
│ │ ├── ViewModels
│ │ │ ├── LogViewModel.cs
│ │ │ ├── ShellViewModel.cs
│ │ │ └── TaskViewModel.cs
│ │ ├── RemoteServices
│ │ │ ├── LogService.cs
│ │ │ ├── AddressBookService.cs
│ │ │ └── EventAggregator.cs
│ │ └── Controllers
│ │ │ └── ModuleController.cs
│ ├── Presentation
│ │ ├── Resources
│ │ │ ├── ConverterResources.xaml
│ │ │ ├── BrushResources.xaml
│ │ │ ├── LayoutResources.xaml
│ │ │ └── ControlResources.xaml
│ │ └── Views
│ │ │ ├── TaskView.xaml.cs
│ │ │ ├── ShellWindow.xaml.cs
│ │ │ ├── LogView.xaml
│ │ │ ├── LogView.xaml.cs
│ │ │ ├── TaskView.xaml
│ │ │ └── ShellWindow.xaml
│ ├── Foundation
│ │ └── TaskHelper.cs
│ ├── App.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── App.xaml
│ ├── packages.config
│ ├── App.xaml.cs
│ └── Shell.csproj
├── PluginHost
│ ├── App.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Bootstrapper.cs
│ ├── PluginLoader.cs
│ ├── Program.cs
│ ├── RuntimeConfig.cs
│ └── PluginHost.csproj
├── AddressBookPlugin
│ ├── Applications
│ │ ├── IContactListView.cs
│ │ ├── ContactListViewModel.cs
│ │ ├── SampleDataProvider.cs
│ │ └── ContactController.cs
│ ├── packages.config
│ ├── Presentation
│ │ ├── ContactListView.xaml.cs
│ │ ├── ControlResources.xaml
│ │ └── ContactListView.xaml
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── LogServiceExtensions.cs
│ ├── Domain
│ │ ├── AddressBookRoot.cs
│ │ └── Contact.cs
│ ├── PluginController.cs
│ └── AddressBookPlugin.csproj
├── Shell.Interfaces
│ ├── ILogService.cs
│ ├── IAddressBookService.cs
│ ├── IEventAggregator.cs
│ ├── TaskChangedEventArgs.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ContactDto.cs
│ └── Shell.Interfaces.csproj
├── PluginFramework
│ ├── IPluginController.cs
│ ├── Internals
│ │ ├── IPluginLoader.cs
│ │ ├── RemoteService.cs
│ │ ├── TypedArgs.cs
│ │ └── NativeHandleContractInsulator.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── DisposableObject.cs
│ ├── RemoteServiceLocator.cs
│ └── PluginFramework.csproj
└── WpfPluginSample.sln
├── CONTRIBUTING.md
├── .gitignore
├── LICENSE
└── README.md
/src/SamplePlugin/Properties/Resources.de.Designer.cs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | I'm not ready to accept contributions at this time. Please post any feedback to the Issues list. Thank you!
2 |
--------------------------------------------------------------------------------
/src/Shell/Applications/Views/ITaskView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.Shell.Applications.Views
4 | {
5 | public interface ITaskView : IView
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Applications/ISampleView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.SamplePlugin.Applications
4 | {
5 | public interface ISampleView : IView
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/PluginHost/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Applications/IContactListView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.AddressBookPlugin.Applications
4 | {
5 | public interface IContactListView : IView
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Shell.Interfaces/ILogService.cs:
--------------------------------------------------------------------------------
1 | namespace WpfPluginSample.Shell.Interfaces
2 | {
3 | public interface ILogService
4 | {
5 | void Message(string text);
6 |
7 | void Error(string text);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Shell/Applications/Views/IShellView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.Shell.Applications.Views
4 | {
5 | public interface IShellView : IView
6 | {
7 | void Show();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/PluginFramework/IPluginController.cs:
--------------------------------------------------------------------------------
1 | namespace WpfPluginSample.PluginFramework
2 | {
3 | public interface IPluginController
4 | {
5 | void Initialize();
6 |
7 | object CreateMainView();
8 |
9 | void Shutdown();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Shell.Interfaces/IAddressBookService.cs:
--------------------------------------------------------------------------------
1 | namespace WpfPluginSample.Shell.Interfaces
2 | {
3 | public interface IAddressBookService
4 | {
5 | void SelectContact(ContactDto contact);
6 |
7 | void ContactDeleted(ContactDto contact);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Applications/IDialogView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.SamplePlugin.Applications
4 | {
5 | public interface IDialogView : IView
6 | {
7 | bool? ShowDialog();
8 |
9 | void Close();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Shell.Interfaces/IEventAggregator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.Shell.Interfaces
4 | {
5 | public interface IEventAggregator
6 | {
7 | void Publish(TEvent sampleEvent);
8 |
9 | IObservable GetEvent();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Shell/Applications/Views/ILogView.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 |
3 | namespace WpfPluginSample.Shell.Applications.Views
4 | {
5 | public interface ILogView : IView
6 | {
7 | void AppendOutputText(string text);
8 |
9 | void AppendErrorText(string text);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Presentation/SampleView.xaml.cs:
--------------------------------------------------------------------------------
1 | using WpfPluginSample.SamplePlugin.Applications;
2 |
3 | namespace WpfPluginSample.SamplePlugin.Presentation
4 | {
5 | public partial class SampleView : ISampleView
6 | {
7 | public SampleView()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Presentation/DialogWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using WpfPluginSample.SamplePlugin.Applications;
2 |
3 | namespace WpfPluginSample.SamplePlugin.Presentation
4 | {
5 | public partial class DialogWindow : IDialogView
6 | {
7 | public DialogWindow()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Presentation/ContactListView.xaml.cs:
--------------------------------------------------------------------------------
1 | using WpfPluginSample.AddressBookPlugin.Applications;
2 |
3 | namespace WpfPluginSample.AddressBookPlugin.Presentation
4 | {
5 | public partial class ContactListView : IContactListView
6 | {
7 | public ContactListView()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/PluginFramework/Internals/IPluginLoader.cs:
--------------------------------------------------------------------------------
1 | using System.AddIn.Contract;
2 | using System.Runtime.Remoting.Messaging;
3 |
4 | namespace WpfPluginSample.PluginFramework.Internals
5 | {
6 | public interface IPluginLoader
7 | {
8 | INativeHandleContract LoadPlugin(string assembly, string typeName);
9 |
10 | [OneWay]
11 | void Shutdown();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Resources/ConverterResources.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Views/TaskView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using WpfPluginSample.Shell.Applications.Views;
3 |
4 | namespace WpfPluginSample.Shell.Presentation.Views
5 | {
6 | [Export(typeof(ITaskView))]
7 | public partial class TaskView : ITaskView
8 | {
9 | public TaskView()
10 | {
11 | InitializeComponent();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Resources/BrushResources.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Shell/Foundation/TaskHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace WpfPluginSample.Shell.Foundation
6 | {
7 | public static class TaskHelper
8 | {
9 | public static Task Run(Action action, TaskScheduler scheduler)
10 | {
11 | return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, scheduler);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | My test settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | .vs/
6 | *.suo
7 | *.user
8 |
9 | # Build results
10 | out/
11 | [Dd]ebug/
12 | [Rr]elease/
13 | [Bb]in/
14 | [Oo]bj/
15 |
16 | # Unit test results
17 | [Tt]est[Rr]esults
18 |
19 | # NuGet Packages Directory
20 | packages/
21 |
22 | # NuGet package restore lockfiles
23 | project.lock.json
24 |
25 | # Visual Studio Upgrade Log
26 | UpgradeLog*.htm
--------------------------------------------------------------------------------
/src/Shell.Interfaces/TaskChangedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.Shell.Interfaces
4 | {
5 | [Serializable]
6 | public class TaskChangedEventArgs : EventArgs
7 | {
8 | public TaskChangedEventArgs(string subject, ContactDto assignedTo)
9 | {
10 | Subject = subject;
11 | AssignedTo = assignedTo;
12 | }
13 |
14 | public string Subject { get; }
15 |
16 | public ContactDto AssignedTo { get; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Resources/LayoutResources.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 | 7
5 |
6 | 22
7 |
8 | 7
9 |
10 | 22
11 |
12 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Presentation/ControlResources.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
10 |
11 |
--------------------------------------------------------------------------------
/src/PluginFramework/Internals/RemoteService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security;
3 |
4 | namespace WpfPluginSample.PluginFramework.Internals
5 | {
6 | ///
7 | /// Base class for singleton services which can be accessed over application domain boundaries via remoting.
8 | ///
9 | [Serializable]
10 | public abstract class RemoteService : MarshalByRefObject
11 | {
12 | [SecurityCritical]
13 | public override object InitializeLifetimeService()
14 | {
15 | return null;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Shell/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/PluginFramework/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("PluginFramework")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("jbe2277")]
8 | [assembly: AssemblyProduct("PluginFramework")]
9 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 |
15 | [assembly: AssemblyVersion("1.0.0.0")]
16 | [assembly: AssemblyFileVersion("1.0.0.0")]
17 |
--------------------------------------------------------------------------------
/src/Shell.Interfaces/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("Shell.Interfaces")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("jbe2277")]
8 | [assembly: AssemblyProduct("Shell.Interfaces")]
9 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 |
15 | [assembly: AssemblyVersion("1.0.0.0")]
16 | [assembly: AssemblyFileVersion("1.0.0.0")]
17 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Views/ShellWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using System.Windows;
3 | using WpfPluginSample.Shell.Applications.Views;
4 |
5 | namespace WpfPluginSample.Shell.Presentation.Views
6 | {
7 | [Export(typeof(IShellView))]
8 | public partial class ShellWindow : IShellView
9 | {
10 | public ShellWindow()
11 | {
12 | InitializeComponent();
13 | Loaded += LoadedHandler;
14 | }
15 |
16 | private void LoadedHandler(object sender, RoutedEventArgs e)
17 | {
18 | pluginsBox.Focus();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Resources/ControlResources.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
12 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Shell.Interfaces/ContactDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.Shell.Interfaces
4 | {
5 | [Serializable]
6 | public class ContactDto
7 | {
8 | public ContactDto(string firstname, string lastname, string email)
9 | {
10 | Firstname = firstname;
11 | Lastname = lastname;
12 | Email = email;
13 | }
14 |
15 | public string Firstname { get; }
16 |
17 | public string Lastname { get; }
18 |
19 | public string Email { get; }
20 |
21 | public override string ToString()
22 | {
23 | return Firstname + " " + Lastname;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/PluginHost/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("PluginHost")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("jbe2277")]
9 | [assembly: AssemblyProduct("PluginHost")]
10 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
16 |
17 | [assembly: AssemblyVersion("1.0.0.0")]
18 | [assembly: AssemblyFileVersion("1.0.0.0")]
19 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("SamplePlugin")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("jbe2277")]
9 | [assembly: AssemblyProduct("Test Sample Plugin")]
10 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
16 |
17 | [assembly: AssemblyVersion("1.1.0.0")]
18 | [assembly: AssemblyFileVersion("1.1.0.0")]
19 |
--------------------------------------------------------------------------------
/src/SamplePlugin/LogServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using WpfPluginSample.Shell.Interfaces;
3 |
4 | namespace WpfPluginSample.SamplePlugin
5 | {
6 | internal static class LogServiceExtensions
7 | {
8 | public static string Prefix => "SamplePlugin (" + Process.GetCurrentProcess().Id + "): ";
9 |
10 | public static void Message(this ILogService logService, string text, bool usePrefix)
11 | {
12 | logService.Message(usePrefix ? Prefix + text : text);
13 | }
14 |
15 | public static void Error(this ILogService logService, string text, bool usePrefix)
16 | {
17 | logService.Error(usePrefix ? Prefix + text : text);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyTitle("AddressBookPlugin")]
6 | [assembly: AssemblyDescription("")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("jbe2277")]
9 | [assembly: AssemblyProduct("Address Book Plugin")]
10 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | [assembly: ComVisible(false)]
15 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
16 |
17 | [assembly: AssemblyVersion("1.0.0.0")]
18 | [assembly: AssemblyFileVersion("1.0.0.0")]
19 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/LogServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using WpfPluginSample.Shell.Interfaces;
3 |
4 | namespace WpfPluginSample.AddressBookPlugin
5 | {
6 | internal static class LogServiceExtensions
7 | {
8 | public static string Prefix => "AddressBookPlugin (" + Process.GetCurrentProcess().Id + "): ";
9 |
10 | public static void Message(this ILogService logService, string text, bool usePrefix)
11 | {
12 | logService.Message(usePrefix ? Prefix + text : text);
13 | }
14 |
15 | public static void Error(this ILogService logService, string text, bool usePrefix)
16 | {
17 | logService.Error(usePrefix ? Prefix + text : text);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/SamplePlugin/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Applications/DialogViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 | using System.Windows.Input;
3 |
4 | namespace WpfPluginSample.SamplePlugin.Applications
5 | {
6 | public class DialogViewModel : ViewModel
7 | {
8 | public DialogViewModel(IDialogView view) : base(view)
9 | {
10 | OkCommand = new DelegateCommand(Close);
11 | CancelCommand = new DelegateCommand(Close);
12 | }
13 |
14 | public ICommand OkCommand { get; }
15 |
16 | public ICommand CancelCommand { get; }
17 |
18 | public void ShowDialog()
19 | {
20 | ViewCore.ShowDialog();
21 | }
22 |
23 | private void Close()
24 | {
25 | ViewCore.Close();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Domain/AddressBookRoot.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Waf.Foundation;
4 |
5 | namespace WpfPluginSample.AddressBookPlugin.Domain
6 | {
7 | public class AddressBookRoot : Model
8 | {
9 | private readonly ObservableCollection contacts;
10 |
11 | public AddressBookRoot()
12 | {
13 | contacts = new ObservableCollection();
14 | }
15 |
16 | public IReadOnlyList Contacts => contacts;
17 |
18 | public void AddContact(Contact contact)
19 | {
20 | contacts.Add(contact);
21 | }
22 |
23 | public void RemoveContact(Contact contact)
24 | {
25 | contacts.Remove(contact);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Shell/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 | using System.Windows;
5 |
6 | [assembly: AssemblyTitle("Wpf Plugin Demo")]
7 | [assembly: AssemblyDescription("")]
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("jbe2277")]
10 | [assembly: AssemblyProduct("Wpf Plugin Demo")]
11 | [assembly: AssemblyCopyright("Copyright © jbe2277 2017")]
12 | [assembly: AssemblyTrademark("")]
13 | [assembly: AssemblyCulture("")]
14 |
15 | [assembly: ComVisible(false)]
16 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
17 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
18 |
19 | [assembly: AssemblyVersion("1.0.0.0")]
20 | [assembly: AssemblyFileVersion("1.0.0.0")]
21 |
--------------------------------------------------------------------------------
/src/Shell/App.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Shell/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/SamplePlugin/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Every Plugin has it's own settings.
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Shell/Applications/Services/PluginInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.Shell.Applications.Services
4 | {
5 | [Serializable]
6 | public class PluginInfo
7 | {
8 | public PluginInfo(string assemblyFile, string pluginControllerName, string name, string version, string company, string copyright)
9 | {
10 | AssemblyFile = assemblyFile;
11 | PluginControllerName = pluginControllerName;
12 | Name = name;
13 | Version = version;
14 | Company = company;
15 | Copyright = copyright;
16 | }
17 |
18 | public string AssemblyFile { get; }
19 |
20 | public string PluginControllerName { get; }
21 |
22 | public string Name { get; }
23 |
24 | public string Version { get; }
25 |
26 | public string Company { get; }
27 |
28 | public string Copyright { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Applications/ContactListViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Waf.Applications;
3 | using System.Windows.Input;
4 | using WpfPluginSample.AddressBookPlugin.Domain;
5 |
6 | namespace WpfPluginSample.AddressBookPlugin.Applications
7 | {
8 | public class ContactListViewModel : ViewModel
9 | {
10 | private Contact selectedContact;
11 |
12 | public ContactListViewModel(IContactListView view) : base(view)
13 | {
14 | }
15 |
16 | public IReadOnlyList Contacts { get; set; }
17 |
18 | public Contact SelectedContact
19 | {
20 | get { return selectedContact; }
21 | set { SetProperty(ref selectedContact, value); }
22 | }
23 |
24 | public ICommand SelectContactCommand { get; set; }
25 |
26 | public ICommand DeleteContactCommand { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Views/LogView.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Shell/Presentation/Views/LogView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using System.Windows.Controls;
3 | using System.Windows.Documents;
4 | using System.Windows.Media;
5 | using WpfPluginSample.Shell.Applications.Views;
6 |
7 | namespace WpfPluginSample.Shell.Presentation.Views
8 | {
9 | [Export(typeof(ILogView))]
10 | public partial class LogView : ILogView
11 | {
12 | public LogView()
13 | {
14 | InitializeComponent();
15 | outputBox.TextChanged += OutputBoxTextChanged;
16 | }
17 |
18 | public void AppendOutputText(string text)
19 | {
20 | outputParagraph.Inlines.Add(text);
21 | }
22 |
23 | public void AppendErrorText(string text)
24 | {
25 | outputParagraph.Inlines.Add(new Run(text) { Foreground = (Brush)FindResource("ErrorForeground") });
26 | }
27 |
28 | private void OutputBoxTextChanged(object sender, TextChangedEventArgs e)
29 | {
30 | outputBox.ScrollToEnd();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Shell/Applications/ViewModels/LogViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Composition;
3 | using System.Waf.Applications;
4 | using WpfPluginSample.Shell.Applications.RemoteServices;
5 | using WpfPluginSample.Shell.Applications.Views;
6 |
7 | namespace WpfPluginSample.Shell.Applications.ViewModels
8 | {
9 | [Export]
10 | public class LogViewModel : ViewModel
11 | {
12 | [ImportingConstructor]
13 | public LogViewModel(ILogView view, LogService logService) : base(view)
14 | {
15 | logService.MessageCallback = AppendOutputText;
16 | logService.ErrorCallback = AppendErrorText;
17 | }
18 |
19 | public void AppendOutputText(string text)
20 | {
21 | ViewCore.AppendOutputText(DateTime.Now.ToString("HH:mm:ss.fff") + " " + text + Environment.NewLine);
22 | }
23 |
24 | public void AppendErrorText(string text)
25 | {
26 | ViewCore.AppendErrorText(DateTime.Now.ToString("HH:mm:ss.fff") + " " + text + Environment.NewLine);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Shell/Applications/RemoteServices/LogService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Composition;
3 | using System.Threading.Tasks;
4 | using WpfPluginSample.PluginFramework.Internals;
5 | using WpfPluginSample.Shell.Foundation;
6 | using WpfPluginSample.Shell.Interfaces;
7 |
8 | namespace WpfPluginSample.Shell.Applications.RemoteServices
9 | {
10 | [Export]
11 | public class LogService : RemoteService, ILogService
12 | {
13 | private readonly TaskScheduler taskScheduler;
14 |
15 | public LogService()
16 | {
17 | taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
18 | }
19 |
20 | public Action MessageCallback { get; set; }
21 |
22 | public Action ErrorCallback { get; set; }
23 |
24 | public void Message(string text)
25 | {
26 | TaskHelper.Run(() => MessageCallback?.Invoke(text), taskScheduler);
27 | }
28 |
29 | public void Error(string text)
30 | {
31 | TaskHelper.Run(() => ErrorCallback?.Invoke(text), taskScheduler);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 jbe2277
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Applications/SampleViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Applications;
2 | using System.Windows.Input;
3 | using WpfPluginSample.SamplePlugin.Properties;
4 |
5 | namespace WpfPluginSample.SamplePlugin.Applications
6 | {
7 | public class SampleViewModel : ViewModel
8 | {
9 | private string taskSubject;
10 |
11 | public SampleViewModel(ISampleView view) : base(view)
12 | {
13 | }
14 |
15 | public string TestApplicationSetting => Settings.Default.TestSetting;
16 |
17 | public ICommand ShowDialogCommand { get; set; }
18 |
19 | public string TaskSubject
20 | {
21 | get { return taskSubject; }
22 | set { SetProperty(ref taskSubject, value); }
23 | }
24 |
25 | public ICommand BlockUIThreadCommand { get; set; }
26 |
27 | public ICommand ExceptionUIThreadCommand { get; set; }
28 |
29 | public ICommand ExceptionBackgroundThreadCommand { get; set; }
30 |
31 | public ICommand ExceptionTaskCommand { get; set; }
32 |
33 | public ICommand RunGarbageCollectorCommand { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/PluginFramework/Internals/TypedArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.PluginFramework.Internals
4 | {
5 | public class TypedArgs
6 | {
7 | public TypedArgs(int parentProcessId, string assemblyFile, string instanceName)
8 | {
9 | ParentProcessId = parentProcessId;
10 | AssemblyFile = assemblyFile;
11 | InstanceName = instanceName;
12 | }
13 |
14 |
15 | public int ParentProcessId { get; }
16 |
17 | public string AssemblyFile { get; }
18 |
19 | public string InstanceName { get; }
20 |
21 |
22 | public static TypedArgs FromArgs(string[] args)
23 | {
24 | int parentProcessId = Convert.ToInt32(args[0]);
25 | var assemblyFile = args[1];
26 | var name = args[2];
27 | return new TypedArgs(parentProcessId, assemblyFile, name);
28 | }
29 |
30 | public string ToArgs()
31 | {
32 | // Enclose the AssemblyFile within " chars because the path might contain spaces.
33 | return ParentProcessId + " \"" + AssemblyFile + "\" " + InstanceName;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Domain/Contact.cs:
--------------------------------------------------------------------------------
1 | using System.Waf.Foundation;
2 |
3 | namespace WpfPluginSample.AddressBookPlugin.Domain
4 | {
5 | public class Contact : Model
6 | {
7 | private string firstname;
8 | private string lastname;
9 | string company;
10 | private string email;
11 | private string phone;
12 |
13 | public string Firstname
14 | {
15 | get { return firstname; }
16 | set { SetProperty(ref firstname, value); }
17 | }
18 |
19 | public string Lastname
20 | {
21 | get { return lastname; }
22 | set { SetProperty(ref lastname, value); }
23 | }
24 |
25 | public string Company
26 | {
27 | get { return company; }
28 | set { SetProperty(ref company, value); }
29 | }
30 |
31 | public string Email
32 | {
33 | get { return email; }
34 | set { SetProperty(ref email, value); }
35 | }
36 |
37 | public string Phone
38 | {
39 | get { return phone; }
40 | set { SetProperty(ref phone, value); }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WPF Plugin Sample
2 | This reference sample application shows how to create a Plugin Framework for a WPF application. The design goal is to enable dynamic composition of isolatable Plugins.
3 |
4 | ## Use Case
5 | Enable customers and partners to extend the functionality of a software product. Example: Browser Extensions.
6 |
7 | ## Features
8 | - Stability: Plugins are created in an own process. This way instabilities of the Plugins do not affect the Host application.
9 | - Side-by-side execution: Plugins are deployed in a separate directory so that they can come with their own version of dependent libraries.
10 | - Localization: The Plugins can be localized via satellite assemblies.
11 | - Configuration: The `App.config` file is supported for Plugins.
12 |
13 | ## Known Issues
14 | - Stability: The UI thread of the Host application and the Plugins are synchronized. Plugins can block the Host application.
15 | - Performance: The communication between the Host and the Plugins is done via .NET Remoting. This has some negative impact on the performance.
16 | - Limitation: A Plugin cannot show a modal Dialog. It can show Dialogs but they cannot be Modal because the Dialog cannot connect with the Host Window.
17 |
--------------------------------------------------------------------------------
/src/Shell/Applications/RemoteServices/AddressBookService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Composition;
3 | using System.Threading.Tasks;
4 | using WpfPluginSample.PluginFramework.Internals;
5 | using WpfPluginSample.Shell.Foundation;
6 | using WpfPluginSample.Shell.Interfaces;
7 |
8 | namespace WpfPluginSample.Shell.Applications.RemoteServices
9 | {
10 | [Export]
11 | public class AddressBookService : RemoteService, IAddressBookService
12 | {
13 | private readonly TaskScheduler taskScheduler;
14 |
15 | public AddressBookService()
16 | {
17 | taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
18 | }
19 |
20 | public Action SelectContactCallback { get; set; }
21 |
22 | public Action ContactDeletedCallback { get; set; }
23 |
24 | public void SelectContact(ContactDto contact)
25 | {
26 | TaskHelper.Run(() => SelectContactCallback?.Invoke(contact), taskScheduler);
27 | }
28 |
29 | public void ContactDeleted(ContactDto contact)
30 | {
31 | TaskHelper.Run(() => ContactDeletedCallback?.Invoke(contact), taskScheduler);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/SamplePlugin/Presentation/DialogWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/PluginController.cs:
--------------------------------------------------------------------------------
1 | using WpfPluginSample.AddressBookPlugin.Applications;
2 | using WpfPluginSample.AddressBookPlugin.Presentation;
3 | using WpfPluginSample.PluginFramework;
4 | using WpfPluginSample.Shell.Interfaces;
5 |
6 | namespace WpfPluginSample.AddressBookPlugin
7 | {
8 | public class PluginController : IPluginController
9 | {
10 | private ILogService logService;
11 | private IAddressBookService addressBookService;
12 | private ContactController contactController;
13 |
14 | public void Initialize()
15 | {
16 | logService = RemoteServiceLocator.GetService();
17 | addressBookService = RemoteServiceLocator.GetService();
18 | logService.Message("Initialize", true);
19 | }
20 |
21 | public object CreateMainView()
22 | {
23 | var viewModel = new ContactListViewModel(new ContactListView());
24 | contactController = new ContactController(logService, addressBookService, viewModel);
25 | contactController.Initialize();
26 | return viewModel.View;
27 | }
28 |
29 | public void Shutdown()
30 | {
31 | logService.Message("Shutdown", true);
32 | contactController.Shutdown();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/AddressBookPlugin/Applications/SampleDataProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using WpfPluginSample.AddressBookPlugin.Domain;
3 |
4 | namespace WpfPluginSample.AddressBookPlugin.Applications
5 | {
6 | public static class SampleDataProvider
7 | {
8 | public static IReadOnlyList CreateContacts()
9 | {
10 | var contacts = new List()
11 | {
12 | CreateContact("Jesper", "Aaberg", "jesper.aaberg@example.com", "(111) 555-0100", "A. Datum Corporation"),
13 | CreateContact("Lori", "Penor", "lori.penor@fabrikam.com", "(111) 555-0104", "Baldwin Museum of Science"),
14 | CreateContact("Michael", "Pfeiffer", "michael.pfeiffer@fabrikam.com", "(222) 555-0105", "Blue Yonder Airlines"),
15 | CreateContact("Terry", "Adams", "terry.adams@adventure-works.com", "(333) 555-0102", "Adventure Works"),
16 | CreateContact("Miles", "Reid", "miles.reid@adventure-works.com", "(444) 555-0123", "Adventure Works")
17 | };
18 | return contacts;
19 | }
20 |
21 | private static Contact CreateContact(string firstname, string lastname, string email, string phone, string company)
22 | {
23 | return new Contact { Firstname = firstname, Lastname = lastname, Email = email, Phone = phone, Company = company };
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/PluginFramework/DisposableObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WpfPluginSample.PluginFramework
4 | {
5 | ///
6 | /// Base class that implements the IDisposable interface.
7 | ///
8 | public abstract class DisposableObject : IDisposable
9 | {
10 | private volatile bool isDisposed;
11 |
12 |
13 | ///
14 | /// Indicates, whether the object was disposed.
15 | ///
16 | protected bool IsDisposed => isDisposed;
17 |
18 |
19 | ///
20 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
21 | ///
22 | public void Dispose()
23 | {
24 | Dispose(true);
25 | GC.SuppressFinalize(this);
26 | }
27 |
28 | ///
29 | /// Override this method to free, release or reset any resources.
30 | ///
31 | /// if true then dispose unmanaged and managed resources; otherwise dispose only unmanaged resources.
32 | protected virtual void DisposeCore(bool isDisposing)
33 | {
34 | }
35 |
36 | private void Dispose(bool isDiposing)
37 | {
38 | if (isDisposed) return;
39 |
40 | isDisposed = true;
41 | DisposeCore(isDiposing);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/PluginFramework/Internals/NativeHandleContractInsulator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.AddIn.Contract;
3 |
4 | namespace WpfPluginSample.PluginFramework.Internals
5 | {
6 | public class NativeHandleContractInsulator : RemoteService, INativeHandleContract
7 | {
8 | private readonly INativeHandleContract source;
9 |
10 | public NativeHandleContractInsulator(INativeHandleContract source)
11 | {
12 | this.source = source;
13 | }
14 |
15 | public IntPtr GetHandle()
16 | {
17 | return source.GetHandle();
18 | }
19 |
20 | public int AcquireLifetimeToken()
21 | {
22 | return source.AcquireLifetimeToken();
23 | }
24 |
25 | public int GetRemoteHashCode()
26 | {
27 | return source.GetRemoteHashCode();
28 | }
29 |
30 | public IContract QueryContract(string contractIdentifier)
31 | {
32 | return source.QueryContract(contractIdentifier);
33 | }
34 |
35 | public bool RemoteEquals(IContract contract)
36 | {
37 | return source.RemoteEquals(contract);
38 | }
39 |
40 | public string RemoteToString()
41 | {
42 | return source.RemoteToString();
43 | }
44 |
45 | public void RevokeLifetimeToken(int token)
46 | {
47 | source.RevokeLifetimeToken(token);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Shell/Applications/RemoteServices/EventAggregator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.ComponentModel.Composition;
4 | using System.Reactive.Linq;
5 | using System.Reactive.Subjects;
6 | using WpfPluginSample.PluginFramework.Internals;
7 | using WpfPluginSample.Shell.Interfaces;
8 |
9 | namespace WpfPluginSample.Shell.Applications.RemoteServices
10 | {
11 | [Export(typeof(IEventAggregator))]
12 | public class EventAggregator : RemoteService, IEventAggregator
13 | {
14 | private readonly ConcurrentDictionary subjects = new ConcurrentDictionary();
15 |
16 | public IObservable GetEvent()
17 | {
18 | var subject = (ISubject)subjects.GetOrAdd(typeof(TEvent), t => new Subject());
19 | return subject.AsObservable().Remotable(null);
20 | }
21 |
22 | public void Publish(TEvent sampleEvent)
23 | {
24 | object subject;
25 | if (subjects.TryGetValue(typeof(TEvent), out subject))
26 | {
27 | try
28 | {
29 | ((ISubject)subject).OnNext(sampleEvent);
30 | }
31 | catch (Exception)
32 | {
33 | // This can happen when a Plugin did not unsubscribe from the event.
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/PluginHost/Bootstrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Runtime.Remoting;
4 | using System.Threading;
5 | using System.Windows.Threading;
6 | using WpfPluginSample.PluginFramework;
7 | using WpfPluginSample.PluginFramework.Internals;
8 |
9 | namespace WpfPluginSample.PluginHost
10 | {
11 | public class Bootstrapper : RemoteService
12 | {
13 | private static Process parentProcess;
14 |
15 | public static Dispatcher Dispatcher { get; private set; }
16 |
17 | public void RunPluginDomain(string[] args)
18 | {
19 | var typedArgs = TypedArgs.FromArgs(args);
20 |
21 | parentProcess = Process.GetProcessById(typedArgs.ParentProcessId);
22 | parentProcess.Exited += ParentProcessExited;
23 | parentProcess.EnableRaisingEvents = true;
24 |
25 | Dispatcher = Dispatcher.CurrentDispatcher;
26 |
27 | RemoteServiceLocator.InitializeIpcChannel(typedArgs.InstanceName);
28 | RemotingConfiguration.RegisterWellKnownServiceType(typeof(PluginLoader), "PluginLoader", WellKnownObjectMode.Singleton);
29 |
30 | // Signal ready
31 | var readyEvent = EventWaitHandle.OpenExisting(typedArgs.InstanceName + ".Ready");
32 | readyEvent.Set();
33 |
34 | Dispatcher.Run();
35 | }
36 |
37 | private static void ParentProcessExited(object sender, EventArgs e)
38 | {
39 | // This happens only if the parent process crashed.
40 | Environment.Exit(1);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Shell/Applications/ViewModels/ShellViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.ComponentModel.Composition;
3 | using System.Waf.Applications;
4 | using System.Windows.Input;
5 | using WpfPluginSample.Shell.Applications.Services;
6 | using WpfPluginSample.Shell.Applications.Views;
7 |
8 | namespace WpfPluginSample.Shell.Applications.ViewModels
9 | {
10 | [Export]
11 | public class ShellViewModel : ViewModel
12 | {
13 | private PluginInfo selectedPlugin;
14 | private object selectedPluginView;
15 |
16 | [ImportingConstructor]
17 | public ShellViewModel(IShellView view) : base(view)
18 | {
19 | }
20 |
21 | public ICommand LoadCommand { get; set; }
22 |
23 | public ICommand UnloadCommand { get; set; }
24 |
25 | public IReadOnlyList Plugins { get; set; }
26 |
27 | public PluginInfo SelectedPlugin
28 | {
29 | get { return selectedPlugin; }
30 | set { SetProperty(ref selectedPlugin, value); }
31 | }
32 |
33 | public IReadOnlyList