Asv.Avalonia.Docs Help

Module example source code

image

AppHost

using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Asv.Avalonia.Module; public static class ModuleModuleMixin { public static IHostApplicationBuilder UseModuleModule( this IHostApplicationBuilder builder, 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); return builder; } }
namespace Asv.Avalonia.Module; public class ModuleModuleOptions { public const string Section = "GeoMap"; public required bool IsEnabled { get; set; } public required bool IsDogsPageEnabled { get; set; } public required bool IsCatsPageEnabled { get; set; } }
using Microsoft.Extensions.Options; namespace Asv.Avalonia.Module; public class ModuleModuleOptionsBuilder { private bool _isCatsPageEnabled; private bool _isDogsPageEnabled; internal ModuleModuleOptionsBuilder() { } internal ModuleModuleOptionsBuilder(ModuleModuleOptions defaultOptions) { _isCatsPageEnabled = defaultOptions.IsCatsPageEnabled; _isDogsPageEnabled = defaultOptions.IsDogsPageEnabled; } public ModuleModuleOptionsBuilder WithCats() { _isCatsPageEnabled = true; return this; } public ModuleModuleOptionsBuilder WithDogs() { _isDogsPageEnabled = true; return this; } internal OptionsBuilder<ModuleModuleOptions> Build(OptionsBuilder<ModuleModuleOptions> options) { return options.Configure(config => { config.IsDogsPageEnabled = _isDogsPageEnabled; config.IsCatsPageEnabled = _isCatsPageEnabled; config.IsEnabled = true; }); } }

Commands

