The key feature of modules is the ability to be disabled entirely or partially. Currently, the module cannot do this, so we need to extend its capabilities.
Adding AppHost extension
Create an AppHost folder in the root directory of Asv.Avalonia.Samples.CreateModule. Inside this folder, add three files: ModuleModuleMixin, ModuleModuleOptionsBuilder, and ModuleModuleOptions.
ModuleModuleOptions
In this file, we define the configuration options for the module. These options will later be used by the builder.
public class ModuleModuleOptions
{
public const string Section = "Module"; // Configuration section for the module
public required bool IsEnabled { get; set; }
}
ModuleModuleOptionsBuilder
Add one base method for the builder. We will extend this class later, but for now this method is enough.
This file extends the builder in Program.cs. We use this method to set up our module.
public static class ModuleModuleMixin
{
extension(IHostApplicationBuilder builder)
{
public IHostApplicationBuilder UseModuleModule()
{
var options = builder
.Services.AddOptions<ModuleModuleOptions>()
.Bind(builder.Configuration.GetSection(ModuleModuleOptions.Section));
var optionsBuilder = new ModuleModuleOptionsBuilder();
optionsBuilder.Build(options);
return builder;
}
}
}
Updating the mixin to use options
Now we need to update the module's mixin to conditionally register components based on the options:
public static class ModuleModuleMixin
{
extension(IHostApplicationBuilder builder)
{
public IHostApplicationBuilder UseModuleModule(
Action<ModuleModuleOptionsBuilder>? configure = null)
{
var options = builder
.Services.AddOptions<ModuleModuleOptions>()
.Bind(builder.Configuration.GetSection(ModuleModuleOptions.Section));
var defaultOptions = builder
.Configuration.GetSection(ModuleModuleOptions.Section)
.Get<ModuleModuleOptions>();
var optionsBuilder = defaultOptions is null
? new ModuleModuleOptionsBuilder()
: new ModuleModuleOptionsBuilder(defaultOptions);
if (configure is null)
{
return builder;
}
configure.Invoke(optionsBuilder);
optionsBuilder.Build(options);
var resolvedOptions = optionsBuilder.Resolve();
if (!resolvedOptions.IsEnabled)
{
return builder;
}
if (resolvedOptions.IsCatsPageEnabled)
{
builder.Shell.Pages.Register<CatsPageViewModel, CatsPageView>(CatsPageViewModel.PageId);
builder.Commands.Register<OpenCatsPageCommand>();
builder.Extensions.Register<IHomePage, HomePageCatsPageExtension>();
}
return builder;
}
}
}
Adding modularity to the app
Now our module is fully modular, and we need to enable it in the application.
Go to Program.cs and add the following line to the builder:
builder.UseModuleModule(opt => opt.WithCats());
Run the application. You should see the Cats page.
Now remove UseModuleModule from the builder. Run the application again. The page should no longer be visible.
If you did everything right, now your module meets our standards for modules. Next we will show you how to extend your module and add more settings to it so that you will be able to split your module into smaller parts.
Extending the module
We have already implemented the basic module structure with some simple features. In this section we are going to extend our module with new features. We will also add new settings to the builder. This section is useful if you want to add to your module optional features.
Adding a new page
We want to provide additional customization options for the user. Let's add the ability to open a page with dogs.
Download a picture with dogs from the internet.
Add a new picture to the assets folder.
Create a new View and ViewModel for the dogs page. We will skip the details of this step as it is similar to the Cats page implementation.
We need to change the builder extension to use our new options. Here we will show you the advanced version of the builder extension that can take default options from the appsettings.json. It also conditionally registers both cats and dogs pages based on the resolved options:
public static class ModuleModuleMixin
{
extension(IHostApplicationBuilder builder)
{
public IHostApplicationBuilder UseModuleModule(
Action<ModuleModuleOptionsBuilder>? configure = null)
{
var options = builder
.Services.AddOptions<ModuleModuleOptions>()
.Bind(builder.Configuration.GetSection(ModuleModuleOptions.Section));
var defaultOptions = builder
.Configuration.GetSection(ModuleModuleOptions.Section)
.Get<ModuleModuleOptions>();
var optionsBuilder = defaultOptions is null
? new ModuleModuleOptionsBuilder()
: new ModuleModuleOptionsBuilder(defaultOptions);
if (configure is null)
{
return builder;
}
configure.Invoke(optionsBuilder);
optionsBuilder.Build(options);
var resolvedOptions = optionsBuilder.Resolve();
if (!resolvedOptions.IsEnabled)
{
return builder;
}
if (resolvedOptions.IsCatsPageEnabled)
{
builder.Shell.Pages.Register<CatsPageViewModel, CatsPageView>(CatsPageViewModel.PageId);
builder.Services.AddSingleton<IAsyncCommand, OpenCatsPageCommand>();
builder.Extensions.Register<IHomePage, HomePageCatsPageExtension>();
}
if (resolvedOptions.IsDogsPageEnabled)
{
builder.Shell.Pages.Register<DogsPageViewModel, DogsPageView>(DogsPageViewModel.PageId);
builder.Commands.Register<OpenDogsPageCommand>();
builder.Extensions.Register<IHomePage, HomePageDogsPageExtension>();
}
return builder;
}
}
}
Now you can run the application and see that all pages are disabled. To enable them, modify the UseModuleModule call in Program.cs as follows: