├── 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 |