using System.Composition; namespace Asv.Avalonia.Module; [ExportCommand] [method: ImportingConstructor] public class OpenCatsPageCommand(INavigationService nav) : OpenPageCommandBase(CatsPageViewModel.PageId, nav) { public override ICommandInfo Info => StaticInfo; #region Static public const string Id = $"{BaseId}.open.cats"; public static readonly ICommandInfo StaticInfo = new CommandInfo { Id = Id, Name = "Open cats page", Description = "Command opens cats page", Icon = CatsPageViewModel.PageIcon, IconColor = CatsPageViewModel.PageIconColor, DefaultHotKey = null, Source = ModuleModule.Instance, }; #endregion }
using System.Composition; namespace Asv.Avalonia.Module; [ExportCommand] [method: ImportingConstructor] public class OpenDogsPageCommand(INavigationService nav) : OpenPageCommandBase(DogsPageViewModel.PageId, nav) { public override ICommandInfo Info => StaticInfo; #region Static public const string Id = $"{BaseId}.open.dogs"; public static readonly ICommandInfo StaticInfo = new CommandInfo { Id = Id, Name = "Open dogs page", Description = "Command opens dogs page", Icon = DogsPageViewModel.PageIcon, IconColor = DogsPageViewModel.PageIconColor, DefaultHotKey = null, Source = ModuleModule.Instance, }; #endregion }

Cats page

<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:module="clr-namespace:Asv.Avalonia.Module" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Asv.Avalonia.Module.CatsPageView" x:DataType="module:CatsPageViewModel"> <Design.DataContext> <module:CatsPageViewModel/> </Design.DataContext> <Grid> <Image Source="{CompiledBinding SelectedImage}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </UserControl>
using Avalonia.Controls; namespace Asv.Avalonia.Module; [ExportViewFor<CatsPageViewModel>] public partial class CatsPageView : UserControl { public CatsPageView() { InitializeComponent(); } }
using System.Composition; using Avalonia.Media.Imaging; using Microsoft.Extensions.Logging; using Avalonia.Platform; using Asv.Common; using Avalonia.Media; using Material.Icons; namespace Asv.Avalonia.Module; [ExportPage(PageId)] public class CatsPageViewModel : PageViewModel<CatsPageViewModel> { public const string PageId = "cats"; public const MaterialIconKind PageIcon = MaterialIconKind.Cat; public const AsvColorKind PageIconColor = AsvColorKind.Info3; public CatsPageViewModel() : this(DesignTime.CommandService, DesignTime.LoggerFactory, DesignTime.DialogService) { DesignTime.ThrowIfNotDesignMode(); } [ImportingConstructor] public CatsPageViewModel( ICommandService cmd, ILoggerFactory loggerFactory, IDialogService dialogService) : base(PageId, cmd, loggerFactory, dialogService) { var stream = AssetLoader .Open(new Uri("avares://Asv.Avalonia.Module/Assets/cat.jpg")) .DisposeItWith(Disposable); var defaultPicture = new Bitmap(stream).DisposeItWith(Disposable); SelectedImage = defaultPicture; } public IImage? SelectedImage { get; private init => SetField(ref field, value); } public override IEnumerable<IRoutable> GetChildren() { return []; } protected override void AfterLoadExtensions() { // ignore } public override IExportInfo Source => ModuleModule.Instance; }
using System.Composition; using Asv.Common; using Microsoft.Extensions.Logging; using R3; namespace Asv.Avalonia.Module; [ExportExtensionFor<IHomePage>] [method: ImportingConstructor] public class HomePageCatsPageExtension(ILoggerFactory loggerFactory) : AsyncDisposableOnce, IExtensionFor<IHomePage> { public void Extend(IHomePage context, CompositeDisposable contextDispose) { context.Tools.Add( OpenCatsPageCommand .StaticInfo .CreateAction(loggerFactory) .DisposeItWith(contextDispose) ); } }

Dogs page

<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:module="clr-namespace:Asv.Avalonia.Module" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Asv.Avalonia.Module.DogsPageView" x:DataType="module:DogsPageViewModel"> <Design.DataContext> <module:DogsPageViewModel/> </Design.DataContext> <Grid> <Image Source="{CompiledBinding SelectedImage}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </UserControl>
[ExportViewFor<DogsPageViewModel>] public partial class DogsPageView : UserControl { public DogsPageView() { InitializeComponent(); } }
using System.Composition; using Asv.Common; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; using Material.Icons; using Microsoft.Extensions.Logging; namespace Asv.Avalonia.Module; [ExportPage(PageId)] public class DogsPageViewModel : PageViewModel<DogsPageViewModel> { public const string PageId = "dogs"; public const MaterialIconKind PageIcon = MaterialIconKind.Dog; public const AsvColorKind PageIconColor = AsvColorKind.Info7; public DogsPageViewModel() : this(DesignTime.CommandService, DesignTime.LoggerFactory, DesignTime.DialogService) { DesignTime.ThrowIfNotDesignMode(); } [ImportingConstructor] public DogsPageViewModel( ICommandService cmd, ILoggerFactory loggerFactory, IDialogService dialogService) : base(PageId, cmd, loggerFactory, dialogService) { var stream = AssetLoader .Open(new Uri("avares://Asv.Avalonia.Module/Assets/dog.jpg")) .DisposeItWith(Disposable); var defaultPicture = new Bitmap(stream).DisposeItWith(Disposable); SelectedImage = defaultPicture; } public IImage? SelectedImage { get; private init => SetField(ref field, value); } public override IEnumerable<IRoutable> GetChildren() { return []; } protected override void AfterLoadExtensions() { // ignore } public override IExportInfo Source => ModuleModule.Instance; }
using System.Composition; using Asv.Common; using Microsoft.Extensions.Logging; using R3; namespace Asv.Avalonia.Module; [ExportExtensionFor<IHomePage>] [method: ImportingConstructor] public sealed class HomePageDogsPageExtension(ILoggerFactory loggerFactory) : AsyncDisposableOnce, IExtensionFor<IHomePage> { public void Extend(IHomePage context, CompositeDisposable contextDispose) { context.Tools.Add( OpenDogsPageCommand .StaticInfo .CreateAction(loggerFactory) .DisposeItWith(contextDispose) ); } }

ModuleModule

using System.Composition.Hosting; using Avalonia.Controls; using Microsoft.Extensions.Options; namespace Asv.Avalonia.Module; public class ModuleModule : IExportInfo { public const string Name = "Asv.Avalonia.Module"; private ModuleModule() { } public static IExportInfo Instance { get; } = new ModuleModule(); public string ModuleName => Name; } public static class ContainerConfigurationMixin { public static ContainerConfiguration WithDependenciesFromModuleModule( this ContainerConfiguration containerConfiguration ) { if (Design.IsDesignMode) { return containerConfiguration.WithAssemblies([typeof(ModuleModule).Assembly]); } var exceptionTypes = new List<Type>(); var options = AppHost.Instance.GetService<IOptions<ModuleModuleOptions>>().Value; if (!options.IsEnabled) { // if the module is disabled, we should remove all the dependencies from the export exceptionTypes.AddRange(typeof(ModuleModule).Assembly.GetTypes()); } else if (!options.IsDogsPageEnabled) { // if we disable the dogs page, we should remove all dependencies for this page exceptionTypes.AddRange([ typeof(DogsPageView), typeof(DogsPageViewModel), typeof(HomePageDogsPageExtension), typeof(OpenDogsPageCommand) ]); } else if (!options.IsCatsPageEnabled) { // if we disable the cats page, we should remove all dependencies for this page exceptionTypes.AddRange([ typeof(CatsPageView), typeof(CatsPageViewModel), typeof(HomePageCatsPageExtension), typeof(OpenCatsPageCommand) ]); } var typesToExport = typeof(ModuleModule).Assembly .GetTypes() .Except(exceptionTypes); return containerConfiguration.WithParts(typesToExport); } }
Last modified: 16 December 2025