Software abstraction layer core API. SAL Interface for Windows application.
$ dotnet add package SAL.WindowsSAL.Windows is a .NET library that provides a plugin-based architecture for developing extensible Windows Forms applications. It's part of the SAL (Software Abstraction Layer) framework that enables modular and flexible application development.
The SAL architecture consists of three main components:
The architecture is built around the IPlugin interface, allowing plugins to:
SAL supports multiple host types:
public class Plugin : IPlugin, IPluginSettings<PluginSettings>
{
private readonly IHost _host;
private PluginSettings _settings;
public Plugin(IHost host)
{
_host = host;
}
public PluginSettings Settings
{
get
{
if(_settings == null)
{
_settings = new PluginSettings(this);
_host.Plugins.Settings(this).LoadAssemblyParameters(_settings);
}
return _settings;
}
}
}
IHostWindows host = this.Host as IHostWindows;
if(host != null)
{
IMenuItem menuTools = host.MainMenu.FindMenuItem("Tools");
if(menuTools != null)
{
var newMenuItem = menuTools.Create("New Feature");
newMenuItem.Name = "tsmiToolsNewFeature";
newMenuItem.Click += OnNewFeatureClick;
menuTools.Items.Insert(0, newMenuItem);
}
}
IWindow window = hostWindows.Windows.CreateWindow(
plugin: this,
formType: typeof(MyUserControl).ToString(),
searchForOpened: false,
showHint: DockState.DockLeft
);
public class MyPlugin : IPlugin
{
private IMenuItem _pluginMenuItem;
internal IHost Host { get; }
/// <summary>It's allowed to launch the plugin with any host, and at startup - the plugin decides how to start</summary>
public MyPlugin(IHost host)
=> this.Host = host;
Boolean IPlugin.OnConnection(ConnectMode mode)
{
IHostWindows host = this.Host as IHostWindows;
if(host != null)
{
IMenuItem menuTools = host.MainMenu.FindMenuItem("Tools");
if(menuTools != null)
{
_pluginMenuItem = menuTools.Create("Menu Item Name");
_pluginMenuItem.Name = "tsmiToolsPluginMenuItem";
_pluginMenuItem.Click += new EventHandler(_pluginMenuItem_Click);
menuTools.Items.Insert(0, _pluginMenuItem);
return true;
}
}
return false;
}
Boolean IPlugin.OnDisconnection(DisconnectMode mode)
{
if(this._pluginMenuItem != null)
this.HostWindows.MainMenu.Items.Remove(this._pluginMenuItem);
return true;
}
}
public class MyPlugin : IPlugin
{
private IHostWindows HostWindows { get; private set; }
/// <summary>Creating an instance of this plugin is only possible in a host that implements the IHostWindows interface (Constructor is optional and works as IoC)</summary>
public MyPlugin(IHostWindows host)
=> this.HostWindows = host;
Boolean IPlugin.OnConnection(ConnectMode mode)
{
this.HostWindows.Plugins.PluginsLoaded += (sender, e) => this.CreateWindow(typeof(PanelMyWindowControl).ToString(), true);
return true;
}
Boolean IPlugin.OnDisconnection(DisconnectMode mode)
=> true;
private IWindow CreateWindow(String typeName, Boolean searchForOpened, Object args = null)
=> this.HostWindows.Windows.CreateWindow(this, typeName, searchForOpened, DockState.DockLeft, args);
}
public partial class PanelMyWindowControl : UserControl
{
private IWindow Window { get { return (IWindow)base.Parent; } }
private MyPlugin Plugin { get { return (MyPlugin)this.Window.Plugin; } }
protected override void OnCreateControl()
{
this.Window.Caption = "I want cookie";
base.OnCreateControl();
}
}
IHost interface and its extensionsIPluginSettings interface// Loading data
using(Stream stream = plugin.Host.Plugins.Settings(plugin).LoadAssemblyBlob("config.xml"))
{
if(stream != null)
// Process configuration
}
// Saving data
using(MemoryStream stream = new MemoryStream())
{
// Write configuration
plugin.Host.Plugins.Settings(plugin).SaveAssemblyBlob("config.xml", stream);
}
Contributions are welcome. Please ensure your changes maintain compatibility with the supported .NET versions.