├── .gitignore ├── CHANGELOG.txt ├── LICENSE.txt ├── README-EN.md ├── README.md ├── Rakefile ├── Samples ├── Stylet.Samples.DryIoC │ ├── App.axaml │ ├── App.axaml.cs │ ├── DryIocStyletApplication.cs │ ├── MainView.axaml │ ├── MainView.axaml.cs │ ├── MainViewModel.cs │ ├── Program.cs │ ├── Stylet.Samples.DryIoC.csproj │ └── app.manifest ├── Stylet.Samples.Hello │ ├── App.axaml │ ├── App.axaml.cs │ ├── MainView.axaml │ ├── MainView.axaml.cs │ ├── MainViewModel.cs │ ├── Program.cs │ ├── RootView.axaml │ ├── RootView.axaml.cs │ └── Stylet.Samples.Hello.csproj ├── Stylet.Samples.HelloDialog │ ├── .gitignore │ ├── App.axaml │ ├── App.axaml.cs │ ├── Dialog1View.axaml │ ├── Dialog1View.axaml.cs │ ├── Dialog1ViewModel.cs │ ├── Program.cs │ ├── ShellView.axaml │ ├── ShellView.axaml.cs │ ├── ShellViewModel.cs │ └── Stylet.Samples.HelloDialog.csproj ├── Stylet.Samples.MSIoC │ ├── App.axaml │ ├── App.axaml.cs │ ├── MainView.axaml │ ├── MainView.axaml.cs │ ├── MainViewModel.cs │ ├── MsStyletApplication.cs │ ├── Program.cs │ ├── Stylet.Samples.MSIoC.csproj │ └── app.manifest ├── Stylet.Samples.MasterDetail │ ├── .gitignore │ ├── App.axaml │ ├── App.axaml.cs │ ├── Program.cs │ ├── ShellView.axaml │ ├── ShellView.axaml.cs │ ├── ShellViewModel.cs │ └── Stylet.Samples.MasterDetail.csproj ├── Stylet.Samples.NavigationController │ ├── .gitignore │ ├── App.axaml │ ├── App.axaml.cs │ ├── NavigationController.cs │ ├── Pages │ │ ├── HeaderView.axaml │ │ ├── HeaderView.axaml.cs │ │ ├── HeaderViewModel.cs │ │ ├── Page1View.axaml │ │ ├── Page1View.axaml.cs │ │ ├── Page1ViewModel.cs │ │ ├── Page2View.axaml │ │ ├── Page2View.axaml.cs │ │ ├── Page2ViewModel.cs │ │ ├── ShellView.axaml │ │ ├── ShellView.axaml.cs │ │ └── ShellViewModel.cs │ ├── Program.cs │ └── Stylet.Samples.NavigationController.csproj ├── Stylet.Samples.OverridingViewManager │ ├── .gitignore │ ├── App.axaml │ ├── App.axaml.cs │ ├── CustomViewManager.cs │ ├── Program.cs │ ├── ShellView.axaml │ ├── ShellView.axaml.cs │ ├── ShellViewModel.cs │ └── Stylet.Samples.OverridingViewManager.csproj └── Stylet.Samples.TabNavigation │ ├── .gitignore │ ├── App.axaml │ ├── App.axaml.cs │ ├── Page1View.axaml │ ├── Page1View.axaml.cs │ ├── Page1ViewModel.cs │ ├── Page2View.axaml │ ├── Page2View.axaml.cs │ ├── Page2ViewModel.cs │ ├── Program.cs │ ├── ShellView.axaml │ ├── ShellView.axaml.cs │ ├── ShellViewModel.cs │ └── Stylet.Samples.TabNavigation.csproj ├── Stylet.Avalonia.sln ├── Stylet.Avalonia ├── Assets │ ├── error.png │ ├── information.png │ ├── question.png │ └── warning.png ├── BindableCollection.cs ├── Conductor.cs ├── ConductorAllActive.cs ├── ConductorBase.cs ├── ConductorBaseWithActiveItem.cs ├── ConductorNavigating.cs ├── ConductorOneActive.cs ├── EventAggregator.cs ├── Execute.cs ├── ExpressionExtensions.cs ├── Extensions │ └── EnumExtensions.cs ├── IConductor.cs ├── IDispatcher.cs ├── INotifyCollectionChanging.cs ├── INotifyPropertyChangedDispatcher.cs ├── IScreen.cs ├── IValidationAdapter.cs ├── IoC.cs ├── LabelledValue.cs ├── Logging │ ├── ILogger.cs │ ├── LogManager.cs │ ├── NullLogger.cs │ └── TraceLogger.cs ├── Primitive │ ├── IMessageBoxViewModel.cs │ ├── MessageBoxView.axaml │ ├── MessageBoxView.axaml.cs │ └── MessageBoxViewModel.cs ├── Properties │ └── AssemblyInfo.cs ├── PropertyChangedBase.cs ├── PropertyChangedExtensions.cs ├── PropertyChangedWeakEventManager.cs ├── RelayCommand.cs ├── RoutedCommand.cs ├── Screen.cs ├── ScreenExtensions.cs ├── Stylet.Avalonia.csproj ├── StyletApplicationBase.cs ├── StyletConductorExtensions.cs ├── StyletIoC │ ├── Creation │ │ ├── BuilderTypeKey.cs │ │ ├── BuilderUpper.cs │ │ ├── ICreator.cs │ │ ├── IRegistration.cs │ │ └── IRegistrationContext.cs │ ├── FluentInterface.cs │ ├── IContainer.cs │ ├── IInjectionAware.cs │ ├── InjectAttribute.cs │ ├── Internal │ │ ├── AbstractFactoryBuilder.cs │ │ ├── Builders │ │ │ ├── BuilderAbstractFactoryBinding.cs │ │ │ ├── BuilderBindTo.cs │ │ │ ├── BuilderBindingBase.cs │ │ │ ├── BuilderFactoryBinding.cs │ │ │ ├── BuilderInstanceBinding.cs │ │ │ ├── BuilderToAllImplementationsBinding.cs │ │ │ └── BuilderTypeBinding.cs │ │ ├── Container.cs │ │ ├── Creators │ │ │ ├── AbstractFactoryCreator.cs │ │ │ ├── CreatorBase.cs │ │ │ ├── FactoryCreator.cs │ │ │ └── TypeCreator.cs │ │ ├── IRegistrationCollection.cs │ │ ├── RegistrationCollections │ │ │ ├── EmptyRegistrationCollection.cs │ │ │ ├── RegistrationCollection.cs │ │ │ └── SingleRegistration.cs │ │ ├── Registrations │ │ │ ├── FuncRegistration.cs │ │ │ ├── GetAllRegistration.cs │ │ │ ├── InstanceRegistration.cs │ │ │ ├── RegistrationBase.cs │ │ │ ├── SingletonRegistration.cs │ │ │ └── TransientRegistration.cs │ │ ├── TypeExtensions.cs │ │ ├── TypeKey.cs │ │ └── UnboundGeneric.cs │ ├── StyletApplication.cs │ ├── StyletIoCBuilder.cs │ ├── StyletIoCContainer.cs │ ├── StyletIoCException.cs │ └── StyletIoCModule.cs ├── ValidatingModelBase.cs ├── ViewManager.cs ├── WeakEventManagerBase.cs ├── WindowManager.cs └── Xaml │ ├── ActionBase.cs │ ├── ActionExtension.cs │ ├── ApplicationLoader.cs │ ├── CommandAction.cs │ ├── DebugConverter.cs │ ├── EqualityConverter.cs │ ├── EventAction.cs │ ├── IconToBitmapSourceConverter.cs │ ├── RethrowingBinding.cs │ ├── StyletResourceDictionary.axaml │ ├── StyletResourceDictionary.xaml │ ├── StyletResourceDictionary1.xaml │ └── View.cs ├── Stylet.sln.DotSettings ├── StyletIcon.png └── StyletIcon.svg /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 sealoyal 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | ![Project Icon](StyletIcon.png) Stylet 2 | ====================================== 3 | 4 | [中文文档](./README.md) 5 | 6 | [![NuGet](https://img.shields.io/nuget/v/Stylet.svg)](https://www.nuget.org/packages/Stylet/) 7 | [![Build status](https://ci.appveyor.com/api/projects/status/nqucthach0x6gkil?svg=true)](https://ci.appveyor.com/project/canton7/stylet) 8 | 9 | Introduction 10 | ------------ 11 | 12 | Stylet is a small but powerful ViewModel-first MVVM framework for Avaloinia UI (.NET 4.5+ and .NET Core 3.0+), which allows you to write maintainable and extensible code in a way which is easy to test. 13 | Stylet's aims are to: 14 | 15 | - Solve the blockers, niggles, and annoyances which hamper MVVM development without a framework, using simple but powerful concepts. 16 | - Be obvious to people picking up your project for the first time: there's very little magic 17 | - Be easy to verify/validate. The LOC count is low, and it comes with a very comprehensive test suite. The code is well-written and well-documented. 18 | - Be flexible while providing sensible defaults. Almost any part of the framework can be overridden if you wish, but you probably won't want to. 19 | 20 | It is inspired by [Caliburn.Micro](http://caliburnmicro.com/), and shares many of its concepts, but removes most of the magic (replacing it with more powerful alternatives), and simplifies parts considerably by targeting only MVVM and Avaloinia UI . 21 | 22 | 23 | Getting Started 24 | --------------- 25 | 26 | ### Avaloinia UI >= 0.11.0-preview [requirement] 27 | 28 | 29 | 30 | > **Please see the sample project about how to use** 31 | 32 | 33 | 34 | 35 | 36 | 37 | Documentation 38 | ------------- 39 | 40 | [The Wiki is the documentation source](https://github.com/canton7/Stylet/wiki). 41 | There's loads of information there - go and have a look, or start with the [Quick Start](https://github.com/canton7/Stylet/wiki/Quick-Start). 42 | 43 | 44 | Contributing 45 | ------------ 46 | 47 | Contributions are always welcome. 48 | If you've got a problem or a question, [raise an issue](https://github.com/rsdte/Avalonia.Stylet/issues). 49 | If you've got code you want to contribute, please read [the Contributing guidelines](https://github.com/canton7/Stylet/wiki/Contributing) first of all. 50 | Create a feature branch off the `develop` branch, add your changes there, and submit it as a pull request. 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Project Icon](./StyletIcon.png) Stylet.Avalonia 2 | ====================================== 3 | 4 | [英文文档](./README-EN.md) 5 | 6 | > 请注意本项目需要 AvaloniaUI 版本 >= 11.x 7 | 8 | ## 项目介绍 9 | 10 | `Stylet.Avalonia`是原来[Stylet](https://github.com/canton7/Stylet)项目对[AvaloniaUI](https://github.com/AvaloniaUI/Avalonia) 框架的适配。具体介绍请查看[Stylet项目介绍](https://github.com/canton7/Stylet) 11 | 12 | ## 快速开始 13 | 14 | 第一步:创建一个Avalonia框架类型的项目 15 | 16 | 第二步:nuget 管理器安装 `Stylet.Avalonia`包 17 | 18 | 第三步:创建`ShellViewModel`类,以及名为`ShellView`的窗口组件(`Avalonia Window`类型),其内容如下【其实啥也没动】 19 | 20 | - ShellViewModel.cs 21 | 22 | ```c# 23 | public class ShellViewModel 24 | { 25 | 26 | } 27 | ``` 28 | 29 | - ShellView.axaml 30 | 31 | ```xaml 32 | 39 | Welcome to Avalonia! 40 | 41 | ``` 42 | 43 | - ShellView.axaml.cs 44 | 45 | ```csharp 46 | public partial class ShellView : Window 47 | { 48 | public ShellView() 49 | { 50 | InitializeComponent(); 51 | } 52 | } 53 | ``` 54 | 55 | 56 | 第四步:找到并打开`App.axaml.cs`文件,使其继承于`StyletApplication`其中`T`为任一`ViewModel`,当前设置为第三步创建的`ShellViewModel`,修改后内容如下 57 | 58 | ```c# 59 | public partial class App : StyletApplication 60 | { 61 | public override void Initialize() 62 | { 63 | AvaloniaXamlLoader.Load(this); 64 | base.Initialize(); // 初始化stylet,不能去掉 65 | } 66 | } 67 | ``` 68 | 69 | 第五步:运行。快乐的写代码吧! 70 | 71 | ## 其他 72 | 73 | 更多资料点击[这里](https://github.com/canton7/Stylet/wiki)跳转查看。同时,可以查看本仓库中存放的示例项目。 74 | 75 | 76 | 77 | ## 从 0.0.1升级? 78 | 79 | > 请将avalonia 升级到11.x,[升级指南](https://docs.avaloniaui.net/docs/next/stay-up-to-date/upgrade-from-0.10) 80 | 81 | 0.将`nuget`包`XamlNameReferenceGenerator`移除(新版本已内置) 82 | 83 | 1.找到并打开`App.axaml`文件,移除`AppBootstrapper`资源,即: 84 | 85 | ```xaml 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | ``` 98 | 99 | 2.找到并打开`App.axaml.cs`文件,使其继承于`StyletApplication`, 此时`App.axaml.cs`成为了原来`AppBootstrapper`, 将原来的`AppBootstrapper.cs`的内容移到`App.axaml.cs`文件中即可。 100 | 101 | ***其注意***:`App.axaml.cs`文件内`Initialize()`方法必须调用`base.Initialize();` 102 | 103 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | CONFIG = ENV['CONFIG'] || 'Debug' 2 | 3 | COVERAGE_DIR = 'Coverage' 4 | COVERAGE_FILE = File.join(COVERAGE_DIR, 'coverage.xml') 5 | 6 | GITLINK_REMOTE = 'https://github.com/canton7/stylet' 7 | NUSPEC = 'NuGet/Stylet.nuspec' 8 | NUSPEC_START = 'NuGet/Stylet.start.nuspec' 9 | 10 | ASSEMBLY_INFO = 'Stylet/Properties/AssemblyInfo.cs' 11 | 12 | CSPROJ = 'Stylet/Stylet.csproj' 13 | TEMPLATES_CSPROJ = 'StyletTemplates/StyletTemplates.csproj' 14 | UNIT_TESTS = 'StyletUnitTests/StyletUnitTests.csproj' 15 | 16 | TEMPLATES_DIR = 'StyletTemplates/templates' 17 | 18 | directory COVERAGE_DIR 19 | 20 | desc "Build the project using the current CONFIG (or Debug)" 21 | task :build do 22 | sh 'dotnet', 'build', '-c', CONFIG, CSPROJ 23 | end 24 | 25 | desc "Run unit tests using the current CONFIG (or Debug)" 26 | task :test do 27 | sh 'dotnet', 'test', '-c', CONFIG, UNIT_TESTS 28 | end 29 | 30 | desc "Create NuGet package" 31 | task :package do 32 | # Not sure why these have to be this way around, but they do 33 | sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, "-p:NuSpecFile=../#{NUSPEC_START}" 34 | sh 'dotnet', 'pack', '--no-build', '-c', CONFIG, CSPROJ, '-p:IncludeSymbols=true' 35 | sh 'dotnet', 'pack', '-c', CONFIG, TEMPLATES_CSPROJ 36 | end 37 | 38 | desc "Bump version number" 39 | task :version, [:version] do |t, args| 40 | parts = args[:version].split('.') 41 | parts << '0' if parts.length == 3 42 | version = parts.join('.') 43 | 44 | content = IO.read(CSPROJ) 45 | content[/(.+?)<\/VersionPrefix>/, 1] = version 46 | File.open(CSPROJ, 'w'){ |f| f.write(content) } 47 | 48 | content = IO.read(TEMPLATES_CSPROJ) 49 | content[/(.+?)<\/VersionPrefix>/, 1] = version 50 | File.open(TEMPLATES_CSPROJ, 'w'){ |f| f.write(content) } 51 | 52 | content = IO.read(NUSPEC_START) 53 | content[/(.+?)<\/version>/, 1] = args[:version] 54 | content[%r{}, 1] = args[:version] 55 | File.open(NUSPEC_START, 'w'){ |f| f.write(content) } 56 | 57 | Dir[File.join(TEMPLATES_DIR, '**/*.csproj')].each do |csproj| 58 | content = IO.read(csproj) 59 | content[//, 1] = version 60 | File.open(csproj, 'w'){ |f| f.write(content) } 61 | end 62 | end 63 | 64 | desc "Extract StyletIoC as a standalone file" 65 | task :"extract-stylet-ioc" do 66 | filenames = Dir['Stylet/StyletIoC/**/*.cs'] 67 | usings = Set.new 68 | files = [] 69 | 70 | filenames.each do |file| 71 | contents = File.read(file) 72 | file_usings = contents.scan(/using .*?;$/) 73 | usings.merge(file_usings) 74 | 75 | matches = contents.match(/namespace (.+?)\n{\n(.+)}.*/m) 76 | namespace, file_contents = matches.captures 77 | 78 | files << { 79 | :from => file, 80 | :contents => file_contents, 81 | :namespace => namespace 82 | } 83 | # merged_contents << " // Originally from #{file}\n\n" << file_contents << "\n" 84 | end 85 | 86 | File.open('StyletIoC.cs', 'w') do |outf| 87 | outf.write(usings.to_a.join("\n")) 88 | 89 | outf.puts 90 | 91 | files.group_by{ |x| x[:namespace ] }.each do |namespace, ns_files| 92 | outf.puts("\nnamespace #{namespace}") 93 | outf.puts("{") 94 | 95 | ns_files.each do |file| 96 | outf.puts("\n // Originally from #{file[:from]}\n\n") 97 | outf.puts(file[:contents]) 98 | end 99 | 100 | outf.puts("}\n") 101 | end 102 | end 103 | 104 | # puts merged_contents 105 | 106 | end 107 | 108 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Markup.Xaml; 2 | 3 | namespace Stylet.Samples.DryIoC; 4 | 5 | public partial class App : DryIocStyletApplication 6 | { 7 | public override void Initialize() 8 | { 9 | AvaloniaXamlLoader.Load(this); 10 | base.Initialize(); 11 | } 12 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/DryIocStyletApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Avalonia.Controls; 6 | using DryIoc; 7 | using Stylet.Avalonia; 8 | using Stylet.Avalonia.Primitive; 9 | 10 | namespace Stylet.Samples.DryIoC; 11 | 12 | public class DryIocStyletApplication : StyletApplicationBase 13 | where T: class 14 | { 15 | 16 | /// 17 | /// Gets or sets the StyletApplication's IoC container. This is created after ConfigureIoC has been run. 18 | /// 19 | private readonly IContainer _container; 20 | private readonly List _assemblies; 21 | 22 | protected DryIocStyletApplication() 23 | { 24 | _container = new Container(); 25 | _assemblies = new List(); 26 | } 27 | 28 | /// 29 | /// Overridden from StyletApplicationBase, this sets up the IoC container 30 | /// 31 | protected sealed override void Configure() 32 | { 33 | var assemblies = this.LoadAssemblies(); 34 | this._assemblies.AddRange(assemblies); 35 | this.ConfigureIoC(_container); 36 | this.AutoRegister(); 37 | } 38 | 39 | protected virtual List LoadAssemblies() 40 | { 41 | var assembly = Assembly.GetAssembly(this.GetType()); 42 | if (assembly is null) 43 | assembly = Assembly.GetExecutingAssembly(); 44 | return new List { assembly }; 45 | } 46 | 47 | protected override object GetInstance(Type type) 48 | { 49 | return this._container.Resolve(type); 50 | } 51 | 52 | protected override object GetInstance(Type service, string? key) 53 | { 54 | return this._container.Resolve(service, serviceKey: key); 55 | } 56 | 57 | protected override IEnumerable GetInstances(Type service) 58 | { 59 | return this._container.ResolveMany(service); 60 | } 61 | 62 | protected virtual void ConfigureIoC(IContainer container) 63 | { 64 | // Mark these as weak-bindings, so the user can replace them if they want 65 | var viewManagerConfig = new ViewManagerConfig() 66 | { 67 | ViewFactory = this.GetInstance, 68 | ViewAssemblies = new List { this.GetType().Assembly } 69 | }; 70 | container.RegisterInstance(viewManagerConfig); 71 | container.Register(); 72 | container.RegisterInstance(this); 73 | container.Register(); 74 | container.Register(); 75 | container.Register(); 76 | container.Register(); 77 | } 78 | 79 | /// 80 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 81 | /// 82 | public override void Dispose() 83 | { 84 | base.Dispose(); 85 | 86 | if (this._container != null) 87 | this._container.Dispose(); 88 | } 89 | 90 | private void AutoRegister() 91 | { 92 | var viewManager = _container.Resolve(typeof(IViewManager)) as ViewManager; 93 | if (viewManager == null) 94 | throw new KeyNotFoundException($"{nameof(ViewManager)}未找到"); 95 | var viewTypes = this._assemblies.SelectMany(v => v.GetTypes()).Where(v => v.FullName.EndsWith(viewManager.ViewModelNameSuffix) || typeof(Control).IsAssignableFrom(v)).ToList(); 96 | foreach (var type in viewTypes) 97 | { 98 | _container.Register(type, type); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/MainView.axaml: -------------------------------------------------------------------------------- 1 |  8 | Welcome to Avalonia! 9 | 10 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.DryIoC; 6 | 7 | public partial class MainView : Window 8 | { 9 | public MainView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Stylet.Samples.DryIoC; 2 | 3 | public class MainViewModel 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.DryIoC; 5 | 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .WithInterFont() 20 | .LogToTrace(); 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/Stylet.Samples.DryIoC.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0 5 | enable 6 | true 7 | app.manifest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.DryIoC/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using Avalonia.Markup.Xaml; 5 | using Stylet.Avalonia; 6 | using Stylet.Avalonia.StyletIoC; 7 | 8 | namespace Stylet.Samples.Hello; 9 | 10 | public partial class App : StyletApplication 11 | { 12 | public override void Initialize() 13 | { 14 | AvaloniaXamlLoader.Load(this); 15 | base.Initialize(); 16 | } 17 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/MainView.axaml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace Stylet.Samples.Hello; 4 | 5 | public partial class MainView : Window 6 | { 7 | public MainView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Avalonia.Media; 8 | 9 | using Stylet.Avalonia; 10 | using Stylet.Avalonia.Primitive; 11 | 12 | namespace Stylet.Samples.Hello; 13 | 14 | public class MainViewModel : Screen { 15 | private string _name; 16 | private readonly IWindowManager windowManager; 17 | 18 | public string Name { 19 | 20 | get 21 | { 22 | return this._name; 23 | } 24 | 25 | set 26 | { 27 | SetAndNotify(ref this._name, value); 28 | NotifyOfPropertyChange(nameof(CanSayHello)); 29 | } 30 | } 31 | 32 | public MainViewModel(IWindowManager windowManager) 33 | { 34 | this.DisplayName = "Hello, Stylet"; 35 | this.windowManager = windowManager; 36 | } 37 | 38 | public bool CanSayHello { 39 | get { return !String.IsNullOrEmpty(Name); } 40 | } 41 | 42 | public async Task SayHello() 43 | { 44 | await this.windowManager.ShowMessageBox( 45 | $"Hello, {this.Name}", 46 | "提示框", 47 | MessageBoxButton.OKCancel, 48 | icon: MessageBoxImage.Information, 49 | textAlignment: TextAlignment.Center 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | using Avalonia; 4 | 5 | namespace Stylet.Samples.Hello 6 | { 7 | class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | [STAThread] 13 | public static void Main(string[] args) => BuildAvaloniaApp() 14 | .StartWithClassicDesktopLifetime(args); 15 | 16 | // Avalonia configuration, don't remove; also used by visual designer. 17 | public static AppBuilder BuildAvaloniaApp() 18 | => AppBuilder.Configure() 19 | .UsePlatformDetect() 20 | .LogToTrace(); 21 | } 22 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/RootView.axaml: -------------------------------------------------------------------------------- 1 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/RootView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace Stylet.Samples.Hello; 4 | 5 | partial class RootView 6 | { 7 | public RootView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.Hello/Stylet.Samples.Hello.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | latest 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 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Controls.ApplicationLifetimes; 5 | using Avalonia.Markup.Xaml; 6 | using Stylet.Avalonia; 7 | using Stylet.Avalonia.StyletIoC; 8 | 9 | namespace Stylet.Samples.HelloDialog; 10 | public partial class App : StyletApplication { 11 | public override void Initialize() 12 | { 13 | AvaloniaXamlLoader.Load(this); 14 | base.Initialize(); 15 | } 16 | 17 | protected override void ConfigureIoC(IStyletIoCBuilder builder) 18 | { 19 | base.ConfigureIoC(builder); 20 | builder.Bind().ToAbstractFactory(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/Dialog1View.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/Dialog1View.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Stylet.Samples.HelloDialog; 5 | public partial class Dialog1View : Window 6 | { 7 | public Dialog1View() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/Dialog1ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stylet.Samples.HelloDialog; 9 | public class Dialog1ViewModel : Screen 10 | { 11 | public string Name { get; set; } 12 | 13 | public Dialog1ViewModel() 14 | { 15 | this.DisplayName = "I'm Dialog 1"; 16 | } 17 | 18 | public void Close() 19 | { 20 | this.RequestClose(null); 21 | } 22 | 23 | public void Save() 24 | { 25 | this.RequestClose(true); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using System; 5 | 6 | namespace Stylet.Samples.HelloDialog; 7 | internal class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | [STAThread] 13 | public static void Main(string[] args) => BuildAvaloniaApp() 14 | .StartWithClassicDesktopLifetime(args); 15 | 16 | // Avalonia configuration, don't remove; also used by visual designer. 17 | public static AppBuilder BuildAvaloniaApp() 18 | => AppBuilder.Configure() 19 | .UsePlatformDetect() 20 | .LogToTrace(); 21 | } 22 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/ShellView.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/ShellView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Stylet.Samples.HelloDialog; 5 | public partial class ShellView : Window 6 | { 7 | public ShellView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/ShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stylet.Samples.HelloDialog; 9 | public class ShellViewModel : Screen 10 | { 11 | private IWindowManager windowManager; 12 | private IDialogFactory dialogFactory; 13 | 14 | private string _nameString; 15 | public string NameString 16 | { 17 | get { return this._nameString; } 18 | set { SetAndNotify(ref _nameString, value); } 19 | } 20 | 21 | public ShellViewModel(IWindowManager windowManager, IDialogFactory dialogFactory) 22 | { 23 | this.DisplayName = "Hello Dialog"; 24 | 25 | this.windowManager = windowManager; 26 | this.dialogFactory = dialogFactory; 27 | 28 | this.NameString = "Click the button to show the dialog"; 29 | } 30 | 31 | public async System.Threading.Tasks.Task ShowDialog() 32 | { 33 | //throw new Exception("KABLAMMO"); 34 | var dialogVm = this.dialogFactory.CreateDialog1(); 35 | var result = await this.windowManager.ShowDialog(dialogVm); 36 | if (result) 37 | this.NameString = String.Format("Your name is {0}", dialogVm.Name); 38 | else 39 | this.NameString = "Dialog cancelled"; 40 | } 41 | } 42 | 43 | public interface IDialogFactory 44 | { 45 | Dialog1ViewModel CreateDialog1(); 46 | } 47 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.HelloDialog/Stylet.Samples.HelloDialog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | enable 6 | 7 | copyused 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.MSIoC; 6 | 7 | public partial class App : MsStyletApplication 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | base.Initialize(); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/MainView.axaml: -------------------------------------------------------------------------------- 1 |  8 | Welcome to Avalonia! 9 | 10 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.MSIoC; 6 | 7 | public partial class MainView : Window 8 | { 9 | public MainView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Stylet.Samples.MSIoC; 2 | 3 | public class MainViewModel 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.MSIoC; 5 | 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .WithInterFont() 20 | .LogToTrace(); 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/Stylet.Samples.MSIoC.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0 5 | enable 6 | true 7 | app.manifest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MSIoC/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Controls.ApplicationLifetimes; 5 | using Avalonia.Markup.Xaml; 6 | using Stylet.Avalonia; 7 | using Stylet.Avalonia.StyletIoC; 8 | 9 | namespace Stylet.Samples.MasterDetail; 10 | public partial class App : StyletApplication 11 | { 12 | public override void Initialize() 13 | { 14 | AvaloniaXamlLoader.Load(this); 15 | base.Initialize(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using System; 5 | 6 | namespace Stylet.Samples.MasterDetail; 7 | internal class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | [STAThread] 13 | public static void Main(string[] args) => BuildAvaloniaApp() 14 | .StartWithClassicDesktopLifetime(args); 15 | 16 | // Avalonia configuration, don't remove; also used by visual designer. 17 | public static AppBuilder BuildAvaloniaApp() 18 | => AppBuilder.Configure() 19 | .UsePlatformDetect() 20 | .LogToTrace(); 21 | } 22 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/ShellView.axaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/ShellView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Stylet.Samples.MasterDetail; 5 | public partial class ShellView : Window 6 | { 7 | public ShellView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/ShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Stylet.Samples.MasterDetail; 9 | public class ShellViewModel : Screen 10 | { 11 | public IObservableCollection Employees { get; private set; } 12 | 13 | private EmployeeModel _selectedEmployee; 14 | public EmployeeModel SelectedEmployee 15 | { 16 | get { return this._selectedEmployee; } 17 | set { SetAndNotify(ref this._selectedEmployee, value); } 18 | } 19 | 20 | public ShellViewModel() 21 | { 22 | this.DisplayName = "Master-Detail"; 23 | 24 | this.Employees = new BindableCollection(); 25 | 26 | this.Employees.Add(new EmployeeModel() { Name = "Fred" }); 27 | this.Employees.Add(new EmployeeModel() { Name = "Bob" }); 28 | 29 | this.SelectedEmployee = this.Employees.FirstOrDefault(); 30 | } 31 | 32 | public void AddEmployee() 33 | { 34 | this.Employees.Add(new EmployeeModel() { Name = "Unnamed" }); 35 | } 36 | 37 | public void RemoveEmployee(EmployeeModel item) 38 | { 39 | this.Employees.Remove(item); 40 | } 41 | } 42 | public class EmployeeModel : PropertyChangedBase 43 | { 44 | private string _name; 45 | public string Name 46 | { 47 | get { return this._name; } 48 | set { this.SetAndNotify(ref this._name, value); } 49 | } 50 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.MasterDetail/Stylet.Samples.MasterDetail.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | enable 6 | 7 | copyused 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using Stylet.Samples.NavigationController.Pages; 5 | using System; 6 | using Avalonia.Controls; 7 | using Stylet.Avalonia; 8 | using Stylet.Avalonia.StyletIoC; 9 | 10 | namespace Stylet.Samples.NavigationController 11 | { 12 | public partial class App : StyletApplication 13 | { 14 | public override void Initialize() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | base.Initialize(); 18 | } 19 | protected override void ConfigureIoC(IStyletIoCBuilder builder) 20 | { 21 | base.ConfigureIoC(builder); 22 | builder.Bind().And().To().InSingletonScope(); 23 | builder.Bind().And().To().InSingletonScope(); 24 | // https://github.com/canton7/Stylet/issues/24 25 | builder.Bind>().ToFactory>(c => () => c.Get()); 26 | builder.Bind>().ToFactory>(c => () => c.Get()); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/NavigationController.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using Stylet.Samples.NavigationController.Pages; 3 | using System; 4 | 5 | namespace Stylet.Samples.NavigationController; 6 | public interface INavigationController 7 | { 8 | void NavigateToPage1(); 9 | void NavigateToPage2(string initiator); 10 | } 11 | 12 | public interface INavigationControllerDelegate 13 | { 14 | void NavigateTo(IScreen screen); 15 | } 16 | public class NavigationController : INavigationController 17 | { 18 | private readonly Func page1ViewModelFactory; 19 | private readonly Func page2ViewModelFactory; 20 | private INavigationControllerDelegate? @delegate; 21 | public INavigationControllerDelegate Delegate 22 | { 23 | get 24 | { 25 | if(@delegate is null) 26 | @delegate = IoC.Get(); 27 | return @delegate; 28 | } 29 | } 30 | 31 | public NavigationController(Func page1ViewModelFactory, Func page2ViewModelFactory) 32 | { 33 | this.page1ViewModelFactory = page1ViewModelFactory ?? throw new ArgumentNullException(nameof(page1ViewModelFactory)); 34 | this.page2ViewModelFactory = page2ViewModelFactory ?? throw new ArgumentNullException(nameof(page2ViewModelFactory)); 35 | } 36 | 37 | public void NavigateToPage1() 38 | { 39 | this.Delegate?.NavigateTo(this.page1ViewModelFactory()); 40 | } 41 | 42 | public void NavigateToPage2(string initiator) 43 | { 44 | var vm = this.page2ViewModelFactory(); 45 | vm.Initiator = initiator; 46 | this.Delegate?.NavigateTo(vm); 47 | } 48 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/HeaderView.axaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/HeaderView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.NavigationController.Pages; 6 | 7 | public partial class HeaderView : UserControl 8 | { 9 | public HeaderView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/HeaderViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.NavigationController.Pages; 5 | 6 | public class HeaderViewModel : Screen 7 | { 8 | private readonly INavigationController navigationController; 9 | 10 | public HeaderViewModel(INavigationController navigationController) 11 | { 12 | this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController)); 13 | } 14 | 15 | public void NavigateToPage1() => this.navigationController.NavigateToPage1(); 16 | public void NavigateToPage2() => this.navigationController.NavigateToPage2("the Header"); 17 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page1View.axaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page1View.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.NavigationController.Pages; 6 | 7 | public partial class Page1View : UserControl 8 | { 9 | public Page1View() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page1ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.NavigationController.Pages; 5 | 6 | public class Page1ViewModel : Screen 7 | { 8 | private readonly INavigationController navigationController; 9 | 10 | public Page1ViewModel(INavigationController navigationController) 11 | { 12 | this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController)); 13 | } 14 | 15 | public void NavigateToPage2() => this.navigationController.NavigateToPage2("Page 1"); 16 | 17 | protected override void OnActivate() 18 | { 19 | base.OnActivate(); 20 | } 21 | 22 | protected override void OnDeactivate() 23 | { 24 | base.OnDeactivate(); 25 | } 26 | 27 | protected override void OnViewLoaded() 28 | { 29 | base.OnViewLoaded(); 30 | } 31 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page2View.axaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page2View.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.NavigationController.Pages; 6 | 7 | public partial class Page2View : UserControl 8 | { 9 | public Page2View() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/Page2ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.NavigationController.Pages; 5 | 6 | public class Page2ViewModel : Screen 7 | { 8 | private readonly INavigationController navigationController; 9 | 10 | private string _initiator; 11 | public string Initiator 12 | { 13 | get => this._initiator; 14 | set => this.SetAndNotify(ref this._initiator, value); 15 | } 16 | 17 | public Page2ViewModel(INavigationController navigationController) 18 | { 19 | this.navigationController = navigationController ?? throw new ArgumentNullException(nameof(navigationController)); 20 | } 21 | 22 | public void NavigateToPage1() => this.navigationController.NavigateToPage1(); 23 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/ShellView.axaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/ShellView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.NavigationController.Pages; 6 | 7 | public partial class ShellView : Window 8 | { 9 | public ShellView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Pages/ShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Stylet.Samples.NavigationController.Pages; 6 | 7 | public class ShellViewModel : Conductor, INavigationControllerDelegate 8 | { 9 | public HeaderViewModel HeaderViewModel { get; } 10 | 11 | public ShellViewModel(HeaderViewModel headerViewModel) 12 | { 13 | this.HeaderViewModel = headerViewModel ?? throw new ArgumentNullException(nameof(headerViewModel)); 14 | } 15 | 16 | public void NavigateTo(IScreen screen) 17 | { 18 | this.ActivateItem(screen); 19 | } 20 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.NavigationController 5 | { 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .LogToTrace(); 20 | } 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.NavigationController/Stylet.Samples.NavigationController.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | enable 6 | 7 | copyused 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Markup.Xaml; 2 | using Stylet.Avalonia; 3 | using Stylet.Avalonia.StyletIoC; 4 | 5 | namespace Stylet.Samples.OverridingViewManager; 6 | 7 | public partial class App : StyletApplication 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | base.Initialize(); 13 | } 14 | protected override void ConfigureIoC(IStyletIoCBuilder builder) 15 | { 16 | builder.Bind().To(); 17 | base.ConfigureIoC(builder); 18 | } 19 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/CustomViewManager.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Stylet.Avalonia; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Stylet.Samples.OverridingViewManager; 9 | 10 | 11 | [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] 12 | sealed class ViewModelAttribute : Attribute 13 | { 14 | readonly Type viewModel; 15 | 16 | public ViewModelAttribute(Type viewModel) 17 | { 18 | this.viewModel = viewModel; 19 | } 20 | 21 | public Type ViewModel 22 | { 23 | get { return viewModel; } 24 | } 25 | } 26 | 27 | public class CustomViewManager : ViewManager 28 | { 29 | // Dictionary of ViewModel type -> View type 30 | private readonly Dictionary viewModelToViewMapping; 31 | 32 | public CustomViewManager(ViewManagerConfig config) 33 | : base(config) 34 | { 35 | var mappings = from type in this.ViewAssemblies.SelectMany(x => x.GetExportedTypes()) 36 | let attribute = type.GetCustomAttribute() 37 | where attribute != null && typeof(Control).IsAssignableFrom(type) 38 | select new { View = type, ViewModel = attribute.ViewModel }; 39 | 40 | this.viewModelToViewMapping = mappings.ToDictionary(x => x.ViewModel, x => x.View); 41 | } 42 | 43 | protected override Type LocateViewForModel(Type modelType) 44 | { 45 | Type? viewType = null; 46 | if (!this.viewModelToViewMapping.TryGetValue(modelType, out viewType)) 47 | throw new Exception(String.Format("Could not find View for ViewModel {0}", modelType.Name)); 48 | return viewType; 49 | } 50 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.OverridingViewManager 5 | { 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .LogToTrace(); 20 | } 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/ShellView.axaml: -------------------------------------------------------------------------------- 1 |  9 | Welcome to Avalonia! 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/ShellView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.OverridingViewManager; 6 | 7 | [ViewModel(typeof(ShellViewModel))] 8 | public partial class ShellView : Window 9 | { 10 | public ShellView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/ShellViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Stylet.Samples.OverridingViewManager; 2 | 3 | public class ShellViewModel 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.OverridingViewManager/Stylet.Samples.OverridingViewManager.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | enable 6 | 7 | copyused 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Markup.Xaml; 2 | using Stylet.Avalonia.StyletIoC; 3 | 4 | namespace Stylet.Samples.TabNavigation; 5 | 6 | public partial class App : StyletApplication 7 | { 8 | public override void Initialize() 9 | { 10 | AvaloniaXamlLoader.Load(this); 11 | base.Initialize(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page1View.axaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page1View.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.TabNavigation; 6 | 7 | public partial class Page1View : UserControl 8 | { 9 | public Page1View() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page1ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.TabNavigation; 5 | public class Page1ViewModel : Screen, IDisposable 6 | { 7 | public Page1ViewModel() 8 | { 9 | this.DisplayName = "Page 1"; 10 | } 11 | 12 | protected override void OnClose() 13 | { 14 | base.OnClose(); 15 | } 16 | 17 | public void Dispose() 18 | { 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page2View.axaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page2View.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.TabNavigation; 6 | 7 | public partial class Page2View : UserControl 8 | { 9 | public Page2View() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Page2ViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.TabNavigation; 5 | 6 | public class Page2ViewModel : Screen, IDisposable 7 | { 8 | public Page2ViewModel() 9 | { 10 | this.DisplayName = "Page 2"; 11 | } 12 | 13 | protected override void OnActivate() 14 | { 15 | base.OnActivate(); 16 | } 17 | 18 | protected override void OnDeactivate() 19 | { 20 | base.OnDeactivate(); 21 | } 22 | 23 | protected override void OnClose() 24 | { 25 | base.OnClose(); 26 | } 27 | 28 | public void Dispose() 29 | { 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Stylet.Samples.TabNavigation 5 | { 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .LogToTrace(); 20 | } 21 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/ShellView.axaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/ShellView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Stylet.Samples.TabNavigation; 6 | 7 | public partial class ShellView : Window 8 | { 9 | public ShellView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/ShellViewModel.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia; 2 | 3 | namespace Stylet.Samples.TabNavigation; 4 | 5 | public class ShellViewModel : Conductor.Collection.OneActive 6 | { 7 | public ShellViewModel(Page1ViewModel page1, Page2ViewModel page2) 8 | { 9 | this.Items.Add(page1); 10 | this.Items.Add(page2); 11 | 12 | this.ActiveItem = page1; 13 | } 14 | } -------------------------------------------------------------------------------- /Samples/Stylet.Samples.TabNavigation/Stylet.Samples.TabNavigation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0;net7.0;net8.0 5 | enable 6 | 7 | copyused 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Assets/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealoyal2018/Stylet.Avalonia/cc5cf1bac60b944c43ede7221a522a5418459fdf/Stylet.Avalonia/Assets/error.png -------------------------------------------------------------------------------- /Stylet.Avalonia/Assets/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealoyal2018/Stylet.Avalonia/cc5cf1bac60b944c43ede7221a522a5418459fdf/Stylet.Avalonia/Assets/information.png -------------------------------------------------------------------------------- /Stylet.Avalonia/Assets/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealoyal2018/Stylet.Avalonia/cc5cf1bac60b944c43ede7221a522a5418459fdf/Stylet.Avalonia/Assets/question.png -------------------------------------------------------------------------------- /Stylet.Avalonia/Assets/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealoyal2018/Stylet.Avalonia/cc5cf1bac60b944c43ede7221a522a5418459fdf/Stylet.Avalonia/Assets/warning.png -------------------------------------------------------------------------------- /Stylet.Avalonia/Conductor.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// Conductor with a single active item, and no other items 7 | /// 8 | /// Type of child to conduct 9 | public partial class Conductor : ConductorBaseWithActiveItem where T : class 10 | { 11 | /// 12 | /// Activate the given item, discarding the previous ActiveItem 13 | /// 14 | /// Item to active 15 | public override async void ActivateItem(T item) 16 | { 17 | if (item != null && item.Equals(this.ActiveItem)) 18 | { 19 | if (this.IsActive) 20 | ScreenExtensions.TryActivate(item); 21 | } 22 | else if (await this.CanCloseItem(this.ActiveItem)) 23 | { 24 | // CanCloseItem is null-safe 25 | 26 | this.ChangeActiveItem(item, true); 27 | } 28 | } 29 | 30 | /// 31 | /// Deactive the given item 32 | /// 33 | /// Item to deactivate 34 | public override void DeactivateItem(T item) 35 | { 36 | if (item != null && item.Equals(this.ActiveItem)) 37 | ScreenExtensions.TryDeactivate(this.ActiveItem); 38 | } 39 | 40 | /// 41 | /// Close the given item 42 | /// 43 | /// Item to close 44 | public override async void CloseItem(T item) 45 | { 46 | if (item == null || !item.Equals(this.ActiveItem)) 47 | return; 48 | 49 | if (await this.CanCloseItem(item)) 50 | this.ChangeActiveItem(default(T), true); 51 | } 52 | 53 | /// 54 | /// Determine if this conductor can close. Depends on whether the ActiveItem can close 55 | /// 56 | /// Task indicating whether this can be closed 57 | public override Task CanCloseAsync() 58 | { 59 | // Temporarily, until we remove CanClose 60 | #pragma warning disable CS0618 // Type or member is obsolete 61 | if (!this.CanClose()) 62 | #pragma warning restore CS0618 // Type or member is obsolete 63 | return Task.FromResult(false); 64 | return this.CanCloseItem(this.ActiveItem); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Stylet.Avalonia/ConductorBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Stylet.Avalonia; 7 | 8 | /// 9 | /// Base class for all conductors 10 | /// 11 | /// Type of item to be conducted 12 | public abstract class ConductorBase : Screen, IConductor, IParent, IChildDelegate where T : class 13 | { 14 | private bool _disposeChildren = true; 15 | 16 | /// 17 | /// Gets or sets a value indicating whether to dispose a child when it's closed. True by default 18 | /// 19 | // Can't be an auto-property, since it's virtual so we can't set it in the ctor 20 | public virtual bool DisposeChildren 21 | { 22 | get { return _disposeChildren; } 23 | set { _disposeChildren = value; } 24 | } 25 | 26 | /// 27 | /// Retrieves the Item or Items associated with this Conductor 28 | /// 29 | /// Item or Items associated with this Conductor 30 | public abstract IEnumerable GetChildren(); 31 | 32 | /// 33 | /// Activate the given item 34 | /// 35 | /// Item to activate 36 | public abstract void ActivateItem(T item); 37 | 38 | /// 39 | /// Deactivate the given item 40 | /// 41 | /// Item to deactivate 42 | public abstract void DeactivateItem(T item); 43 | 44 | /// 45 | /// Close the given item 46 | /// 47 | /// Item to deactivate 48 | public abstract void CloseItem(T item); 49 | 50 | /// 51 | /// Ensure an item is ready to be activated 52 | /// 53 | /// Item to use 54 | protected virtual void EnsureItem(T newItem) 55 | { 56 | Debug.Assert(newItem != null); 57 | 58 | var newItemAsChild = newItem as IChild; 59 | if (newItemAsChild != null && newItemAsChild.Parent != this) 60 | newItemAsChild.Parent = this; 61 | } 62 | 63 | /// 64 | /// Utility method to determine if all of the give items can close 65 | /// 66 | /// Items to close 67 | /// Task indicating whether all items can close 68 | protected virtual async Task CanAllItemsCloseAsync(IEnumerable itemsToClose) 69 | { 70 | // We need to call these in order: we don't want them all do show "are you sure you 71 | // want to close" dialogs at once, for instance. 72 | foreach (var itemToClose in itemsToClose) 73 | { 74 | if (!await CanCloseItem(itemToClose)) 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | /// 82 | /// Determine if the given item can be closed 83 | /// 84 | /// Item to use 85 | /// Task indicating whether the item can be closed 86 | protected virtual Task CanCloseItem(T item) 87 | { 88 | var itemAsGuardClose = item as IGuardClose; 89 | if (itemAsGuardClose != null) 90 | return itemAsGuardClose.CanCloseAsync(); 91 | else 92 | return Task.FromResult(true); 93 | } 94 | 95 | /// 96 | /// Close the given child 97 | /// 98 | /// Child to close 99 | /// Unused in this scenario 100 | void IChildDelegate.CloseItem(object item, bool? dialogResult) 101 | { 102 | T typedItem = item as T; 103 | if (typedItem != null) 104 | CloseItem(typedItem); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Stylet.Avalonia/ConductorBaseWithActiveItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// Base class for all conductors which had a single active item 7 | /// 8 | /// Type of item being conducted 9 | public abstract class ConductorBaseWithActiveItem : ConductorBase, IHaveActiveItem where T : class 10 | { 11 | private T _activeItem; 12 | 13 | /// 14 | /// Gets or sets the item which is currently active 15 | /// 16 | public T ActiveItem 17 | { 18 | get { return _activeItem; } 19 | set { ActivateItem(value); } 20 | } 21 | 22 | /// 23 | /// From IParent, fetch all items 24 | /// 25 | /// Children of this conductor 26 | public override IEnumerable GetChildren() 27 | { 28 | return new[] { ActiveItem }; 29 | } 30 | 31 | /// 32 | /// Switch the active item to the given item 33 | /// 34 | /// New item to activate 35 | /// Whether the previously-active item should be closed 36 | protected virtual void ChangeActiveItem(T newItem, bool closePrevious) 37 | { 38 | ScreenExtensions.TryDeactivate(ActiveItem); 39 | if (closePrevious) 40 | this.CloseAndCleanUp(ActiveItem, DisposeChildren); 41 | 42 | _activeItem = newItem; 43 | 44 | if (newItem != null) 45 | { 46 | EnsureItem(newItem); 47 | 48 | if (IsActive) 49 | ScreenExtensions.TryActivate(newItem); 50 | else 51 | ScreenExtensions.TryDeactivate(newItem); 52 | } 53 | 54 | NotifyOfPropertyChange("ActiveItem"); 55 | } 56 | 57 | /// 58 | /// When we're activated, also activate the ActiveItem 59 | /// 60 | protected override void OnActivate() 61 | { 62 | ScreenExtensions.TryActivate(ActiveItem); 63 | } 64 | 65 | /// 66 | /// When we're deactivated, also deactivate the ActiveItem 67 | /// 68 | protected override void OnDeactivate() 69 | { 70 | ScreenExtensions.TryDeactivate(ActiveItem); 71 | } 72 | 73 | /// 74 | /// When we're closed, also close the ActiveItem 75 | /// 76 | protected override void OnClose() 77 | { 78 | this.CloseAndCleanUp(ActiveItem, DisposeChildren); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Stylet.Avalonia/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Stylet.Avalonia; 5 | 6 | /// 7 | /// Useful extension methods on Expressions 8 | /// 9 | public static class ExpressionExtensions 10 | { 11 | /// 12 | /// Given a MemberExpression (or MemberExpression wrapped in a UnaryExpression), get the name of the property 13 | /// 14 | /// Type of the delegate 15 | /// Expression describe the property whose name we want to extract 16 | /// Name of the property referenced by the expression 17 | public static string NameForProperty(this Expression propertyExpression) 18 | { 19 | Expression body; 20 | var expression = propertyExpression.Body as UnaryExpression; 21 | if (expression != null) 22 | body = expression.Operand; 23 | else 24 | body = propertyExpression.Body; 25 | 26 | var member = body as MemberExpression; 27 | if (member == null) 28 | throw new ArgumentException("Property must be a MemberExpression"); 29 | 30 | return member.Member.Name; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | 5 | namespace Stylet.Avalonia.Extensions; 6 | 7 | internal static class EnumExtensions 8 | { 9 | public static string GetDescription(this Enum? self) 10 | { 11 | if (self is null) 12 | { 13 | return null; 14 | } 15 | var attribute = self.GetType() 16 | .GetField(self.ToString()) 17 | .GetCustomAttributes(typeof(DescriptionAttribute), false) 18 | .FirstOrDefault() as DescriptionAttribute; 19 | return attribute == null ? self.ToString() : attribute.Description; 20 | } 21 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/IConductor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// Generalised parent, with many children 7 | /// 8 | /// Type of children 9 | public interface IParent 10 | { 11 | /// 12 | /// Fetch all children of this parent 13 | /// 14 | /// All children owned by this parent 15 | IEnumerable GetChildren(); 16 | } 17 | 18 | /// 19 | /// Thing which has a single active item 20 | /// 21 | /// Type of the active item 22 | public interface IHaveActiveItem 23 | { 24 | /// 25 | /// Gets or sets the only item which is currently active. 26 | /// This normally corresponds to the item being displayed 27 | /// 28 | T ActiveItem { get; set; } 29 | } 30 | 31 | /// 32 | /// Thing which has one or more children, and from which a child can request that it be closed 33 | /// 34 | public interface IChildDelegate 35 | { 36 | /// 37 | /// Called by the child to request that is be closed 38 | /// 39 | /// Child object, which is passed by the child itself 40 | /// DialogResult to use to close, if any 41 | void CloseItem(object item, bool? dialogResult = null); 42 | } 43 | 44 | /// 45 | /// Thing which owns one or more children, and can manage their lifecycles accordingly 46 | /// 47 | /// Type of child being conducted 48 | // ReSharper disable once TypeParameterCanBeVariant 49 | // Not sure whether this might change in future... 50 | public interface IConductor 51 | { 52 | /// 53 | /// Gets or sets a value indicating whether to dispose a child when it's closed. True by default 54 | /// 55 | bool DisposeChildren { get; set; } 56 | 57 | /// 58 | /// Activate the given item 59 | /// 60 | /// Item to activate 61 | void ActivateItem(T item); 62 | 63 | /// 64 | /// Deactivate the given item 65 | /// 66 | /// Item to deactivate 67 | void DeactivateItem(T item); 68 | 69 | /// 70 | /// Close the given item 71 | /// 72 | /// Item to close 73 | void CloseItem(T item); 74 | } 75 | -------------------------------------------------------------------------------- /Stylet.Avalonia/IDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Threading; 4 | 5 | namespace Stylet.Avalonia; 6 | 7 | /// 8 | /// Generalised dispatcher, which can post and send. 9 | /// Used by . 10 | /// 11 | public interface IDispatcher 12 | { 13 | /// 14 | /// Execute asynchronously 15 | /// 16 | /// Action to execute 17 | void Post(Action action); 18 | 19 | /// 20 | /// Execute synchronously 21 | /// 22 | /// Action to execute 23 | void Send(Action action); 24 | 25 | /// 26 | /// Gets a value indicating whether the current thread is the thread being dispatched to 27 | /// 28 | bool IsCurrent { get; } 29 | } 30 | 31 | /// 32 | /// implementation which can dispatch using 33 | /// 34 | public class ApplicationDispatcher : IDispatcher 35 | { 36 | private readonly Dispatcher dispatcher; 37 | 38 | /// 39 | /// Initialises a new instance of the class with the given 40 | /// 41 | /// to use, normally Application.Current.Dispatcher 42 | public ApplicationDispatcher(Dispatcher dispatcher) 43 | { 44 | this.dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); 45 | } 46 | 47 | /// 48 | /// Initialises a new instance of the class with the given 49 | /// 50 | /// to use, normally Application 51 | public ApplicationDispatcher() 52 | : this(Dispatcher.UIThread) 53 | { 54 | } 55 | 56 | /// 57 | public void Post(Action action) 58 | { 59 | dispatcher.Post(action); 60 | } 61 | 62 | /// 63 | public void Send(Action action) 64 | { 65 | dispatcher.InvokeAsync(action); 66 | } 67 | 68 | /// 69 | public bool IsCurrent 70 | { 71 | get { return dispatcher.CheckAccess(); } 72 | } 73 | } 74 | 75 | /// 76 | /// implementation whcih dispatches synchronously. 77 | /// Usually used for unit testing. 78 | /// 79 | public class SynchronousDispatcher : IDispatcher 80 | { 81 | /// 82 | /// Gets the singleton instance of 83 | /// 84 | public static SynchronousDispatcher Instance { get; } = new SynchronousDispatcher(); 85 | private SynchronousDispatcher() { } 86 | 87 | /// 88 | public void Post(Action action) 89 | { 90 | action(); 91 | } 92 | 93 | /// 94 | public void Send(Action action) 95 | { 96 | action(); 97 | } 98 | 99 | /// 100 | public bool IsCurrent 101 | { 102 | get { return true; } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Stylet.Avalonia/INotifyCollectionChanging.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// Notifies listeners of the intention to perform dynamic changes, such as when items get added and removed or the whole list is refreshed. 7 | /// 8 | public interface INotifyCollectionChanging 9 | { 10 | /// 11 | /// Occurs when the collection will change 12 | /// 13 | event NotifyCollectionChangedEventHandler CollectionChanging; 14 | } 15 | -------------------------------------------------------------------------------- /Stylet.Avalonia/INotifyPropertyChangedDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// Knows how to dispatch its PropertyChanged events using a given dispatcher 7 | /// 8 | public interface INotifyPropertyChangedDispatcher 9 | { 10 | /// 11 | /// Gets or sets the dispatcher to use. 12 | /// Called with an action, which should itself be called in the appropriate context 13 | /// 14 | Action PropertyChangedDispatcher { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /Stylet.Avalonia/IValidationAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Stylet.Avalonia; 6 | 7 | /// 8 | /// Generic version of IValidationArapter. Provided for use with StyletIoC 9 | /// 10 | /// 11 | /// Having a generic version allows you implement it using a generic ModelValidator (ModelValidator{T} : IModelValidator{T}) 12 | /// then write a binding rule like this: 13 | /// builder.Bind(typeof(IModelValidator{})).ToAllImplementations() 14 | /// and request a new IModelValidator{MyViewModelType} in your ViewModel's constructor. 15 | /// 16 | /// Type of model being validated 17 | // ReSharper disable once UnusedTypeParameter 18 | public interface IModelValidator : IModelValidator 19 | { 20 | } 21 | 22 | /// 23 | /// Adapter used by ValidationModelBase to perform validation. 24 | /// 25 | /// 26 | /// This should be specialised to the particular ValidationModelBase instance it's validating 27 | /// 28 | public interface IModelValidator 29 | { 30 | /// 31 | /// Called by ValidatingModelBase, which passes in an instance of itself. 32 | /// This allows the IModelValidator to specialize to validating that particular ValidatingModelBase instance 33 | /// 34 | /// Subject to initialize 35 | void Initialize(object subject); 36 | 37 | /// 38 | /// Validate a single property by name, and return an array of validation errors for that property (or null if validation was successful) 39 | /// 40 | /// Property to validate, or to validate the entire model 41 | /// Array of validation errors, or null / empty if validation was successful 42 | Task> ValidatePropertyAsync(string propertyName); 43 | 44 | /// 45 | /// Validate all properties, and return the results for all properties 46 | /// 47 | /// 48 | /// Use a key of to indicate validation errors for the entire model. 49 | /// 50 | /// If a property validates successfully, you MUST return a null entry for it in the returned dictionary! 51 | /// 52 | /// A dictionary of property name => array of validation errors (or null if that property validated successfully) 53 | Task>> ValidateAllPropertiesAsync(); 54 | } 55 | -------------------------------------------------------------------------------- /Stylet.Avalonia/IoC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Stylet.Avalonia; 6 | 7 | public static class IoC 8 | { 9 | internal static Func GetInstance = (service, key) => throw new InvalidOperationException("IoC not initailized"); 10 | 11 | internal static Func> GetInstances = (service) => throw new InvalidOperationException("IoC not initailized"); 12 | 13 | 14 | public static T Get(string? key = null) 15 | { 16 | return (T)GetInstance(typeof(T), key); 17 | } 18 | 19 | public static IEnumerable GetAll() 20 | { 21 | return GetInstances(typeof(T)).Cast(); 22 | } 23 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/LabelledValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Stylet.Avalonia; 5 | 6 | /// 7 | /// Key-value pair useful for attaching labels to objects and displaying them in the view 8 | /// 9 | /// Type of the value 10 | public class LabelledValue : PropertyChangedBase, IEquatable> 11 | { 12 | private string _label; 13 | 14 | /// 15 | /// Gets or sets the label associated with this item. This is displayed in your View 16 | /// 17 | public string Label 18 | { 19 | get { return _label; } 20 | set { SetAndNotify(ref _label, value); } 21 | } 22 | 23 | private T _value; 24 | 25 | /// 26 | /// Gets or sets the value associated with this item. This is used by your ViewModel 27 | /// 28 | public T Value 29 | { 30 | get { return _value; } 31 | set { SetAndNotify(ref _value, value); } 32 | } 33 | 34 | /// 35 | /// Initialises a new instance of the class, without setting Label or Value 36 | /// 37 | public LabelledValue() 38 | { 39 | } 40 | 41 | /// 42 | /// Initialises a new instance of the class, with the given label and value 43 | /// 44 | /// Label to use. This value is displayed in your view 45 | /// Value to use. This is used by your ViewModel 46 | public LabelledValue(string label, T value) 47 | { 48 | _label = label; 49 | _value = value; 50 | } 51 | 52 | /// 53 | /// Indicates whether the current object is equal to another object of the same type. 54 | /// 55 | /// An object to compare with this object 56 | /// true if the current object is equal to the other parameter; otherwise, false. 57 | public bool Equals(LabelledValue other) 58 | { 59 | if (ReferenceEquals(this, other)) 60 | return true; 61 | if (ReferenceEquals(other, null)) 62 | return false; 63 | 64 | return Label == other.Label && EqualityComparer.Default.Equals(Value, other.Value); 65 | } 66 | 67 | /// 68 | /// Indicates whether the current object is equal to another object of any type 69 | /// 70 | /// An object to compare with this object 71 | /// true if the current object is of the same type as the other object, and equal to the other parameter; otherwise, false. 72 | public override bool Equals(object obj) 73 | { 74 | return Equals(obj as LabelledValue); 75 | } 76 | 77 | /// 78 | /// Returns a hash code for the this object 79 | /// 80 | /// A hash code for this object. 81 | public override int GetHashCode() 82 | { 83 | unchecked 84 | { 85 | int hash = 17; 86 | if (Label != null) 87 | hash = hash * 23 + Label.GetHashCode(); 88 | if (Value != null) 89 | hash = hash * 23 + Value.GetHashCode(); 90 | return hash; 91 | } 92 | } 93 | 94 | /// 95 | /// Return the Label associated with this object 96 | /// 97 | /// The Label associated with this object 98 | public override string ToString() 99 | { 100 | return Label; 101 | } 102 | } 103 | 104 | /// 105 | /// Convenience class for constructing LabellelValue{T}'s 106 | /// 107 | public static class LabelledValue 108 | { 109 | /// 110 | /// Construct a new LabelledValue{T}, using method type inference 111 | /// 112 | /// Type of value 113 | /// Label to assign 114 | /// Value to assign 115 | /// Constructed LabelledValue{T} 116 | public static LabelledValue Create(string label, T value) 117 | { 118 | return new LabelledValue(label, value); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Logging/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.Logging; 4 | 5 | /// 6 | /// Logger used by Stylet for internal logging 7 | /// 8 | public interface ILogger 9 | { 10 | /// 11 | /// Log the message as info 12 | /// 13 | /// A formatted message 14 | /// format parameters 15 | void Info(string format, params object[] args); 16 | 17 | /// 18 | /// Log the message as a warning 19 | /// 20 | /// A formatted message 21 | /// format parameters 22 | void Warn(string format, params object[] args); 23 | 24 | /// 25 | /// Log an exception as an error 26 | /// 27 | /// Exception to log 28 | /// Additional message to add to the exception 29 | void Error(Exception exception, string message = null); 30 | } 31 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Logging/LogManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.Logging; 4 | 5 | /// 6 | /// Manager for ILoggers. Used to create new ILoggers, and set up how ILoggers are created 7 | /// 8 | public static class LogManager 9 | { 10 | private static readonly ILogger nullLogger = new NullLogger(); 11 | 12 | /// 13 | /// Gets or sets a value indicating whether logging is enabled 14 | /// 15 | /// 16 | /// When false (the default), a null logger will be returned by GetLogger(). 17 | /// When true, LoggerFactory will be used to create a new logger 18 | /// 19 | public static bool Enabled { get; set; } 20 | 21 | /// 22 | /// Gets or sets the factory used to create new ILoggers, used by GetLogger 23 | /// 24 | /// 25 | /// LogManager.LoggerFactory = name => new MyLogger(name); 26 | /// 27 | public static Func LoggerFactory { get; set; } 28 | 29 | static LogManager() 30 | { 31 | LoggerFactory = name => new TraceLogger(name); 32 | } 33 | 34 | /// 35 | /// Get a new ILogger for the given type 36 | /// 37 | /// Type which is using the ILogger 38 | /// ILogger for use by the given type 39 | public static ILogger GetLogger(Type type) 40 | { 41 | return GetLogger(type.FullName); 42 | } 43 | 44 | /// 45 | /// Get a new ILogger with the given name 46 | /// 47 | /// Name of the ILogger 48 | /// ILogger with the given name 49 | public static ILogger GetLogger(string name) 50 | { 51 | return Enabled ? LoggerFactory(name) : nullLogger; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Logging/NullLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.Logging; 4 | 5 | /// 6 | /// ILogger implementation which does nothing - used by default 7 | /// 8 | public class NullLogger : ILogger 9 | { 10 | /// 11 | /// Log the message as info 12 | /// 13 | /// A formatted message 14 | /// format parameters 15 | public void Info(string format, params object[] args) { } 16 | 17 | /// 18 | /// Log the message as a warning 19 | /// 20 | /// A formatted message 21 | /// format parameters 22 | public void Warn(string format, params object[] args) { } 23 | 24 | /// 25 | /// Log an exception as an error 26 | /// 27 | /// Exception to log 28 | /// Additional message to add to the exception 29 | public void Error(Exception exception, string message = null) { } 30 | } 31 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Logging/TraceLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Stylet.Avalonia.Logging; 5 | 6 | /// 7 | /// ILogger implementation which uses Debug.WriteLine 8 | /// 9 | public class TraceLogger : ILogger 10 | { 11 | private readonly string name; 12 | 13 | /// 14 | /// Initialises a new instance of the class, with the given name 15 | /// 16 | /// Name of the DebugLogger 17 | public TraceLogger(string name) 18 | { 19 | this.name = name; 20 | } 21 | 22 | /// 23 | /// Log the message as info 24 | /// 25 | /// A formatted message 26 | /// format parameters 27 | public void Info(string format, params object[] args) 28 | { 29 | Trace.WriteLine(string.Format("INFO [{1}] {0}", string.Format(format, args), name), "Stylet"); 30 | } 31 | 32 | /// 33 | /// Log the message as a warning 34 | /// 35 | /// A formatted message 36 | /// format parameters 37 | public void Warn(string format, params object[] args) 38 | { 39 | Trace.WriteLine(string.Format("WARN [{1}] {0}", string.Format(format, args), name), "Stylet"); 40 | } 41 | 42 | /// 43 | /// Log an exception as an error 44 | /// 45 | /// Exception to log 46 | /// Additional message to add to the exception 47 | public void Error(Exception exception, string message = null) 48 | { 49 | if (message == null) 50 | Trace.WriteLine(string.Format("ERROR [{1}] {0}", exception, name), "Stylet"); 51 | else 52 | Trace.WriteLine(string.Format("ERROR [{2}] {0} {1}", message, exception, name), "Stylet"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Primitive/IMessageBoxViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | 3 | namespace Stylet.Avalonia.Primitive; 4 | 5 | /// 6 | /// Interface for a MessageBoxViewModel. MessageBoxWindowManagerExtensions.ShowMessageBox will use the configured implementation of this 7 | /// 8 | public interface IMessageBoxViewModel 9 | { 10 | /// 11 | /// Setup the MessageBoxViewModel with the information it needs 12 | /// 13 | /// A that specifies the text to display. 14 | /// A that specifies the title bar caption to display. 15 | /// A value that specifies which button or buttons to display. 16 | /// A value that specifies the icon to display. 17 | /// A value that specifies the default result of the message box. 18 | /// A value that specifies the cancel result of the message box 19 | /// The to use, overrides the 20 | /// The to use, overrides the 21 | void Setup( 22 | string text, 23 | string? caption , 24 | MessageBoxButton buttons = MessageBoxButton.OK, 25 | MessageBoxImage icon = MessageBoxImage.None, 26 | MessageBoxResult defaultResult = MessageBoxResult.OK, 27 | MessageBoxResult cancelResult = MessageBoxResult.None, 28 | FlowDirection flowDirection = FlowDirection.LeftToRight, 29 | TextAlignment textAlignment = TextAlignment.Left); 30 | 31 | /// 32 | /// Gets the button clicked by the user, after they've clicked it 33 | /// 34 | MessageBoxResult ClickedButton { get; } 35 | } 36 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Primitive/MessageBoxView.axaml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 25 | 32 | 38 | 39 | 40 | 41 | 47 | 48 | 52 | 56 | 57 | 58 | 62 | 63 | 64 | 65 | 66 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Primitive/MessageBoxView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace Stylet.Avalonia.Primitive; 4 | 5 | public partial class MessageBoxView : Window 6 | { 7 | public MessageBoxView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Avalonia.Metadata; 5 | 6 | [assembly: XmlnsDefinition("https://github.com/sealoyal2018/stylet.avalonia", "Stylet.Avalonia.Xaml")] 7 | [assembly: XmlnsPrefix("https://github.com/sealoyal2018/stylet.avalonia", "s")] 8 | -------------------------------------------------------------------------------- /Stylet.Avalonia/PropertyChangedBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq.Expressions; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace Stylet.Avalonia; 8 | 9 | /// 10 | /// Base class for things which can raise PropertyChanged events 11 | /// 12 | public abstract class PropertyChangedBase : INotifyPropertyChanged, INotifyPropertyChangedDispatcher 13 | { 14 | private Action _propertyChangedDispatcher = Execute.DefaultPropertyChangedDispatcher; 15 | 16 | /// 17 | /// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to Execute.DefaultPropertyChangedDispatcher 18 | /// 19 | [System.Xml.Serialization.XmlIgnore] 20 | public virtual Action PropertyChangedDispatcher 21 | { 22 | get { return _propertyChangedDispatcher; } 23 | set { _propertyChangedDispatcher = value; } 24 | } 25 | 26 | /// 27 | /// Occurs when a property value changes 28 | /// 29 | public event PropertyChangedEventHandler PropertyChanged; 30 | 31 | /// 32 | /// Refresh all properties 33 | /// 34 | public void Refresh() 35 | { 36 | NotifyOfPropertyChange(string.Empty); 37 | } 38 | 39 | /// 40 | /// Raise a PropertyChanged notification from the property in the given expression, e.g. NotifyOfPropertyChange(() => this.Property) 41 | /// 42 | /// Type of property being notified 43 | /// Expression describing the property to raise a PropertyChanged notification for 44 | protected virtual void NotifyOfPropertyChange(Expression> property) 45 | { 46 | OnPropertyChanged(property.NameForProperty()); 47 | } 48 | 49 | /// 50 | /// Raise a PropertyChanged notification from the property with the given name 51 | /// 52 | /// Name of the property to raise a PropertyChanged notification for. Defaults to the calling property 53 | protected virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = "") 54 | { 55 | OnPropertyChanged(propertyName); 56 | } 57 | 58 | /// 59 | /// Fires the PropertyChanged notification. 60 | /// 61 | /// Specially named so that Fody.PropertyChanged calls it 62 | /// Name of the property to raise the notification for 63 | [EditorBrowsable(EditorBrowsableState.Never)] 64 | protected virtual void OnPropertyChanged(string propertyName) 65 | { 66 | if (PropertyChanged != null) 67 | { 68 | Execute.OnUIThread(() => PropertyChangedDispatcher(() => 69 | { 70 | var handler = PropertyChanged; 71 | if (handler != null) 72 | handler(this, new PropertyChangedEventArgs(propertyName)); 73 | })); 74 | } 75 | } 76 | 77 | /// 78 | /// Takes, by reference, a field, and its new value. If field != value, will set field = value and raise a PropertyChanged notification 79 | /// 80 | /// Type of field being set and notified 81 | /// Field to assign 82 | /// Value to assign to the field, if it differs 83 | /// Name of the property to notify for. Defaults to the calling property 84 | /// True if field != value and a notification was raised; false otherwise 85 | protected virtual bool SetAndNotify(ref T field, T value, [CallerMemberName] string propertyName = "") 86 | { 87 | if (!EqualityComparer.Default.Equals(field, value)) 88 | { 89 | field = value; 90 | NotifyOfPropertyChange(propertyName: propertyName); 91 | return true; 92 | } 93 | else 94 | { 95 | return false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Stylet.Avalonia/PropertyChangedWeakEventManager.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Stylet.Avalonia; 4 | 5 | /// 6 | /// WeakEventManager for INotifyPropertyChanged.PropertyChanged. 7 | /// 8 | internal sealed class PropertyChangedWeakEventManager : 9 | WeakEventManagerBase 10 | { 11 | protected override void StartListening(INotifyPropertyChanged source) => source.PropertyChanged += DeliverEvent; 12 | 13 | protected override void StopListening(INotifyPropertyChanged source) => source.PropertyChanged -= DeliverEvent; 14 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace Stylet.Avalonia; 5 | 6 | public sealed class RelayCommand :ICommand 7 | { 8 | public event EventHandler? CanExecuteChanged; 9 | private readonly Action execute; 10 | private readonly Func? canExecute; 11 | 12 | public RelayCommand(Action execute) 13 | { 14 | ArgumentNullException.ThrowIfNull(execute); 15 | 16 | this.execute = execute; 17 | } 18 | 19 | public void NotifyCanExecuteChanged() 20 | { 21 | CanExecuteChanged?.Invoke(this, EventArgs.Empty); 22 | } 23 | 24 | public RelayCommand(Action execute, Func canExecute) 25 | { 26 | ArgumentNullException.ThrowIfNull(execute); 27 | ArgumentNullException.ThrowIfNull(canExecute); 28 | 29 | this.execute = execute; 30 | this.canExecute = canExecute; 31 | } 32 | 33 | public bool CanExecute(object? parameter) 34 | { 35 | if (this.canExecute is null) 36 | return true; 37 | return this.canExecute.Invoke(); 38 | } 39 | 40 | public void Execute(object? parameter) 41 | { 42 | this.execute.Invoke(); 43 | } 44 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/Stylet.Avalonia.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0;net7.0;net8.0 4 | enable 5 | 6 | copyused 7 | true 8 | Stylet.Avalonia 9 | sealoyal 10 | A very lightweight but powerful ViewModel-First MVVM framework for AvaloniaUI, inspired by Caliburn.Micro. 11 | StyletIcon.png 12 | README.md 13 | https://github.com/sealoyal2018/Stylet.Avalonia 14 | https://github.com/sealoyal2018/Stylet.Avalonia 15 | 0.5.1 16 | Copyright © 2023 Sealoyal 17 | True 18 | LICENSE.txt 19 | True 20 | Stylet.Avalonia 21 | 0.5.1 22 | 0.5.1 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | \ 42 | 43 | 44 | True 45 | \ 46 | 47 | 48 | True 49 | \ 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletApplicationBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Avalonia; 5 | using Avalonia.Controls; 6 | using Avalonia.Controls.ApplicationLifetimes; 7 | using Avalonia.Markup.Xaml; 8 | 9 | namespace Stylet.Avalonia; 10 | 11 | /// 12 | /// StyletApplication to be extended by applications which don't want to use StyletIoC as the IoC container. 13 | /// 14 | public abstract class StyletApplicationBase : Application, IWindowManagerConfig, IDisposable 15 | where T: class 16 | { 17 | public override void Initialize() 18 | { 19 | IoC.GetInstance = GetInstance; 20 | IoC.GetInstances = GetInstances; 21 | base.Initialize(); 22 | Execute.Dispatcher = new ApplicationDispatcher(); 23 | Configure(); 24 | } 25 | protected abstract IEnumerable GetInstances(Type service); 26 | 27 | /// 28 | /// Given a type, use the IoC container to fetch an instance of it 29 | /// 30 | /// Type of instance to fetch 31 | /// Fetched instance 32 | protected abstract object GetInstance(Type type); 33 | 34 | protected abstract object GetInstance(Type service, string? key); 35 | 36 | /// 37 | /// Override to configure your IoC container, and anything else 38 | /// 39 | protected virtual void Configure() { } 40 | 41 | /// 42 | /// Called on application startup. This occur after this.Args has been assigned, but before the IoC container has been configured 43 | /// 44 | protected virtual void OnStart() { } 45 | 46 | /// 47 | /// Returns the currently-displayed window, or null if there is none (or it can't be determined) 48 | /// 49 | /// The currently-displayed window, or null 50 | public virtual TopLevel? GetActiveWindow() 51 | { 52 | if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desk) 53 | { 54 | // return win ?? desk.MainWindow; 55 | throw new NotImplementedException("Mobile terminal adaptation is not implemented"); // 移动端暂未支持 56 | } 57 | 58 | var win = desk.Windows.OfType().FirstOrDefault(x => x.IsActive); 59 | return TopLevel.GetTopLevel(win); 60 | } 61 | 62 | public sealed override void OnFrameworkInitializationCompleted() 63 | { 64 | if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) 65 | { 66 | throw new NotImplementedException("Mobile terminal adaptation is not implemented"); // 移动端暂未支持 67 | } 68 | OnStart(); 69 | var vm = IoC.Get(); 70 | var winmgr = IoC.Get(); 71 | winmgr.ShowWindow(vm); 72 | base.OnFrameworkInitializationCompleted(); 73 | } 74 | 75 | /// 76 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 77 | /// 78 | public virtual void Dispose() 79 | { 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletConductorExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq; 3 | 4 | namespace Stylet.Avalonia; 5 | 6 | // Don't name ConductorExtensions, otherwise it's too obvious when someone types 'Conductor' 7 | 8 | /// 9 | /// Extension methods used by the Conductor classes 10 | /// 11 | public static class StyletConductorExtensions 12 | { 13 | /// 14 | /// For each item in a list, set the parent to the current conductor 15 | /// 16 | /// Type of conductor 17 | /// Parent to set the items' parent to 18 | /// Items to manipulate 19 | /// True to active the item, false to deactive it 20 | public static void SetParentAndSetActive(this IConductor parent, IEnumerable items, bool active) 21 | { 22 | foreach (var item in items) 23 | { 24 | var itemAsChild = item as IChild; 25 | if (itemAsChild != null) 26 | itemAsChild.Parent = parent; 27 | 28 | if (active) 29 | ScreenExtensions.TryActivate(item); 30 | else 31 | ScreenExtensions.TryDeactivate(item); 32 | } 33 | } 34 | 35 | /// 36 | /// Close an item, and clear its parent if it's set to the current parent 37 | /// 38 | /// Type of conductor 39 | /// Parent 40 | /// Item to close and clean up 41 | /// True to dispose children as well as close them 42 | public static void CloseAndCleanUp(this IConductor parent, T item, bool dispose) 43 | { 44 | ScreenExtensions.TryClose(item); 45 | 46 | var itemAsChild = item as IChild; 47 | if (itemAsChild != null && itemAsChild.Parent == parent) 48 | itemAsChild.Parent = null; 49 | 50 | if (dispose) 51 | ScreenExtensions.TryDispose(item); 52 | } 53 | 54 | /// 55 | /// For each item in a list, close it, and if its parent is set to the given parent, clear that parent 56 | /// 57 | /// Type of conductor 58 | /// Parent 59 | /// List of items to close and clean up 60 | /// True to dispose children as well as close them 61 | public static void CloseAndCleanUp(this IConductor parent, IEnumerable items, bool dispose) 62 | { 63 | foreach (var item in items.OfType()) 64 | { 65 | parent.CloseAndCleanUp(item, dispose); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Creation/BuilderTypeKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.StyletIoC.Creation; 4 | 5 | /// 6 | /// Defines and type + key for a service, used in setting up bindings 7 | /// 8 | public class BuilderTypeKey : IEquatable 9 | { 10 | /// 11 | /// Gets or sets the Type associated with this Type+Key 12 | /// 13 | public Type Type { get; set; } 14 | 15 | /// 16 | /// Gets or sets the Key associated with this Type+Key 17 | /// 18 | public string Key { get; set; } 19 | 20 | /// 21 | /// Initialises a new instance of the class with the given type 22 | /// 23 | /// Type to associated with this Type+Key 24 | public BuilderTypeKey(Type type) 25 | { 26 | Type = type; 27 | } 28 | 29 | /// 30 | /// Initialises a new instance of the class with the given type and key 31 | /// 32 | /// Type to associated with this Type+Key 33 | /// Key to associated with this Type+Key 34 | public BuilderTypeKey(Type type, string key) 35 | { 36 | Type = type; 37 | Key = key; 38 | } 39 | 40 | /// 41 | /// Determines whether the specified object is equal to the current object. 42 | /// 43 | /// The object to compare with the current object. 44 | /// true if the specified object is equal to the current object; otherwise, false 45 | public override bool Equals(object obj) 46 | { 47 | return base.Equals(obj as BuilderTypeKey); 48 | } 49 | 50 | /// 51 | /// Calculates a HashCode for the current object 52 | /// 53 | /// A hash code for the current object. 54 | public override int GetHashCode() 55 | { 56 | unchecked 57 | { 58 | int hash = 17; 59 | hash = hash * 23 + Type.GetHashCode(); 60 | if (Key != null) 61 | hash = hash * 23 + Key.GetHashCode(); 62 | return hash; 63 | } 64 | } 65 | 66 | /// 67 | /// Determines whether the specified object is equal to the current object. 68 | /// 69 | /// The object to compare with the current object. 70 | /// true if the specified object is equal to the current object; otherwise, false 71 | public bool Equals(BuilderTypeKey other) 72 | { 73 | return other != null && 74 | Type == other.Type && 75 | other.Key == Key; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Creation/ICreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Stylet.Avalonia.StyletIoC.Creation; 5 | 6 | /// 7 | /// An ICreator is responsible for creating an instance of an object on demand 8 | /// 9 | public interface ICreator 10 | { 11 | /// 12 | /// Gets the type of object that will be created 13 | /// 14 | RuntimeTypeHandle TypeHandle { get; } 15 | 16 | /// 17 | /// Fetches an expression evaluating to an instance on demand 18 | /// 19 | /// Context which calls this method 20 | /// An expression evaluating to an instance of the specified Type 21 | Expression GetInstanceExpression(ParameterExpression registrationContext); 22 | } 23 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Creation/IRegistration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Creation; 6 | 7 | /// 8 | /// Delegate used to create an IRegistration 9 | /// 10 | /// Context on which this registration will be created 11 | /// Service types and keys for this registration 12 | /// ICreator used by the IRegistration to create new instances 13 | /// A new IRegistration 14 | public delegate IRegistration RegistrationFactory(IRegistrationContext parentContext, List serviceTypes, ICreator creator); 15 | 16 | /// 17 | /// An IRegistration is responsible to returning an appropriate (new or cached) instanced of a type, or an expression doing the same. 18 | /// It owns an ICreator, and will use it to create a new instance when needed. 19 | /// 20 | public interface IRegistration 21 | { 22 | /// 23 | /// Gets the type of the object returned by the registration 24 | /// 25 | RuntimeTypeHandle TypeHandle { get; } 26 | 27 | /// 28 | /// Fetches an instance of the relevaent type 29 | /// 30 | /// An object of type Type, which is supplied by the ICreator 31 | Func GetGenerator(); 32 | 33 | /// 34 | /// Fetches an expression which evaluates to an instance of the relevant type 35 | /// 36 | /// Context which calls this method 37 | /// An expression evaluating to an instance of type Type, which is supplied by the ICreator> 38 | Expression GetInstanceExpression(ParameterExpression registrationContext); 39 | } 40 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Creation/IRegistrationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Stylet.Avalonia.StyletIoC.Creation; 5 | 6 | /// 7 | /// Context used by IRegistration and ICreator to get things needed to create instances, etc 8 | /// 9 | public interface IRegistrationContext : IContainer 10 | { 11 | /// 12 | /// Get the BuilderUpper for the given type 13 | /// 14 | /// 15 | /// A BuilderUpper is something which knows how to build up an object - that is, populate 16 | /// all parameters marked with [Inject]. 17 | /// 18 | /// The type of object to retrieve the BuilderUpper for 19 | /// The appropriate BuilderUpper 20 | BuilderUpper GetBuilderUpper(Type type); 21 | 22 | /// 23 | /// Determine whether the container can resolve the given type+key combination 24 | /// 25 | /// Type to resolve 26 | /// Key to resolve 27 | /// True if the container can resolve this type+key combination 28 | bool CanResolve(Type type, string key); 29 | 30 | /// 31 | /// Retrieve a single IRegistration for the type+key combination, or throw an exception if non, or more than one, are avaiable 32 | /// 33 | /// Type to search for 34 | /// Key to search for 35 | /// 36 | /// If true, a Type of IEnumerableI{Something} can return a registration which can generate a List{ISomething}, 37 | /// where each element in that list is a different instance implementing ISomething 38 | /// 39 | /// The appropriate registration 40 | IRegistration GetSingleRegistration(Type type, string key, bool searchGetAllTypes); 41 | 42 | /// 43 | /// Retrieve all IRegistrations for the type+key combination 44 | /// 45 | /// If a single registration exists, then the returned list will contain a single entry 46 | /// Type to search for 47 | /// Key to search for 48 | /// 49 | /// If true, a Type of IEnumerableI{Something} can return a registration which can generate a List{ISomething}, 50 | /// where each element in that list is a different instance implementing ISomething 51 | /// 52 | /// The appropriate registrations 53 | IReadOnlyList GetAllRegistrations(Type type, string key, bool searchGetAllTypes); 54 | 55 | /// 56 | /// Fired when Dispose is called on the container. 57 | /// Registrations which retain instances should dispose and release them when this event is fired 58 | /// 59 | event EventHandler Disposing; 60 | } 61 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/IContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Stylet.Avalonia.StyletIoC; 5 | 6 | /// 7 | /// Describes an IoC container, specifically StyletIoC 8 | /// 9 | public interface IContainer : IDisposable 10 | { 11 | /// 12 | /// Compile all known bindings (which would otherwise be compiled when needed), checking the dependency graph for consistency 13 | /// 14 | /// If true, throw if we fail to compile a type 15 | void Compile(bool throwOnError = true); 16 | 17 | /// 18 | /// Fetch a single instance of the specified type 19 | /// 20 | /// Type of service to fetch an implementation for 21 | /// Key that implementations of the service to fetch were registered with, defaults to null 22 | /// An instance of the requested service 23 | object Get(Type type, string key = null); 24 | 25 | /// 26 | /// Fetch a single instance of the specified type 27 | /// 28 | /// Type of service to fetch an implementation for 29 | /// Key that implementations of the service to fetch were registered with, defaults to null 30 | /// An instance of the requested service 31 | T Get(string key = null); 32 | 33 | /// 34 | /// Fetch instances of all types which implement the specified service 35 | /// 36 | /// Type of the service to fetch implementations for 37 | /// Key that implementations of the service to fetch were registered with, defaults to null 38 | /// All implementations of the requested service, with the requested key 39 | IEnumerable GetAll(Type type, string key = null); 40 | 41 | /// 42 | /// Fetch instances of all types which implement the specified service 43 | /// 44 | /// Type of the service to fetch implementations for 45 | /// Key that implementations of the service to fetch were registered with, defaults to null 46 | /// All implementations of the requested service, with the requested key 47 | IEnumerable GetAll(string key = null); 48 | 49 | /// 50 | /// If type is an IEnumerable{T} or similar, is equivalent to calling GetAll{T}. Else, is equivalent to calling Get{T}. 51 | /// 52 | /// If IEnumerable{T}, will fetch all implementations of T, otherwise wil fetch a single T 53 | /// Key that implementations of the service to fetch were registered with, defaults to null 54 | /// The resolved result 55 | object GetTypeOrAll(Type type, string key = null); 56 | 57 | /// 58 | /// If type is an IEnumerable{T} or similar, is equivalent to calling GetAll{T}. Else, is equivalent to calling Get{T}. 59 | /// 60 | /// If IEnumerable{T}, will fetch all implementations of T, otherwise wil fetch a single T 61 | /// Key that implementations of the service to fetch were registered with, defaults to null 62 | /// The resolved result 63 | T GetTypeOrAll(string key = null); 64 | 65 | /// 66 | /// For each property/field with the [Inject] attribute, sets it to an instance of that type 67 | /// 68 | /// Item to build up 69 | void BuildUp(object item); 70 | } 71 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/IInjectionAware.cs: -------------------------------------------------------------------------------- 1 | namespace StyletIoC; 2 | 3 | /// 4 | /// Interface to be implemented by objects if they want to be notified when property injection has occurred 5 | /// 6 | public interface IInjectionAware 7 | { 8 | /// 9 | /// Called by StyletIoC when property injection has occurred 10 | /// 11 | void ParametersInjected(); 12 | } 13 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/InjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.StyletIoC; 4 | 5 | /// 6 | /// Attribute which can be used to mark the constructor to use, properties to inject, which key to use to resolve an injected property, and others. See the docs 7 | /// 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] 9 | public sealed class InjectAttribute : Attribute 10 | { 11 | /// 12 | /// Initialises a new instance of the class 13 | /// 14 | public InjectAttribute() 15 | { 16 | } 17 | 18 | /// 19 | /// Initialises a new instance of the class, which has the specified key 20 | /// 21 | /// Key to associate (meaning depends on context) 22 | public InjectAttribute(string key) 23 | { 24 | Key = key; 25 | } 26 | 27 | /// 28 | /// Gets or sets the key to use to resolve the relevant dependency 29 | /// 30 | public string Key { get; set; } 31 | } 32 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderAbstractFactoryBinding.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using Stylet.Avalonia.StyletIoC.Creation; 4 | using Stylet.Avalonia.StyletIoC.Internal.Creators; 5 | using Stylet.Avalonia.StyletIoC.Internal.Registrations; 6 | 7 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 8 | 9 | internal class BuilderAbstractFactoryBinding : BuilderBindingBase 10 | { 11 | private BuilderTypeKey ServiceType { get { return ServiceTypes[0]; } } 12 | 13 | public BuilderAbstractFactoryBinding(List serviceTypes) 14 | : base(serviceTypes) 15 | { 16 | // This should be ensured by the fluent interfaces 17 | Trace.Assert(serviceTypes.Count == 1); 18 | 19 | if (ServiceType.Type.IsGenericTypeDefinition) 20 | throw new StyletIoCRegistrationException(string.Format("Unbound generic type {0} can't be used as an abstract factory", ServiceType.Type.GetDescription())); 21 | } 22 | 23 | public override void Build(Container container) 24 | { 25 | var factoryType = container.GetFactoryForType(ServiceType.Type); 26 | var creator = new AbstractFactoryCreator(factoryType); 27 | var registration = new TransientRegistration(creator); 28 | 29 | container.AddRegistration(new TypeKey(ServiceType.Type.TypeHandle, ServiceType.Key), registration); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderBindTo.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 9 | 10 | internal class BuilderBindTo : IBindTo, IAndOrToMultipleServices 11 | { 12 | private readonly Func, string, IEnumerable> getAssemblies; 13 | public List ServiceTypes { get; private set; } 14 | private BuilderBindingBase builderBinding; 15 | public bool IsWeak { get { return builderBinding?.IsWeak ?? false; } } 16 | 17 | public BuilderBindTo(Type serviceType, Func, string, IEnumerable> getAssemblies) 18 | { 19 | ServiceTypes = new List() { new BuilderTypeKey(serviceType) }; 20 | this.getAssemblies = getAssemblies; 21 | } 22 | 23 | public IWithKeyOrAndOrToMultipleServices And() 24 | { 25 | return And(typeof(TService)); 26 | } 27 | 28 | public IWithKeyOrAndOrToMultipleServices And(Type serviceType) 29 | { 30 | ServiceTypes.Add(new BuilderTypeKey(serviceType)); 31 | return this; 32 | } 33 | 34 | public IAndOrToMultipleServices WithKey(string key) 35 | { 36 | // Should have been ensured by the fluent interface 37 | Trace.Assert(ServiceTypes.Count > 0); 38 | 39 | ServiceTypes[ServiceTypes.Count - 1].Key = key; 40 | return this; 41 | } 42 | 43 | public IInScopeOrWithKeyOrAsWeakBinding ToSelf() 44 | { 45 | // This should be ensured by the fluent interfaces 46 | Trace.Assert(ServiceTypes.Count == 1); 47 | 48 | return To(ServiceTypes[0].Type); 49 | } 50 | 51 | public IInScopeOrWithKeyOrAsWeakBinding To(Type implementationType) 52 | { 53 | builderBinding = new BuilderTypeBinding(ServiceTypes, implementationType); 54 | return builderBinding; 55 | } 56 | 57 | public IInScopeOrWithKeyOrAsWeakBinding To() 58 | { 59 | return To(typeof(TImplementation)); 60 | } 61 | 62 | public IInScopeOrWithKeyOrAsWeakBinding ToFactory(Func factory) 63 | { 64 | builderBinding = new BuilderFactoryBinding(ServiceTypes, factory); 65 | return builderBinding; 66 | } 67 | 68 | public IWithKeyOrAsWeakBindingOrDisposeWithContainer ToInstance(object instance) 69 | { 70 | var builderBinding = new BuilderInstanceBinding(ServiceTypes, instance); 71 | this.builderBinding = builderBinding; 72 | return builderBinding; 73 | } 74 | 75 | public IWithKeyOrAsWeakBinding ToAbstractFactory() 76 | { 77 | builderBinding = new BuilderAbstractFactoryBinding(ServiceTypes); 78 | return builderBinding; 79 | } 80 | 81 | public IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(IEnumerable assemblies, bool allowZeroImplementations = false) 82 | { 83 | builderBinding = new BuilderToAllImplementationsBinding(ServiceTypes, getAssemblies(assemblies, "ToAllImplementations"), allowZeroImplementations); 84 | return builderBinding; 85 | } 86 | 87 | public IInScopeOrWithKeyOrAsWeakBinding ToAllImplementations(bool allowZeroImplementations = false, params Assembly[] assemblies) 88 | { 89 | return ToAllImplementations(assemblies.AsEnumerable(), allowZeroImplementations); 90 | } 91 | 92 | internal void Build(Container container) 93 | { 94 | if (builderBinding == null) 95 | throw new StyletIoCRegistrationException(string.Format("Service type {0} is not bound to anything", ServiceTypes[0].Type.GetDescription())); 96 | 97 | builderBinding.Build(container); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderFactoryBinding.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using Stylet.Avalonia.StyletIoC.Internal.Creators; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 7 | 8 | internal class BuilderFactoryBinding : BuilderBindingBase 9 | { 10 | private readonly Func factory; 11 | 12 | public BuilderFactoryBinding(List serviceTypes, Func factory) 13 | : base(serviceTypes) 14 | { 15 | foreach (var serviceType in ServiceTypes) 16 | { 17 | if (serviceType.Type.IsGenericTypeDefinition) 18 | throw new StyletIoCRegistrationException(string.Format("A factory cannot be used to implement unbound generic type {0}", serviceType.Type.GetDescription())); 19 | EnsureTypeAgainstServiceTypes(typeof(TImplementation), assertImplementation: false); 20 | } 21 | this.factory = factory; 22 | } 23 | 24 | public override void Build(Container container) 25 | { 26 | var creator = new FactoryCreator(factory, container); 27 | var registration = CreateRegistration(container, creator); 28 | 29 | foreach (var serviceType in ServiceTypes) 30 | { 31 | container.AddRegistration(new TypeKey(serviceType.Type.TypeHandle, serviceType.Key), registration); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderInstanceBinding.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Stylet.Avalonia.StyletIoC.Creation; 3 | using Stylet.Avalonia.StyletIoC.Internal.Registrations; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 6 | 7 | internal class BuilderInstanceBinding : BuilderBindingBase, IWithKeyOrAsWeakBindingOrDisposeWithContainer 8 | { 9 | private readonly object instance; 10 | private bool disposeWithContainer = true; 11 | 12 | public BuilderInstanceBinding(List serviceTypes, object instance) 13 | : base(serviceTypes) 14 | { 15 | EnsureTypeAgainstServiceTypes(instance.GetType(), assertImplementation: false); 16 | this.instance = instance; 17 | } 18 | 19 | public override void Build(Container container) 20 | { 21 | var registration = new InstanceRegistration(container, instance, disposeWithContainer); 22 | 23 | foreach (var serviceType in ServiceTypes) 24 | { 25 | container.AddRegistration(new TypeKey(serviceType.Type.TypeHandle, serviceType.Key), registration); 26 | } 27 | } 28 | 29 | public IWithKeyOrAsWeakBinding DisposeWithContainer(bool disposeWithContainer) 30 | { 31 | this.disposeWithContainer = disposeWithContainer; 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderToAllImplementationsBinding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Stylet.Avalonia.StyletIoC.Creation; 7 | 8 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 9 | 10 | internal class BuilderToAllImplementationsBinding : BuilderBindingBase 11 | { 12 | private readonly IEnumerable assemblies; 13 | private readonly bool allowZeroImplementations; 14 | 15 | private BuilderTypeKey ServiceType { get { return ServiceTypes[0]; } } 16 | 17 | public BuilderToAllImplementationsBinding(List serviceTypes, IEnumerable assemblies, bool allowZeroImplementations) 18 | : base(serviceTypes) 19 | { 20 | // This should be ensured by the fluent interfaces 21 | Trace.Assert(ServiceTypes.Count == 1); 22 | 23 | this.assemblies = assemblies; 24 | this.allowZeroImplementations = allowZeroImplementations; 25 | } 26 | 27 | public override void Build(Container container) 28 | { 29 | var candidates = (from type in assemblies.Distinct().SelectMany(x => x.GetTypes()) 30 | let baseType = type.GetBaseTypesAndInterfaces().FirstOrDefault(x => x == ServiceType.Type || x.IsGenericType && x.GetGenericTypeDefinition() == ServiceType.Type) 31 | where baseType != null 32 | select new { Type = type, Base = baseType.ContainsGenericParameters ? baseType.GetGenericTypeDefinition() : baseType }).ToList(); 33 | 34 | if (!allowZeroImplementations && candidates.Count == 0) 35 | { 36 | throw new StyletIoCRegistrationException(string.Format("Did not find any implementations of the type {0}", ServiceType.Type)); 37 | } 38 | 39 | foreach (var candidate in candidates) 40 | { 41 | try 42 | { 43 | EnsureType(candidate.Type, candidate.Base); 44 | BindImplementationToSpecificService(container, candidate.Type, candidate.Base, ServiceType.Key); 45 | } 46 | catch (StyletIoCRegistrationException e) 47 | { 48 | Debug.WriteLine(string.Format("Unable to auto-bind type {0} to {1}: {2}", candidate.Base.Name, candidate.Type.GetDescription(), e.Message), "StyletIoC"); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Builders/BuilderTypeBinding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Stylet.Avalonia.StyletIoC.Creation; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Builders; 6 | 7 | internal class BuilderTypeBinding : BuilderBindingBase 8 | { 9 | private readonly Type implementationType; 10 | 11 | public BuilderTypeBinding(List serviceTypes, Type implementationType) 12 | : base(serviceTypes) 13 | { 14 | EnsureTypeAgainstServiceTypes(implementationType); 15 | this.implementationType = implementationType; 16 | } 17 | 18 | public override void Build(Container container) 19 | { 20 | BindImplementationToServices(container, implementationType); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Creators/AbstractFactoryCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Stylet.Avalonia.StyletIoC.Creation; 4 | using System.Diagnostics; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal.Creators; 7 | 8 | /// 9 | /// Knows how to create an instance of an abstract factory (generated by Container.GetFactoryForType) 10 | /// 11 | internal class AbstractFactoryCreator : ICreator 12 | { 13 | private readonly Type abstractFactoryType; 14 | public RuntimeTypeHandle TypeHandle 15 | { 16 | get { return abstractFactoryType.TypeHandle; } 17 | } 18 | 19 | public AbstractFactoryCreator(Type abstractFactoryType) 20 | { 21 | this.abstractFactoryType = abstractFactoryType; 22 | } 23 | 24 | public Expression GetInstanceExpression(ParameterExpression registrationContext) 25 | { 26 | var ctor = abstractFactoryType.GetConstructor(new[] { typeof(IRegistrationContext) }); 27 | Debug.Assert(ctor != null); 28 | var construction = Expression.New(ctor, registrationContext); 29 | return construction; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Creators/CreatorBase.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using StyletIoC; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq.Expressions; 6 | 7 | namespace Stylet.Avalonia.StyletIoC.Internal.Creators; 8 | 9 | /// 10 | /// Base class for all ICreators (which want to use it). Provides convenience 11 | /// 12 | internal abstract class CreatorBase : ICreator 13 | { 14 | public virtual RuntimeTypeHandle TypeHandle { get; protected set; } 15 | protected IRegistrationContext ParentContext { get; set; } 16 | 17 | protected CreatorBase(IRegistrationContext parentContext) 18 | { 19 | ParentContext = parentContext; 20 | } 21 | 22 | // Common utility method 23 | protected Expression CompleteExpressionFromCreator(Expression creator, ParameterExpression registrationContext) 24 | { 25 | var type = Type.GetTypeFromHandle(TypeHandle); 26 | 27 | var instanceVar = Expression.Variable(type, "instance"); 28 | var assignment = Expression.Assign(instanceVar, creator); 29 | 30 | var buildUpExpression = ParentContext.GetBuilderUpper(type).GetExpression(instanceVar, registrationContext); 31 | 32 | // We always start with: 33 | // var instance = new Class(.....) 34 | // instance.Property1 = new .... 35 | // instance.Property2 = new .... 36 | var blockItems = new List() { assignment, buildUpExpression }; 37 | // If it implements IInjectionAware, follow that up with: 38 | // instance.ParametersInjected() 39 | if (typeof(IInjectionAware).IsAssignableFrom(type)) 40 | blockItems.Add(Expression.Call(instanceVar, typeof(IInjectionAware).GetMethod("ParametersInjected"))); 41 | // Final appearance of instanceVar, as this sets the return value of the block 42 | blockItems.Add(instanceVar); 43 | var completeExpression = Expression.Block(new[] { instanceVar }, blockItems); 44 | return completeExpression; 45 | } 46 | 47 | public abstract Expression GetInstanceExpression(ParameterExpression registrationContext); 48 | } 49 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Creators/FactoryCreator.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Creators; 6 | 7 | /// 8 | /// Knows how to create an instance of a type, by using a Func{IRegistration, T} passed by the user during building 9 | /// 10 | /// Type of object created by this factory 11 | internal class FactoryCreator : CreatorBase 12 | { 13 | private readonly Func factory; 14 | 15 | public override RuntimeTypeHandle TypeHandle { get { return typeof(T).TypeHandle; } } 16 | 17 | public FactoryCreator(Func factory, IRegistrationContext parentContext) 18 | : base(parentContext) 19 | { 20 | this.factory = factory; 21 | } 22 | 23 | public override Expression GetInstanceExpression(ParameterExpression registrationContext) 24 | { 25 | // Unfortunately we can't cache the result of this, as it relies on registrationContext 26 | var expr = (Expression>)(ctx => factory(ctx)); 27 | var invoked = Expression.Invoke(expr, registrationContext); 28 | 29 | var completeExpression = CompleteExpressionFromCreator(invoked, registrationContext); 30 | return completeExpression; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/IRegistrationCollection.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System.Collections.Generic; 3 | 4 | namespace Stylet.Avalonia.StyletIoC.Internal; 5 | 6 | internal interface IRegistrationCollection : IReadOnlyRegistrationCollection 7 | { 8 | IRegistrationCollection AddRegistration(IRegistration registration); 9 | } 10 | 11 | internal interface IReadOnlyRegistrationCollection 12 | { 13 | IRegistration GetSingle(); 14 | List GetAll(); 15 | } 16 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/RegistrationCollections/EmptyRegistrationCollection.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using StyletIoC; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal.RegistrationCollections; 7 | 8 | internal class EmptyRegistrationCollection : IReadOnlyRegistrationCollection 9 | { 10 | private readonly Type type; 11 | 12 | public EmptyRegistrationCollection(Type type) 13 | { 14 | this.type = type; 15 | } 16 | 17 | public IRegistration GetSingle() 18 | { 19 | throw new StyletIoCRegistrationException(string.Format("No registrations found for service {0}.", type.GetDescription())); 20 | } 21 | 22 | public List GetAll() 23 | { 24 | return new List(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/RegistrationCollections/RegistrationCollection.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using StyletIoC; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | 7 | namespace Stylet.Avalonia.StyletIoC.Internal.RegistrationCollections; 8 | 9 | internal class RegistrationCollection : IRegistrationCollection 10 | { 11 | private readonly object registrationsLock = new object(); 12 | private readonly List registrations; 13 | 14 | public RegistrationCollection(List registrations) 15 | { 16 | this.registrations = registrations; 17 | } 18 | 19 | public IRegistration GetSingle() 20 | { 21 | throw new StyletIoCRegistrationException("Multiple registrations found."); 22 | } 23 | 24 | public List GetAll() 25 | { 26 | List registrationsCopy; 27 | lock (registrationsLock) { registrationsCopy = registrations.ToList(); } 28 | return registrationsCopy; 29 | } 30 | 31 | public IRegistrationCollection AddRegistration(IRegistration registration) 32 | { 33 | // Need to lock the list, as someone might be fetching from it while we do this 34 | lock (registrationsLock) 35 | { 36 | // Should have been caught by SingleRegistration.AddRegistration 37 | Debug.Assert(!registrations.Any(x => x.TypeHandle.Equals(registration.TypeHandle))); 38 | registrations.Add(registration); 39 | return this; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/RegistrationCollections/SingleRegistration.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using StyletIoC; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal.RegistrationCollections; 7 | 8 | internal class SingleRegistration : IRegistrationCollection 9 | { 10 | private readonly IRegistration registration; 11 | 12 | public SingleRegistration(IRegistration registration) 13 | { 14 | this.registration = registration; 15 | } 16 | 17 | public IRegistration GetSingle() 18 | { 19 | return registration; 20 | } 21 | 22 | public List GetAll() 23 | { 24 | return new List() { registration }; 25 | } 26 | 27 | public IRegistrationCollection AddRegistration(IRegistration registration) 28 | { 29 | if (this.registration.TypeHandle.Equals(registration.TypeHandle)) 30 | throw new StyletIoCRegistrationException(string.Format("Multiple registrations for type {0} found.", Type.GetTypeFromHandle(registration.TypeHandle).GetDescription())); 31 | return new RegistrationCollection(new List() { this.registration, registration }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/FuncRegistration.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 6 | 7 | /// 8 | /// Knows how to create a Func{T}, using a given IRegistration 9 | /// 10 | // We're only created when we're needed, so no point in trying to be lazy 11 | internal class FuncRegistration : IRegistration 12 | { 13 | private readonly RuntimeTypeHandle funcType; 14 | private readonly Func generator; 15 | private readonly IRegistration delegateRegistration; 16 | 17 | public RuntimeTypeHandle TypeHandle 18 | { 19 | get { return funcType; } 20 | } 21 | 22 | public FuncRegistration(IRegistration delegateRegistration) 23 | { 24 | this.delegateRegistration = delegateRegistration; 25 | funcType = Expression.GetFuncType(Type.GetTypeFromHandle(delegateRegistration.TypeHandle)).TypeHandle; 26 | 27 | var registrationContext = Expression.Parameter(typeof(IRegistrationContext), "registrationContext"); 28 | generator = Expression.Lambda>(GetInstanceExpression(registrationContext), registrationContext).Compile(); 29 | } 30 | 31 | public Func GetGenerator() 32 | { 33 | return generator; 34 | } 35 | 36 | public Expression GetInstanceExpression(ParameterExpression registrationContext) 37 | { 38 | return Expression.Lambda(delegateRegistration.GetInstanceExpression(registrationContext)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/GetAllRegistration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Threading; 5 | using Stylet.Avalonia.StyletIoC.Creation; 6 | using System.Diagnostics; 7 | 8 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 9 | 10 | /// 11 | /// Knows how to generate an IEnumerable{T}, which contains all implementations of T 12 | /// 13 | internal class GetAllRegistration : IRegistration 14 | { 15 | private readonly IRegistrationContext parentContext; 16 | 17 | public string Key { get; private set; } 18 | private readonly RuntimeTypeHandle _type; 19 | public RuntimeTypeHandle TypeHandle 20 | { 21 | get { return _type; } 22 | } 23 | 24 | private Expression expression; 25 | private readonly object generatorLock = new object(); 26 | private Func generator; 27 | 28 | public GetAllRegistration(RuntimeTypeHandle typeHandle, IRegistrationContext parentContext, string key) 29 | { 30 | Key = key; 31 | _type = typeHandle; 32 | this.parentContext = parentContext; 33 | } 34 | 35 | public Func GetGenerator() 36 | { 37 | if (generator != null) 38 | return generator; 39 | 40 | lock (generatorLock) 41 | { 42 | if (generator == null) 43 | { 44 | var registrationContext = Expression.Parameter(typeof(IRegistrationContext), "registrationContext"); 45 | generator = Expression.Lambda>(GetInstanceExpression(registrationContext), registrationContext).Compile(); 46 | } 47 | return generator; 48 | } 49 | } 50 | 51 | public Expression GetInstanceExpression(ParameterExpression registrationContext) 52 | { 53 | if (expression != null) 54 | return expression; 55 | 56 | var type = Type.GetTypeFromHandle(TypeHandle); 57 | 58 | var instanceExpressions = parentContext.GetAllRegistrations(type.GenericTypeArguments[0], Key, false).Select(x => x.GetInstanceExpression(registrationContext)).ToArray(); 59 | var listCtor = type.GetConstructor(new[] { typeof(int) }); // ctor which takes capacity 60 | Debug.Assert(listCtor != null); 61 | var listNew = Expression.New(listCtor, Expression.Constant(instanceExpressions.Length)); 62 | Expression list = instanceExpressions.Any() ? Expression.ListInit(listNew, instanceExpressions) : listNew; 63 | 64 | if (StyletIoCContainer.CacheGeneratedExpressions) 65 | { 66 | Interlocked.CompareExchange(ref expression, list, null); 67 | return expression; 68 | } 69 | else 70 | { 71 | return list; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/InstanceRegistration.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 6 | 7 | internal class InstanceRegistration : IRegistration 8 | { 9 | public RuntimeTypeHandle TypeHandle { get; private set; } 10 | private readonly object instance; 11 | private readonly Expression instanceExpression; 12 | 13 | public InstanceRegistration(IRegistrationContext parentContext, object instance, bool disposeWithContainer) 14 | { 15 | var type = instance.GetType(); 16 | TypeHandle = type.TypeHandle; 17 | this.instance = instance; 18 | instanceExpression = Expression.Constant(instance, type); 19 | 20 | if (disposeWithContainer) 21 | { 22 | parentContext.Disposing += (o, e) => 23 | { 24 | var disposable = this.instance as IDisposable; 25 | if (disposable != null) 26 | disposable.Dispose(); 27 | }; 28 | } 29 | } 30 | 31 | public Expression GetInstanceExpression(ParameterExpression registrationContext) 32 | { 33 | return instanceExpression; 34 | } 35 | 36 | public Func GetGenerator() 37 | { 38 | return x => instance; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/RegistrationBase.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 6 | 7 | /// 8 | /// Convenience base class for all IRegistrations which want it 9 | /// 10 | internal abstract class RegistrationBase : IRegistration 11 | { 12 | protected readonly ICreator Creator; 13 | public RuntimeTypeHandle TypeHandle { get { return Creator.TypeHandle; } } 14 | 15 | protected readonly object lockObject = new object(); 16 | protected Func generator; 17 | 18 | protected RegistrationBase(ICreator creator) 19 | { 20 | Creator = creator; 21 | } 22 | 23 | public virtual Func GetGenerator() 24 | { 25 | if (generator != null) 26 | return generator; 27 | 28 | lock (lockObject) 29 | { 30 | if (generator == null) 31 | generator = GetGeneratorInternal(); 32 | return generator; 33 | } 34 | } 35 | 36 | protected virtual Func GetGeneratorInternal() 37 | { 38 | var registrationContext = Expression.Parameter(typeof(IRegistrationContext), "registrationContext"); 39 | return Expression.Lambda>(GetInstanceExpression(registrationContext), registrationContext).Compile(); 40 | } 41 | 42 | public abstract Expression GetInstanceExpression(ParameterExpression registrationContext); 43 | } 44 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/SingletonRegistration.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Threading; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 7 | 8 | /// 9 | /// Registration which generates a single instance, and returns that instance thereafter 10 | /// 11 | internal class SingletonRegistration : RegistrationBase 12 | { 13 | private readonly IRegistrationContext parentContext; 14 | private object instance; 15 | 16 | public SingletonRegistration(IRegistrationContext parentContext, ICreator creator) 17 | : base(creator) 18 | { 19 | this.parentContext = parentContext; 20 | this.parentContext.Disposing += (o, e) => 21 | { 22 | IDisposable disposable; 23 | lock (lockObject) 24 | { 25 | disposable = instance as IDisposable; 26 | instance = null; 27 | generator = null; 28 | } 29 | if (disposable != null) 30 | disposable.Dispose(); 31 | }; 32 | } 33 | 34 | public override Expression GetInstanceExpression(ParameterExpression registrationContext) 35 | { 36 | if (instance == null) 37 | { 38 | lock (lockObject) 39 | { 40 | if (instance == null) 41 | instance = Expression.Lambda>(Creator.GetInstanceExpression(registrationContext), registrationContext).Compile()(parentContext); 42 | } 43 | } 44 | 45 | // This expression yields the actual type of instance, not 'object' 46 | var instanceExpression = Expression.Constant(instance); 47 | return instanceExpression; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/Registrations/TransientRegistration.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using System.Linq.Expressions; 3 | 4 | namespace Stylet.Avalonia.StyletIoC.Internal.Registrations; 5 | 6 | /// 7 | /// Registration which generates a new instance each time one is requested 8 | /// 9 | internal class TransientRegistration : RegistrationBase 10 | { 11 | public TransientRegistration(ICreator creator) : base(creator) { } 12 | 13 | public override Expression GetInstanceExpression(ParameterExpression registrationContext) 14 | { 15 | return Creator.GetInstanceExpression(registrationContext); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal; 7 | 8 | /// 9 | /// Useful extension methods on Type 10 | /// 11 | public static class TypeExtensions 12 | { 13 | /// 14 | /// Return all base types and interfaces implemented by the given type (and its ancestors) 15 | /// 16 | /// Type to return base types and interfaces for 17 | /// Base types and interfaces implemented by the given type 18 | public static IEnumerable GetBaseTypesAndInterfaces(this Type type) 19 | { 20 | return type.GetInterfaces().Concat(type.GetBaseTypes()); 21 | } 22 | 23 | /// 24 | /// Return all base types implemented by the given type (and their base types, etc) 25 | /// 26 | /// Type to interrogate 27 | /// Base types implemented by the given type 28 | public static IEnumerable GetBaseTypes(this Type type) 29 | { 30 | for (var baseType = type.BaseType; baseType != null; baseType = baseType.BaseType) 31 | { 32 | yield return baseType; 33 | } 34 | } 35 | 36 | /// 37 | /// Determine if any of the type's base types or interfaces is equal to the given service type. Also checks generic types 38 | /// 39 | /// 40 | /// For example, given I1{T} and C1{T} : I1{T}, typeof(C1{int}).Implemements(typeof(I1{}) returns true. 41 | /// 42 | /// Implementation type 43 | /// Service type 44 | /// Whether the implementation type implements the service type 45 | public static bool Implements(this Type implementationType, Type serviceType) 46 | { 47 | return serviceType.IsAssignableFrom(implementationType) || 48 | implementationType.IsGenericType && serviceType.IsGenericTypeDefinition && serviceType.IsAssignableFrom(implementationType.GetGenericTypeDefinition()) || 49 | implementationType.GetBaseTypesAndInterfaces().Any(x => x == serviceType || x.IsGenericType && x.GetGenericTypeDefinition() == serviceType); 50 | } 51 | 52 | private static readonly Dictionary primitiveNameMapping = new Dictionary() 53 | { 54 | { typeof(byte), "byte" }, 55 | { typeof(sbyte), "sbyte" }, 56 | { typeof(char), "char" }, 57 | { typeof(short), "short" }, 58 | { typeof(ushort), "ushort" }, 59 | { typeof(int), "int" }, 60 | { typeof(uint), "uint" }, 61 | { typeof(long), "long" }, 62 | { typeof(ulong), "ulong" }, 63 | { typeof(float), "float" }, 64 | { typeof(double), "double" }, 65 | { typeof(decimal), "decimal" }, 66 | { typeof(bool), "bool" }, 67 | { typeof(string), "string" }, 68 | }; 69 | 70 | /// 71 | /// Return a human-readable description of the given type 72 | /// 73 | /// 74 | /// This returns things like 'List{int}' instead of 'List`1[System.Int32]' 75 | /// 76 | /// Type to generate the description for 77 | /// Description of the given type 78 | public static string GetDescription(this Type type) 79 | { 80 | if (type.IsGenericTypeDefinition) 81 | return string.Format("{0}<{1}>", type.Name.Split('`')[0], string.Join(", ", type.GetTypeInfo().GenericTypeParameters.Select(x => x.Name))); 82 | var genericArguments = type.GetGenericArguments(); 83 | 84 | string name; 85 | if (genericArguments.Length > 0) 86 | { 87 | var genericArgumentNames = genericArguments.Select(x => primitiveNameMapping.TryGetValue(x, out name) ? name : x.Name); 88 | return string.Format("{0}<{1}>", type.Name.Split('`')[0], string.Join(", ", genericArgumentNames)); 89 | } 90 | else 91 | { 92 | return primitiveNameMapping.TryGetValue(type, out name) ? name : type.Name; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/TypeKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.StyletIoC.Internal; 4 | 5 | /// 6 | /// Type + key tuple, used as a dictionary key 7 | /// 8 | internal class TypeKey : IEquatable 9 | { 10 | public readonly RuntimeTypeHandle TypeHandle; 11 | public readonly string Key; 12 | 13 | public TypeKey(RuntimeTypeHandle typeHandle, string key) 14 | { 15 | TypeHandle = typeHandle; 16 | Key = key; 17 | } 18 | 19 | public override int GetHashCode() 20 | { 21 | if (Key == null) 22 | return TypeHandle.GetHashCode(); 23 | return TypeHandle.GetHashCode() ^ Key.GetHashCode(); 24 | } 25 | 26 | public override bool Equals(object obj) 27 | { 28 | return Equals(obj as TypeKey); 29 | } 30 | 31 | public bool Equals(TypeKey other) 32 | { 33 | return other != null && TypeHandle.Equals(other.TypeHandle) && Key == other.Key; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/Internal/UnboundGeneric.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Creation; 2 | using Stylet.Avalonia.StyletIoC.Internal.Creators; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Stylet.Avalonia.StyletIoC.Internal; 7 | 8 | internal class UnboundGeneric 9 | { 10 | private readonly Type serviceType; 11 | private readonly IRegistrationContext parentContext; 12 | public Type Type { get; private set; } 13 | public RegistrationFactory RegistrationFactory { get; private set; } 14 | 15 | public UnboundGeneric(Type serviceType, Type type, IRegistrationContext parentContext, RegistrationFactory registrationFactory) 16 | { 17 | this.serviceType = serviceType; 18 | Type = type; 19 | this.parentContext = parentContext; 20 | RegistrationFactory = registrationFactory; 21 | } 22 | 23 | public IRegistration CreateRegistrationForTypeAndKey(Type boundType, string boundKey) 24 | { 25 | var serviceTypes = new List() { new BuilderTypeKey(serviceType, boundKey) }; 26 | return RegistrationFactory(parentContext, serviceTypes, new TypeCreator(boundType, parentContext)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/StyletApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Stylet.Avalonia.Primitive; 5 | 6 | namespace Stylet.Avalonia.StyletIoC; 7 | 8 | /// 9 | /// StyletApplication to be extended by any application which wants to use StyletIoC, but doesn't have a root ViewModel 10 | /// 11 | /// 12 | /// You would normally use , which lets you specify the root ViewModel 13 | /// to display. If you don't want to show a window on startup, override 14 | /// but don't call . 15 | /// 16 | public abstract class StyletApplication : StyletApplicationBase where T : class 17 | { 18 | /// 19 | /// Gets or sets the StyletApplication's IoC container. This is created after ConfigureIoC has been run. 20 | /// 21 | protected IContainer Container { get; private set; } 22 | 23 | /// 24 | /// Overridden from StyletApplicationBase, this sets up the IoC container 25 | /// 26 | protected sealed override void Configure() 27 | { 28 | var builder = new StyletIoCBuilder 29 | { 30 | Assemblies = new List { GetType().Assembly } 31 | }; 32 | 33 | // Call DefaultConfigureIoC *after* ConfigureIoIC, so that they can customize builder.Assemblies 34 | ConfigureIoC(builder); 35 | Container = builder.BuildContainer(); 36 | } 37 | 38 | /// 39 | /// Override to add your own types to the IoC container. 40 | /// 41 | /// StyletIoC builder to use to configure the container 42 | protected virtual void ConfigureIoC(IStyletIoCBuilder builder) 43 | { 44 | // Mark these as weak-bindings, so the user can replace them if they want 45 | var viewManagerConfig = new ViewManagerConfig() 46 | { 47 | ViewFactory = GetInstance, 48 | ViewAssemblies = new List{ GetType().Assembly} 49 | }; 50 | builder.Bind().ToInstance(viewManagerConfig).AsWeakBinding(); 51 | 52 | // Bind it to both IViewManager and to itself, so that people can get it with Container.Get() 53 | builder.Bind().And().To().InSingletonScope().AsWeakBinding(); 54 | 55 | builder.Bind().ToInstance(this).DisposeWithContainer(false).AsWeakBinding(); 56 | builder.Bind().To().InSingletonScope().AsWeakBinding(); 57 | builder.Bind().To().InSingletonScope().AsWeakBinding(); 58 | builder.Bind().To().AsWeakBinding(); 59 | // Stylet's assembly isn't added to the container, so add this explicitly 60 | builder.Bind().ToSelf(); 61 | builder.Autobind(GetType().Assembly); 62 | } 63 | 64 | protected override object GetInstance(Type service, string? key) 65 | { 66 | return Container.Get(service); 67 | } 68 | 69 | protected override IEnumerable GetInstances(Type service) 70 | { 71 | return Container.GetAll(service); 72 | } 73 | 74 | /// 75 | /// Given a type, use the IoC container to fetch an instance of it 76 | /// 77 | /// Type to fetch 78 | /// Fetched instance 79 | protected override object GetInstance(Type type) 80 | { 81 | return Container.Get(type); 82 | } 83 | 84 | /// 85 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 86 | /// 87 | public override void Dispose() 88 | { 89 | base.Dispose(); 90 | 91 | // Dispose the container last 92 | if (Container != null) 93 | Container.Dispose(); 94 | } 95 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/StyletIoCContainer.cs: -------------------------------------------------------------------------------- 1 | namespace Stylet.Avalonia.StyletIoC; 2 | 3 | /// 4 | /// Lightweight, very fast IoC container 5 | /// 6 | public class StyletIoCContainer 7 | { 8 | /// 9 | /// Name of the assembly in which abstract factories are built. Use in [assembly: InternalsVisibleTo(StyletIoCContainer.FactoryAssemblyName)] to allow factories created by .ToAbstractFactory() to access internal types 10 | /// 11 | public static readonly string FactoryAssemblyName = "StyletIoCFactory"; 12 | 13 | /// 14 | /// Gets or sets a value indicating whether generated Expressions are cached, or resolved anew each time. 15 | /// Setting to true may speed up the compilation phase in the case where one type is required by many other types. 16 | /// However, it will cause loads of ConstructorInfos to be cached forever, and they're actually pretty expensive to hold on to. 17 | /// 18 | public static bool CacheGeneratedExpressions { get; set; } 19 | 20 | static StyletIoCContainer() 21 | { 22 | CacheGeneratedExpressions = false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/StyletIoCException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Stylet.Avalonia.StyletIoC; 4 | 5 | /// 6 | /// Base class for all exceptions describing StyletIoC-specific problems? 7 | /// 8 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")] 9 | public abstract class StyletIoCException : Exception 10 | { 11 | internal StyletIoCException(string message) : base(message) { } 12 | internal StyletIoCException(string message, Exception innerException) : base(message, innerException) { } 13 | } 14 | 15 | /// 16 | /// A problem occured with a registration process (failed to register, failed to find a registration, etc) 17 | /// 18 | public class StyletIoCRegistrationException : StyletIoCException 19 | { 20 | internal StyletIoCRegistrationException(string message) : base(message) { } 21 | internal StyletIoCRegistrationException(string message, Exception innerException) : base(message, innerException) { } 22 | } 23 | 24 | /// 25 | /// StyletIoC was unable to find a callable constructor for a type 26 | /// 27 | public class StyletIoCFindConstructorException : StyletIoCException 28 | { 29 | internal StyletIoCFindConstructorException(string message) : base(message) { } 30 | } 31 | 32 | /// 33 | /// StyletIoC was unable to create an abstract factory 34 | /// 35 | public class StyletIoCCreateFactoryException : StyletIoCException 36 | { 37 | internal StyletIoCCreateFactoryException(string message) : base(message) { } 38 | internal StyletIoCCreateFactoryException(string message, Exception innerException) : base(message, innerException) { } 39 | } 40 | -------------------------------------------------------------------------------- /Stylet.Avalonia/StyletIoC/StyletIoCModule.cs: -------------------------------------------------------------------------------- 1 | using Stylet.Avalonia.StyletIoC.Internal.Builders; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace Stylet.Avalonia.StyletIoC; 7 | 8 | /// 9 | /// Module which contains its own bindings, and can be added to a builder 10 | /// 11 | public abstract class StyletIoCModule 12 | { 13 | private StyletIoCBuilder builder; 14 | private Func, string, IEnumerable> getAssemblies; 15 | 16 | /// 17 | /// Bind the specified service (interface, abstract class, concrete class, unbound generic, etc) to something 18 | /// 19 | /// Service to bind 20 | /// Fluent interface to continue configuration 21 | protected IBindTo Bind(Type serviceType) 22 | { 23 | if (builder == null || getAssemblies == null) 24 | throw new InvalidOperationException("Bind should only be called from inside Load, and you must not call Load yourself"); 25 | 26 | var builderBindTo = new BuilderBindTo(serviceType, getAssemblies); 27 | builder.AddBinding(builderBindTo); 28 | return builderBindTo; 29 | } 30 | 31 | /// 32 | /// Bind the specified service (interface, abstract class, concrete class, unbound generic, etc) to something 33 | /// 34 | /// Service to bind 35 | /// Fluent interface to continue configuration 36 | protected IBindTo Bind() 37 | { 38 | return Bind(typeof(TService)); 39 | } 40 | 41 | /// 42 | /// Override, and use 'Bind' to add bindings to the module 43 | /// 44 | protected abstract void Load(); 45 | 46 | internal void AddToBuilder(StyletIoCBuilder builder, Func, string, IEnumerable> getAssemblies) 47 | { 48 | this.builder = builder; 49 | this.getAssemblies = getAssemblies; 50 | 51 | Load(); 52 | 53 | this.builder = null; 54 | this.getAssemblies = null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/ApplicationLoader.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace Stylet.Avalonia.Xaml; 4 | 5 | /// 6 | /// Added to your App.xaml, this is responsible for loading the Boostrapper you specify, and Stylet's other resources 7 | /// 8 | public class ApplicationLoader : ResourceDictionary 9 | { 10 | //private readonly Style styletResourceDictionary; 11 | 12 | ///// 13 | ///// Initialises a new instance of the class 14 | ///// 15 | //public ApplicationLoader() 16 | //{ 17 | // this.styletResourceDictionary = new Style(); // { Source = new Uri("pack://application:,,,/Stylet;component/Xaml/StyletResourceDictionary.xaml", UriKind.Absolute) }; 18 | 19 | // this.styletResourceDictionary.Add(new StyleInclude(new Uri("avares://Avalonia.Stylet/Xaml/StyletResourceDictionary.axaml", UriKind.Absolute))); 20 | 21 | // // this.styletResourceDictionary.MergedDictionaries.Add(new ResourceInclude() 22 | // // { 23 | // // Source = new Uri("avares://Avalonia.Stylet/Xaml/StyletResourceDictionary.axaml", UriKind.Absolute) 24 | // // }); 25 | 26 | // this.LoadStyletResources = true; 27 | //} 28 | 29 | //private IStyletApplication _bootstrapper; 30 | 31 | ///// 32 | ///// Gets or sets the bootstrapper instance to use to start your application. This must be set. 33 | ///// 34 | //public IStyletApplication StyletApplication 35 | //{ 36 | // get { return this._bootstrapper; } 37 | // set 38 | // { 39 | // this._bootstrapper = value; 40 | // this._bootstrapper.Setup(Application.Current); 41 | // } 42 | //} 43 | 44 | //private bool _loadStyletResources; 45 | 46 | ///// 47 | ///// Gets or sets a value indicating whether to load Stylet's own resources (e.g. StyletConductorTabControl). Defaults to true. 48 | ///// 49 | //public bool LoadStyletResources 50 | //{ 51 | // get { return this._loadStyletResources; } 52 | // set 53 | // { 54 | // this._loadStyletResources = value; 55 | // if (this._loadStyletResources) 56 | // this.MergedDictionaries.Add(this.styletResourceDictionary); 57 | // else 58 | // this.MergedDictionaries.Remove(this.styletResourceDictionary); 59 | // } 60 | //} 61 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/DebugConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Diagnostics.CodeAnalysis; 4 | using Avalonia; 5 | using Avalonia.Data.Converters; 6 | 7 | namespace Stylet.Avalonia.Xaml; 8 | 9 | /// 10 | /// Converter which passes through values, but uses Debug.WriteLine to log them. Useful for debugging 11 | /// 12 | [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Don't agree with prefixing static method calls with the class name")] 13 | public class DebugConverter : AvaloniaObject, IValueConverter 14 | { 15 | /// 16 | /// Singleton instance of this DebugConverter. Usage e.g. Converter={x:Static s:DebugConverter.Instance}" 17 | /// 18 | public static readonly DebugConverter Instance; 19 | 20 | /// 21 | /// Gets or sets the category to use with Debug.WriteLine 22 | /// 23 | public string Name 24 | { 25 | get { return (string)GetValue(NameProperty); } 26 | set { SetValue(NameProperty, value); } 27 | } 28 | 29 | /// 30 | /// Property specifying the category to use with Debug.WriteLine 31 | /// 32 | public static readonly AvaloniaProperty NameProperty = 33 | AvaloniaProperty.Register("Name", "DebugConverter"); 34 | 35 | /// 36 | /// Gets or sets the Logger to use. Defaults to Debug.WriteLine. Arguments are: Message, Name 37 | /// 38 | public Action Logger 39 | { 40 | get { return (Action)GetValue(LoggerProperty); } 41 | set { SetValue(LoggerProperty, value); } 42 | } 43 | 44 | /// 45 | /// Property specifying an action, which when called will log an entry. 46 | /// 47 | public static readonly AvaloniaProperty LoggerProperty = 48 | AvaloniaProperty.Register>("Logger", null); 49 | 50 | static DebugConverter() 51 | { 52 | // Have to set this from within a static constructor, as it's run after the field initializers 53 | // Otherwise it gets called before the DependencyProperties have been created, and that causes the (normal) constructor to fall over 54 | Instance = new DebugConverter(); 55 | } 56 | 57 | /// 58 | /// Initialises a new instance of the class 59 | /// 60 | public DebugConverter() 61 | { 62 | if (Logger == null) 63 | Logger = (msg, name) => Debug.WriteLine(msg, name); 64 | } 65 | 66 | /// 67 | /// Perform the conversion 68 | /// 69 | /// value as produced by source binding 70 | /// target type 71 | /// converter parameter 72 | /// culture information 73 | /// Converted value 74 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 75 | { 76 | if (parameter == null) 77 | Logger(string.Format(culture, "Convert: Value = '{0}' TargetType = '{1}'", value, targetType), Name); 78 | else 79 | Logger(string.Format(culture, "Convert: Value = '{0}' TargetType = '{1}' Parameter = '{2}'", value, targetType, parameter), Name); 80 | 81 | return value; 82 | } 83 | 84 | /// 85 | /// Perform the reverse conversion 86 | /// 87 | /// value, as produced by target 88 | /// target type 89 | /// converter parameter 90 | /// culture information 91 | /// Converted back value 92 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 93 | { 94 | if (parameter == null) 95 | Logger(string.Format(culture, "ConvertBack: Value = '{0}' TargetType = '{1}'", value, targetType), Name); 96 | else 97 | Logger(string.Format(culture, "ConvertBack: Value = '{0}' TargetType = '{1}' Parameter = '{2}'", value, targetType, parameter), Name); 98 | 99 | return value; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/EqualityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Globalization; 5 | using System.Linq; 6 | using Avalonia; 7 | using Avalonia.Data.Converters; 8 | 9 | namespace Stylet.Avalonia.Xaml; 10 | 11 | /// 12 | /// Converter to compare a number of values, and return true (or false if Invert is true) if they are all equal 13 | /// 14 | [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Don't agree with prefixing static method calls with the class name")] 15 | public class EqualityConverter : AvaloniaObject, IMultiValueConverter 16 | { 17 | /// 18 | /// Singleton instance of this converter. Usage: Converter="{x:Static s:EqualityConverter.Instance}" 19 | /// 20 | public static readonly EqualityConverter Instance = new EqualityConverter(); 21 | 22 | /// 23 | /// Gets or sets a value indicating whether to return false, instead of true, if call values are equal 24 | /// 25 | public bool Invert 26 | { 27 | get { return (bool)GetValue(InvertProperty); } 28 | set { SetValue(InvertProperty, value); } 29 | } 30 | 31 | /// 32 | /// Property specifying whether the output should be inverted 33 | /// 34 | public static readonly AvaloniaProperty InvertProperty = 35 | AvaloniaProperty.Register("Invert", false); 36 | 37 | /// 38 | /// Perform the conversion 39 | /// 40 | /// 41 | /// Array of values, as produced by source bindings. 42 | /// System.Windows.DependencyProperty.UnsetValue may be passed to indicate that 43 | /// the source binding has no value to provide for conversion. 44 | /// 45 | /// target type 46 | /// converter parameter 47 | /// culture information 48 | /// Converted values 49 | public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) 50 | { 51 | if (values == null || values.Count == 0) 52 | return null; 53 | var first = values.FirstOrDefault(); 54 | var result = values.Skip(1).All(x => x.Equals(first)); 55 | return Invert ? !result : result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/IconToBitmapSourceConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using Avalonia.Data.Converters; 8 | using Avalonia.Media.Imaging; 9 | using Avalonia.Platform; 10 | using Stylet.Avalonia.Logging; 11 | 12 | namespace Stylet.Avalonia.Xaml; 13 | 14 | /// 15 | /// Converter to take an Icon, and convert it to a BitmapSource 16 | /// 17 | [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", 18 | Justification = "Don't agree with prefixing static method calls with the class name")] 19 | public class IconToBitmapSourceConverter : IValueConverter 20 | { 21 | private static readonly ILogger logger = LogManager.GetLogger(typeof(IconToBitmapSourceConverter)); 22 | 23 | /// 24 | /// Singleton instance of this converter. Usage e.g. Converter="{x:Static s:IconToBitmapSourceConverter.Instance}" 25 | /// 26 | public static readonly IconToBitmapSourceConverter Instance = new IconToBitmapSourceConverter(); 27 | 28 | /// 29 | /// Converts a value 30 | /// 31 | /// value as produced by source binding 32 | /// target type 33 | /// converter parameter 34 | /// culture information 35 | /// Converted value 36 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 37 | { 38 | if (value == null) 39 | return null; 40 | 41 | if (value is string rawUri && targetType.IsAssignableFrom(typeof(Bitmap))) 42 | { 43 | Uri uri; 44 | // Allow for assembly overrides 45 | 46 | if (rawUri.StartsWith("avares://")) 47 | { 48 | uri = new Uri(rawUri); 49 | } 50 | else 51 | { 52 | string assemblyName = Assembly.GetEntryAssembly().GetName().Name; 53 | uri = new Uri($"avares://{assemblyName}{rawUri}"); 54 | } 55 | 56 | //var assets = AvaloniaLocator.Current.GetService(); 57 | var asset = AssetLoader.Open(uri); 58 | return new Bitmap(asset); 59 | } 60 | throw new NotSupportedException(); 61 | } 62 | 63 | /// 64 | /// Converts a value back. Not supported. 65 | /// 66 | /// value, as produced by target 67 | /// target type 68 | /// converter parameter 69 | /// culture information 70 | /// Converted back value 71 | public object ConvertBack(object value, Type targetType, object parameter, 72 | CultureInfo culture) 73 | { 74 | throw new NotSupportedException(); 75 | } 76 | } -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/RethrowingBinding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.ExceptionServices; 3 | using Avalonia.Data; 4 | 5 | namespace Stylet.Avalonia.Xaml; 6 | 7 | /// 8 | /// subclass which rethrows exceptions encountered on setting the source 9 | /// 10 | public class RethrowingBinding : Binding 11 | { 12 | /// 13 | public RethrowingBinding() 14 | { 15 | //this.UpdateSourceExceptionFilter = this.ExceptionFilter; 16 | } 17 | 18 | /// 19 | public RethrowingBinding(string path) 20 | : base(path) 21 | { 22 | //this.UpdateSourceExceptionFilter = this.ExceptionFilter; 23 | } 24 | 25 | private object ExceptionFilter(object bindExpression, Exception exception) 26 | { 27 | var edi = ExceptionDispatchInfo.Capture(exception); 28 | Execute.OnUIThread(() => edi.Throw()); 29 | return exception; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/StyletResourceDictionary.axaml: -------------------------------------------------------------------------------- 1 | 5 | 25 | 26 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/StyletResourceDictionary.xaml: -------------------------------------------------------------------------------- 1 |  5 | 21 | 22 | 36 | -------------------------------------------------------------------------------- /Stylet.Avalonia/Xaml/StyletResourceDictionary1.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 21 | 22 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Stylet.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | HINT 4 | HINT 5 | HINT 6 | WARNING 7 | HINT 8 | HINT 9 | HINT 10 | HINT 11 | HINT 12 | UI 13 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy> 14 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy> 15 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> -------------------------------------------------------------------------------- /StyletIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealoyal2018/Stylet.Avalonia/cc5cf1bac60b944c43ede7221a522a5418459fdf/StyletIcon.png --------------------------------------------------------------------------------