Nuget Package:
PM> Install-Package HBD.WinForms.Shell
HBD.WinForms.Shell
This post, I would like to share my Winforms Shell application, that provides the core foundation for the Winforms application. With this foundation, it can be load the external modules from the pre-defined folders when it started. So that you can add-in and maintenance the modules easily.
How does it work?
This Shell application had been developed based on HBD.Mef library to loading and executing external modules when it starts. For more information about HBD.Mef you can found in here.
Build-In Services
In this Shell there are a few services had been developed and exposed into Mef that helps you to interact with the Shell component. 1. The IMainMenuService will help to add Menu Item into the Menu-bar of Main window.
- The IStatusBarService will help to set the status into the Status-Bar of Main window.
- The IMainViewService will help to interact with Tab Manager of the Main window. That allow to add/remove UserControl into TabManager.
- The NavigationManager will provide the details in the below section. # Quick Start – Develop a new module for HBD.WinForms.Shell
1. Add New Module
- Open Visual Studio and create a new Class Library project example project name is Demo.Module.
- Install the latest version of HBD.WinForms.Shell from Nuget.
- Add new class named StartupDemoModule and inherited from HBD.Mef.WinForms.Modularity.WinFormModuleBaseand then implement 2 abstract methods (GetStartUpViewTypes and MenuConfiguration(IMainMenuService menuSet)) as below. Remember to add the ModuleExport attribute into your StartupDemoModule class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[PluginExport(typeof(StartupDemoModule))] [PartCreationPolicy(CreationPolicy.Shared)] public class StartupDemoModule : WinFormModuleBase { protected override IEnumerable<IViewInfo> GetStartUpViewTypes() { //Return the View1 as Startup View. //This view will be loaded automatically when application started. yield return new ViewInfo(typeof(View1)); } protected override void MenuConfiguration(IMainMenuService menuSet) { //Add Main Menu for this module. menuSet.Menu("Demo") .WithIcon(Resources.DemoIcon) .WithToolTip("This is demo menu.") .Children //Add Navigation for View1. .AddNavigation("View 1") .For(new ViewInfo(typeof(View1))) //And this navigation for View 2. .AndNavigation("View 2") .For(new ViewInfo(typeof(View2))); } } |
- Add 2 new User Controls Named View1 and View2 as below.
- On each view overwrite the Text property and return the View Title and add the Export attribute as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[Export] public partial class View1: UserControl { public override string Text => "View 1"; ... } [Export] public partial class View2: UserControl { public override string Text => "View 2"; ... } |
2. Setup Publish Shell folder.
- Copy folder [Solution Directory]\packages\HBD.WinForms.Shell\tools\WinForms.Shell to [Solution Directory]\Publish\WinForms.Shell
3. Update project’s properties.
- Right click on the project select Properties
- Add below code into Post-build event
1 2 3 4 |
if not exist "$(SolutionDir)Publish\WinForms.Shell\Modules\$(ProjectName)" mkdir "$(SolutionDir)Publish\WinForms.Shell\Modules\$(ProjectName)" xcopy "$(TargetDir)*.*" "$(SolutionDir)Publish\WinForms.Shell\Modules\$(ProjectName)" /s /y /r |
- Set Start external program under Debug to Publish\WinForms.Shell\HBD.WinForms.Shell.exe
- And set Working directory to Publish\WinForms.Shell folder.
4. Add Module config and Debug your module.
- Add a new Module_Demo.json file with the following content.
1 2 3 4 5 6 7 8 9 10 11 |
//this file name convention is Module_[Your project Name] { "Name": "Demo Module", "Description": "This is demo module for WinForms Shell", "Version": "1.0.0-beta1", "IsEnabled": true, //or false //The list of assemblies that you want to be exposed to the EMF. "AssemplyFiles": [ "Demo.Module.dll" ] } |
- Set Build action of this config file as below
- Run your module the Shell application will display as below.
Navigation between the Views.
From version 1.0.1 a new interface had been added into HBD.Mef.WinForms.Common to provide the communication channel between the views. So that the view and navigate to the other view with the parameters.
1. Implementation.
If your view wants to be navigated from the other view you need to implement the INavigationAware interface and handle OnNavigatedTo(WinformNavigationContext navigationContext) method, This method will be called when navigating from the other view.
- The interface
1 2 3 4 5 6 |
public interface INavigationAware { void OnNavigatedTo(WinformNavigationContext navigationContext); } |
- The view implementation
1 2 3 4 5 6 7 8 9 10 |
[Export] public partial class View2 : UserControl, INavigationAware { public void OnNavigatedTo(WinformNavigationContext navigationContext) { this.label2.Text = navigationContext.NavigationParameters.First().Value.ToString(); } } |
2. Using NavigationManager
The sample code below will handle the button event and navigate to the other view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[Export] public partial class View1 : UserControl { [Import] public INavigationManager NavigationManager { protected get; set; } private void button1_Click(object sender, System.EventArgs e) { this.NavigationManager.NavigateTo(typeof(View2), new Dictionary<string, object> { ["Message"] = "Navigated from View 1" }); } } |
When running the application and click the button. View2 will be open and display the message was passed from View1.
3. Recommendation
There are 2 base classes had been provided in HBD.Mef.WinForms for your Forms and Views. So that when you create a new Form or View, it can be inherited from this form instead.
- The FormBase
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class FormBase: Form { [Import] public IServiceLocator ContainerService { protected get; set; } [Import] public ILoggerFacade Logger { protected get; set; } [Import] public IMessageBoxService MessageBoxService { protected get; set; } } |
- The ViewBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class ViewBase : UserControl, INotifyPropertyChanged, IActiveAware,INavigationAware { [Import(AllowDefault = true,AllowRecomposition = true)] public IServiceLocator ContainerService { protected get; set; } [Import(AllowDefault = true, AllowRecomposition = true)] public ILoggerFacade Logger { protected get; set; } [Import(AllowDefault = true, AllowRecomposition = true)] public IShellStatusService StatusService { protected get; set; } [Import(AllowDefault = true, AllowRecomposition = true)] public IMessageBoxService MessageBoxService { protected get; set; } [Import(AllowDefault = true, AllowRecomposition = true)] public INavigationManager NavigationManager { protected get; set; } #region IActiveAware ... #endregion #region INotifyPropertyChanged ... #endregion #region NavigationAware ... #endregion } |
- The Form implementation
1 2 3 4 5 6 |
public class MainForm : FormBase { ... } |
- The View implementation
1 2 3 4 5 6 7 8 |
[Export] public partial class View1: ViewBase { public override string Text => "View 1"; ... } |
Demo.Module source code
You can download the Demo.Module source code here in Github
Hope this Shell will help you to develop a plugin-able application faster and easier. Your feedback and ideas are much appreciated. Please feel free to drop me an email with your opinion.Nuget Dependences