feature: .net 5 and C# 9, tvos (#294)

This commit is contained in:
Glenn 2020-12-15 03:05:08 +00:00 коммит произвёл GitHub
Родитель 2800518c5b
Коммит d843512b74
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
96 изменённых файлов: 794 добавлений и 1677 удалений

Просмотреть файл

@ -29,9 +29,13 @@ csharp_new_line_between_query_expression_clauses = true
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
@ -40,10 +44,13 @@ dotnet_style_qualification_for_event = false:suggestion
# only use var when it's obvious what the variable type is
csharp_style_var_for_built_in_types = false:none
csharp_style_var_when_type_is_apparent = false:suggestion
csharp_style_var_elsewhere = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# use language keywords instead of BCL types
# Types: use keywords instead of BCL types, and permit var only when the type is clear
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:none
csharp_style_var_elsewhere = false:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
@ -51,33 +58,42 @@ dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_prefix_style.capitalization = camel_case
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
csharp_using_directive_placement = outside_namespace:suggestion
dotnet_sort_system_directives_first = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true:none
csharp_preserve_single_line_statements = false:none
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_simple_using_statement = false:none
csharp_style_prefer_switch_expression = true:suggestion
# Code quality
dotnet_style_readonly_field = true:suggestion
dotnet_code_quality_unused_parameters = non_public:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
@ -85,14 +101,23 @@ dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
csharp_prefer_simple_default_expression = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_expression_bodied_constructors = true:suggestion
csharp_style_expression_bodied_operators = true:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = true:suggestion
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
@ -103,6 +128,11 @@ csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Other features
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_pattern_local_over_anonymous_function = false:none
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
@ -152,4 +182,4 @@ indent_size = 2
[*.sh]
end_of_line = lf
[*.{cmd, bat}]
end_of_line = crlf
end_of_line = crlf

Просмотреть файл

@ -1,10 +1,7 @@
using System;
using Android;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
namespace SextantSample.Droid
@ -19,7 +16,7 @@ namespace SextantSample.Droid
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}

Просмотреть файл

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.giusepe.SextantSample" android:installLocation="auto">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" />
<application android:label="SextantSample.Android"></application>
</manifest>
</manifest>

Просмотреть файл

@ -15,7 +15,7 @@
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
@ -74,6 +74,7 @@
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0.3" />
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0.3" />
<PackageReference Include="ReactiveUI.XamForms" Version="13.*" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
@ -121,4 +122,4 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>
</Project>

Просмотреть файл

@ -9,6 +9,7 @@
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.0-preview6" />
<PackageReference Include="Citrus.Avalonia" Version="1.3.0-preview" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" />
<PackageReference Include="ReactiveUI" Version="13.*" />
</ItemGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">

Просмотреть файл

@ -1,5 +1,4 @@
using System;
using ReactiveUI;
using Sextant;
namespace SextantSample.ViewModels

Просмотреть файл

@ -3,7 +3,6 @@ using ReactiveUI;
using Sextant;
using System;
using System.Diagnostics;
using Splat;
namespace SextantSample.ViewModels
{
@ -33,9 +32,6 @@ namespace SextantSample.ViewModels
}
public void Destroy()
{
Debug.WriteLine($"Destroy: {nameof(FirstModalViewModel)}");
}
public void Destroy() => Debug.WriteLine($"Destroy: {nameof(FirstModalViewModel)}");
}
}

Просмотреть файл

@ -1,7 +1,6 @@
using System;
using System.Diagnostics;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using ReactiveUI;
using Sextant;
@ -11,13 +10,11 @@ namespace SextantSample.ViewModels
public class GreenViewModel : ViewModelBase, INavigable, IDestructible
{
public GreenViewModel(IViewStackService viewStackService)
: base(viewStackService)
{
: base(viewStackService) =>
OpenModal = ReactiveCommand
.CreateFromObservable(() =>
ViewStackService.PushModal(new FirstModalViewModel(viewStackService), string.Empty, false),
outputScheduler: RxApp.MainThreadScheduler);
}
public override string Id { get; } = string.Empty;
@ -32,9 +29,6 @@ namespace SextantSample.ViewModels
public IObservable<Unit> WhenNavigatingTo(INavigationParameter parameter) =>
Observable.Return(Unit.Default);
public void Destroy()
{
Debug.WriteLine($"Destroy: {nameof(GreenViewModel)}");
}
public void Destroy() => Debug.WriteLine($"Destroy: {nameof(GreenViewModel)}");
}
}

Просмотреть файл

@ -1,7 +1,5 @@
using System;
using System.Diagnostics;
using System.Reactive;
using System.Reactive.Linq;
using ReactiveUI;
using Sextant;
using Splat;

Просмотреть файл

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
using ReactiveUI;
namespace SextantSample.ViewModels
{
public static class Interactions
{
public static Interaction<Exception, bool> ErrorMessage = new Interaction<Exception, bool>();
public static readonly Interaction<Exception, bool> ErrorMessage = new Interaction<Exception, bool>();
}
}

Просмотреть файл

@ -1,14 +1,12 @@
using System;
using System.Diagnostics;
using System.Reactive;
using System.Reactive.Linq;
using System.Windows.Input;
using ReactiveUI;
using Sextant;
namespace SextantSample.ViewModels
{
public class RedViewModel : ViewModelBase, IViewModel
public class RedViewModel : ViewModelBase
{
public ReactiveCommand<Unit, Unit> PopModal { get; set; }

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using ReactiveUI;
using Sextant;
namespace SextantSample.ViewModels
{

Просмотреть файл

@ -3,7 +3,6 @@ using ReactiveUI;
using System;
using System.Diagnostics;
using Sextant;
using System.Reactive.Linq;
namespace SextantSample.ViewModels
{
@ -28,7 +27,7 @@ namespace SextantSample.ViewModels
outputScheduler: RxApp.MainThreadScheduler);
PushPage.Subscribe(x => Debug.WriteLine("PagePushed"));
PopModal.Subscribe(x => Debug.WriteLine("PagePoped"));
PopModal.Subscribe(x => Debug.WriteLine("PagePopped"));
PushPage.ThrownExceptions.Subscribe(error => Interactions.ErrorMessage.Handle(error).Subscribe());
PopModal.ThrownExceptions.Subscribe(error => Interactions.ErrorMessage.Handle(error).Subscribe());

Просмотреть файл

@ -14,7 +14,7 @@ namespace SextantSample.ViewModels
Debugger.Break();
}
RxApp.MainThreadScheduler.Schedule(() => { throw ex; });
RxApp.MainThreadScheduler.Schedule(() => throw ex);
}
public void OnError(Exception ex)
@ -23,14 +23,18 @@ namespace SextantSample.ViewModels
{
Debugger.Break();
}
RxApp.MainThreadScheduler.Schedule(() => { throw ex; });
RxApp.MainThreadScheduler.Schedule(() => throw ex);
}
public void OnCompleted()
{
if (Debugger.IsAttached)
{
Debugger.Break();
RxApp.MainThreadScheduler.Schedule(() => { throw new NotImplementedException(); });
}
RxApp.MainThreadScheduler.Schedule(() => throw new NotImplementedException());
}
}
}

Просмотреть файл

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReactiveUI" Version="12.*" />
<PackageReference Include="ReactiveUI" Version="13.*" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -1,5 +1,4 @@
using System;
using System.Reactive;
using ReactiveUI;
using Sextant;
@ -9,10 +8,7 @@ namespace SextantSample.ViewModels
{
protected readonly IViewStackService ViewStackService;
protected ViewModelBase(IViewStackService viewStackService)
{
ViewStackService = viewStackService;
}
protected ViewModelBase(IViewStackService viewStackService) => ViewStackService = viewStackService;
public virtual string Id { get; }
}

Просмотреть файл

@ -65,7 +65,7 @@ namespace SextantSample.UWP
//// Do not repeat app initialization when the Window already has content,
//// just ensure that the window is active
//if (rootFrame == null)
//if (rootFrame is null)
//{
// // Create a Frame to act as the navigation context and navigate to the first page
// rootFrame = new Frame();
@ -83,7 +83,7 @@ namespace SextantSample.UWP
if (e.PrelaunchActivated == false)
{
//if (rootFrame.Content == null)
//if (rootFrame.Content is null)
//{
// // When the navigation stack isn't restored navigate to the first page,
// // configuring the new page by passing required information as a navigation
@ -109,10 +109,7 @@ namespace SextantSample.UWP
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e) => throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved

Просмотреть файл

@ -12,10 +12,7 @@ namespace SextantSample.UWP.Views
/// </summary>
public sealed partial class GreenView : Page, IViewFor<GreenViewModel>
{
public GreenView()
{
this.InitializeComponent();
}
public GreenView() => this.InitializeComponent();
public static readonly DependencyProperty ViewModelProperty = DependencyProperty
.Register(nameof(ViewModel), typeof(GreenViewModel), typeof(GreenView), null);

Просмотреть файл

@ -13,10 +13,7 @@ namespace SextantSample.UWP.Views
/// </summary>
public sealed partial class HomeView : Page, ReactiveUI.IViewFor<HomeViewModel>
{
public HomeView()
{
this.InitializeComponent();
}
public HomeView() => this.InitializeComponent();
public static readonly DependencyProperty ViewModelProperty = DependencyProperty
.Register(nameof(ViewModel), typeof(HomeViewModel), typeof(HomeView), null);

Просмотреть файл

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
@ -11,7 +9,7 @@ namespace SextantSample.iOS
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
public class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
@ -22,7 +20,7 @@ namespace SextantSample.iOS
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);

Просмотреть файл

@ -1,20 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
namespace SextantSample.iOS
{
public class Application
public static class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
public static void Main(string[] args) =>
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}

Просмотреть файл

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following

Просмотреть файл

@ -12,7 +12,7 @@ using static Sextant.Sextant;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace SextantSample
{
public partial class App : Application
public partial class App
{
public App()
{

Просмотреть файл

@ -1,7 +1,7 @@
using System;
using System.Reactive.Concurrency;
using ReactiveUI;
using Sextant;
using Sextant.XamForms;
using Xamarin.Forms;
@ -12,8 +12,8 @@ namespace SextantSample.Views
public BlueNavigationView()
: base(RxApp.MainThreadScheduler, RxApp.TaskpoolScheduler, ViewLocator.Current)
{
this.BarBackgroundColor = Color.Blue;
this.BarTextColor = Color.White;
BarBackgroundColor = Color.Blue;
BarTextColor = Color.White;
}
public object ViewModel { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

Просмотреть файл

@ -1,12 +1,10 @@
using ReactiveUI;
using ReactiveUI.XamForms;
using Sextant;
using SextantSample.ViewModels;
using Xamarin.Forms;
namespace SextantSample.Views
{
public partial class FirstModalView : ReactiveContentPage<FirstModalViewModel>
public partial class FirstModalView
{
public FirstModalView()
{

Просмотреть файл

@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReactiveUI;
using ReactiveUI.XamForms;
using SextantSample.ViewModels;
using SextantSample.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

Просмотреть файл

@ -1,14 +1,12 @@
using ReactiveUI;
using ReactiveUI.XamForms;
using Sextant;
using System.Reactive.Disposables;
using ReactiveUI;
using SextantSample.ViewModels;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Xamarin.Forms;
namespace SextantSample.Views
{
public partial class HomeView : ReactiveContentPage<HomeViewModel>
public partial class HomeView
{
public HomeView()
{

Просмотреть файл

@ -4,7 +4,7 @@ using SextantSample.ViewModels;
namespace SextantSample.Views
{
public partial class RedView : ReactiveContentPage<RedViewModel>
public partial class RedView
{
public RedView()
{

Просмотреть файл

@ -1,11 +1,9 @@
using ReactiveUI;
using ReactiveUI.XamForms;
using Sextant;
using SextantSample.ViewModels;
namespace SextantSample.Views
{
public partial class SecondModalView : ReactiveContentPage<SecondModalViewModel>
public partial class SecondModalView
{
public SecondModalView()
{

Просмотреть файл

@ -21,9 +21,7 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Optional: Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<!-- disable sourcelink on mono, to workaround https://github.com/dotnet/sourcelink/issues/155 -->
<EnableSourceLink Condition=" '$(OS)' != 'Windows_NT' AND '$(MSBuildRuntimeType)' != 'Core' ">false</EnableSourceLink>
<EnableSourceControlManagerQueries>$(EnableSourceLink)</EnableSourceControlManagerQueries>
<WarningsAsErrors>CS8600;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8623;CS8624;CS8625;CS8626;CS8627;CS8628;CS8629;CS8630;CS8634;CS8766;CS8767</WarningsAsErrors>
</PropertyGroup>
<ItemGroup Condition="$(IsTestProject)">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />

Просмотреть файл

@ -15,10 +15,9 @@ namespace Sextant.Mocks
/// </summary>
public class NavigableViewModelMock : INavigable
{
private readonly string _id;
private ISubject<Unit> _navigatedTo;
private ISubject<Unit> _navigatingTo;
private ISubject<Unit> _navigatedFrom;
private readonly ISubject<Unit> _navigatedTo;
private readonly ISubject<Unit> _navigatingTo;
private readonly ISubject<Unit> _navigatedFrom;
/// <summary>
/// Initializes a new instance of the <see cref="NavigableViewModelMock"/> class.
@ -26,7 +25,7 @@ namespace Sextant.Mocks
/// <param name="id">The id of the page.</param>
public NavigableViewModelMock(string? id = null)
{
_id = id ?? string.Empty;
Id = id ?? string.Empty;
_navigatedTo = new Subject<Unit>();
_navigatedFrom = new Subject<Unit>();
_navigatingTo = new Subject<Unit>();
@ -35,7 +34,7 @@ namespace Sextant.Mocks
/// <summary>
/// Gets the ID of the page.
/// </summary>
public string Id => _id ?? nameof(NavigableViewModelMock);
public string Id { get; }
/// <inheritdoc/>
public virtual IObservable<Unit> WhenNavigatedTo(INavigationParameter parameter) =>

Просмотреть файл

@ -17,19 +17,13 @@ namespace Sextant.Mocks
/// </summary>
public class NavigationViewMock : IView, IDisposable
{
private Subject<IViewModel> _pagePoppedSubject;
private Stack<IViewModel> _pageStack;
private readonly Subject<IViewModel> _pagePoppedSubject = new();
private readonly Stack<IViewModel> _pageStack = new();
/// <summary>
/// Initializes a new instance of the <see cref="NavigationViewMock"/> class.
/// </summary>
public NavigationViewMock()
{
_pagePoppedSubject = new Subject<IViewModel>();
_pageStack = new Stack<IViewModel>();
PagePopped = _pagePoppedSubject.AsObservable();
}
public NavigationViewMock() => PagePopped = _pagePoppedSubject.AsObservable();
/// <inheritdoc/>
public IScheduler MainThreadScheduler { get; } = CurrentThreadScheduler.Instance;
@ -73,7 +67,7 @@ namespace Sextant.Mocks
{
if (disposing)
{
_pagePoppedSubject?.Dispose();
_pagePoppedSubject.Dispose();
}
}
}

Просмотреть файл

@ -15,9 +15,9 @@ namespace Sextant.Mocks
/// </summary>
public class NullViewModelMock : INavigable
{
private ISubject<Unit> _navigatedTo;
private ISubject<Unit> _navigatingTo;
private ISubject<Unit> _navigatedFrom;
private readonly ISubject<Unit> _navigatedTo;
private readonly ISubject<Unit> _navigatingTo;
private readonly ISubject<Unit> _navigatedFrom;
/// <summary>
/// Initializes a new instance of the <see cref="NullViewModelMock"/> class.

Просмотреть файл

@ -4,11 +4,9 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using ReactiveUI;
namespace Sextant.Mocks
@ -38,7 +36,7 @@ namespace Sextant.Mocks
/// <summary>
/// Gets the disposable.
/// </summary>
public CompositeDisposable Disposable { get; } = new CompositeDisposable();
public CompositeDisposable Disposable { get; } = new();
/// <inheritdoc />
public IObservable<Unit> WhenNavigatedTo(INavigationParameter parameter) => Observable.Return(Unit.Default).Do(_ => Unwrap(parameter));
@ -50,10 +48,7 @@ namespace Sextant.Mocks
public IObservable<Unit> WhenNavigatingTo(INavigationParameter parameter) => Observable.Return(Unit.Default).Do(_ => Unwrap(parameter));
/// <inheritdoc/>
public void Destroy()
{
Disposable?.Dispose();
}
public void Destroy() => Disposable.Dispose();
private void Unwrap(INavigationParameter parameter)
{

Просмотреть файл

@ -1,9 +1,8 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<NoWarn>$(NoWarn);1591;CA1707;SA1633;CA1034</NoWarn>
<WarningsAsErrors>CS8625;CS8604;CS8600;CS8614;CS8603;CS8618;CS8619</WarningsAsErrors>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461;uap10.0.16299</TargetFrameworks>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
@ -12,31 +11,4 @@
<ItemGroup>
<ProjectReference Include="..\Sextant\Sextant.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="IDestructibleMock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="IEverything.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="NavigableViewModelMock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="NavigatedMock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="NavigationViewMock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="NullViewModelMock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="PageView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="ParameterViewModel.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>

Просмотреть файл

@ -1,3 +1,4 @@
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/reactiveui/sextant")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")]
namespace Sextant.Plugins.Popup
{
@ -52,7 +53,7 @@ namespace Sextant.Plugins.Popup
public static readonly Xamarin.Forms.BindableProperty ViewModelProperty;
protected SextantPopupPage() { }
public System.IObservable<System.Reactive.Unit> BackgroundClick { get; }
public object ViewModel { get; set; }
public object? ViewModel { get; set; }
protected override void OnBindingContextChanged() { }
}
public abstract class SextantPopupPage<TViewModel> : Sextant.Plugins.Popup.SextantPopupPage, ReactiveUI.IActivatableView, ReactiveUI.IViewFor, ReactiveUI.IViewFor<TViewModel>

Просмотреть файл

@ -23,23 +23,25 @@ namespace Sextant.Plugins.Popup.Tests
[ExcludeFromCodeCoverage]
public class ApiApprovalTests
{
private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
private static readonly Regex _removeCoverletSectionRegex = new(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
/// <summary>
/// Tests to make sure the splat project is approved.
/// </summary>
[Fact]
public void SextantPluginsPopup()
{
CheckApproval(typeof(IPopupViewStackService).Assembly);
}
public void SextantPluginsPopup() => CheckApproval(typeof(IPopupViewStackService).Assembly);
private static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null)
private static void CheckApproval(Assembly assembly, [CallerMemberName]string? memberName = null, [CallerFilePath]string? filePath = null)
{
var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName();
var sourceDirectory = Path.GetDirectoryName(filePath);
if (sourceDirectory is null)
{
throw new ArgumentException("The directory name is empty for path: " + filePath);
}
var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt");
var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt");
@ -55,7 +57,7 @@ namespace Sextant.Plugins.Popup.Tests
var approvedPublicApi = File.ReadAllText(approvedFileName);
var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly, null));
var receivedPublicApi = Filter(assembly.GeneratePublicApi(null));
if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture))
{

Просмотреть файл

@ -3,10 +3,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
using ReactiveUI;
using Rg.Plugins.Popup.Pages;
using Sextant.Mocks;
namespace Sextant.Plugins.Popup.Tests
{
/// <summary>

Просмотреть файл

@ -19,27 +19,24 @@ namespace Sextant.Plugins.Popup.Tests
/// </summary>
public class PopupNavigationMock : IPopupNavigation
{
private Stack<PopupPage> _stack;
private readonly Stack<PopupPage> _stack;
/// <summary>
/// Initializes a new instance of the <see cref="PopupNavigationMock"/> class.
/// </summary>
public PopupNavigationMock()
{
_stack = new Stack<PopupPage>();
}
public PopupNavigationMock() => _stack = new Stack<PopupPage>();
/// <inheritdoc/>
public event EventHandler<PopupNavigationEventArgs> Pushing;
public event EventHandler<PopupNavigationEventArgs>? Pushing;
/// <inheritdoc/>
public event EventHandler<PopupNavigationEventArgs> Pushed;
public event EventHandler<PopupNavigationEventArgs>? Pushed;
/// <inheritdoc/>
public event EventHandler<PopupNavigationEventArgs> Popping;
public event EventHandler<PopupNavigationEventArgs>? Popping;
/// <inheritdoc/>
public event EventHandler<PopupNavigationEventArgs> Popped;
public event EventHandler<PopupNavigationEventArgs>? Popped;
/// <inheritdoc/>
public IReadOnlyList<PopupPage> PopupStack => _stack.ToList();

Просмотреть файл

@ -15,7 +15,7 @@ namespace Sextant.Plugins.Popup.Tests
{
internal class PopupViewStackServiceFixture : IBuilder
{
private IView _view;
private readonly IView _view;
private IPopupNavigation _popupNavigation;
private IViewLocator _viewLocator;
private IViewModelFactory _viewModelFactory;
@ -48,6 +48,6 @@ namespace Sextant.Plugins.Popup.Tests
this.With(ref _viewLocator, viewLocator);
private PopupViewStackService Build() =>
new PopupViewStackService(_view, _popupNavigation, _viewLocator, _viewModelFactory);
new(_view, _popupNavigation, _viewLocator, _viewModelFactory);
}
}

Просмотреть файл

@ -4,12 +4,10 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using NSubstitute;
using NSubstitute.Extensions;
using ReactiveUI;
using Rg.Plugins.Popup.Contracts;
using Rg.Plugins.Popup.Events;
@ -36,7 +34,7 @@ namespace Sextant.Plugins.Popup.Tests
public void Should_Observe_Pushing()
{
// Given
PopupNavigationEvent pushing = null;
PopupNavigationEvent? pushing = null;
var viewModel = new NavigableViewModelMock();
var popup = new PopupMock
{
@ -49,6 +47,11 @@ namespace Sextant.Plugins.Popup.Tests
// When
navigation.Pushing += Raise.EventWith(new PopupNavigationEventArgs(popup, true));
if (pushing is null)
{
throw new InvalidOperationException("pushing should not be null.");
}
// Then
pushing.ViewModel.Should().Be(viewModel);
}
@ -67,7 +70,7 @@ namespace Sextant.Plugins.Popup.Tests
public async Task Should_Observe_Pushed()
{
// Given
PopupNavigationEvent pushing = null;
PopupNavigationEvent? pushing = null;
var viewModel = new NavigableViewModelMock();
var popup = new PopupMock
{
@ -83,6 +86,11 @@ namespace Sextant.Plugins.Popup.Tests
await sut.PushPopup(viewModel);
navigation.Pushed += Raise.EventWith(new PopupNavigationEventArgs(popup, true));
if (pushing is null)
{
throw new InvalidOperationException("pushing should not be null");
}
// Then
pushing.ViewModel.Should().Be(viewModel);
}
@ -100,7 +108,7 @@ namespace Sextant.Plugins.Popup.Tests
public void Should_Observe_Pushing()
{
// Given
PopupNavigationEvent pushing = null;
PopupNavigationEvent? pushing = null;
var viewModel = new NavigableViewModelMock();
var popup = new PopupMock
{
@ -113,6 +121,11 @@ namespace Sextant.Plugins.Popup.Tests
// When
navigation.Popping += Raise.EventWith(new PopupNavigationEventArgs(popup, true));
if (pushing is null)
{
throw new InvalidOperationException("pushing should not be null");
}
// Then
pushing.ViewModel.Should().Be(viewModel);
}
@ -130,7 +143,7 @@ namespace Sextant.Plugins.Popup.Tests
public void Should_Observe_Popped()
{
// Given
PopupNavigationEvent pushing = null;
PopupNavigationEvent? pushing = null;
var viewModel = new NavigableViewModelMock();
var popup = new PopupMock
{
@ -143,6 +156,11 @@ namespace Sextant.Plugins.Popup.Tests
// When
navigation.Popped += Raise.EventWith(new PopupNavigationEventArgs(popup, true));
if (pushing is null)
{
throw new InvalidOperationException("pushing should not be null");
}
// Then
pushing.ViewModel.Should().Be(viewModel);
}
@ -154,7 +172,7 @@ namespace Sextant.Plugins.Popup.Tests
public void Should_Call_Destroy()
{
// Given
PopupNavigationEvent pushing = null;
PopupNavigationEvent? pushing = null;
var viewModel = Substitute.For<IEverything>();
var popup = new PopupMock
{

Просмотреть файл

@ -20,12 +20,12 @@ namespace Sextant.Plugins.Popup
/// <param name="isAnimated">Is the page animated.</param>
public PopupNavigationEvent(IViewFor page, bool isAnimated)
{
if (page == null)
if (page is null)
{
throw new ArgumentNullException(nameof(page));
}
if (page.ViewModel == null)
if (page.ViewModel is null)
{
throw new InvalidOperationException($"{nameof(page.ViewModel)} cannot be null.");
}

Просмотреть файл

@ -44,7 +44,7 @@ namespace Sextant.Plugins.Popup
Pushing = Observable.FromEvent<EventHandler<PopupNavigationEventArgs>, PopupNavigationEventArgs>(
eventHandler =>
{
void Handler(object sender, PopupNavigationEventArgs args) => eventHandler(args);
void Handler(object? sender, PopupNavigationEventArgs args) => eventHandler(args);
return Handler;
},
@ -55,7 +55,7 @@ namespace Sextant.Plugins.Popup
Pushed = Observable.FromEvent<EventHandler<PopupNavigationEventArgs>, PopupNavigationEventArgs>(
eventHandler =>
{
void Handler(object sender, PopupNavigationEventArgs args)
void Handler(object? sender, PopupNavigationEventArgs args)
=> eventHandler(args);
return Handler;
@ -67,7 +67,7 @@ namespace Sextant.Plugins.Popup
Popping = Observable.FromEvent<EventHandler<PopupNavigationEventArgs>, PopupNavigationEventArgs>(
eventHandler =>
{
void Handler(object sender, PopupNavigationEventArgs args)
void Handler(object? sender, PopupNavigationEventArgs args)
=> eventHandler(args);
return Handler;
@ -79,7 +79,7 @@ namespace Sextant.Plugins.Popup
Popped = Observable.FromEvent<EventHandler<PopupNavigationEventArgs>, PopupNavigationEventArgs>(
eventHandler =>
{
void Handler(object sender, PopupNavigationEventArgs args) => eventHandler(args);
void Handler(object? sender, PopupNavigationEventArgs args) => eventHandler(args);
return Handler;
},
@ -112,7 +112,7 @@ namespace Sextant.Plugins.Popup
_popupNavigation
.PopupStack
.Cast<IViewFor<IViewModel>>()
.Where(x => x != null)
.Where(x => x is not null)
.Select(x => x.ViewModel)
.ToList();
#pragma warning restore 8619
@ -125,7 +125,7 @@ namespace Sextant.Plugins.Popup
/// <inheritdoc/>
public IObservable<Unit> PushPopup(IViewModel viewModel, string? contract = null, bool animate = true)
{
if (viewModel == null)
if (viewModel is null)
{
throw new ArgumentNullException(nameof(viewModel));
}
@ -156,12 +156,12 @@ namespace Sextant.Plugins.Popup
string? contract = null,
bool animate = true)
{
if (viewModel == null)
if (viewModel is null)
{
throw new ArgumentNullException(nameof(viewModel));
}
if (navigationParameter == null)
if (navigationParameter is null)
{
throw new ArgumentNullException(nameof(navigationParameter));
}
@ -170,11 +170,11 @@ namespace Sextant.Plugins.Popup
.Start(() => LocatePopupFor(viewModel, contract), CurrentThreadScheduler.Instance)
.ObserveOn(CurrentThreadScheduler.Instance)
.Do(popup =>
popup.ViewModel.InvokeViewModelAction<INavigating>(x => x.WhenNavigatingTo(navigationParameter)))
popup.ViewModel?.InvokeViewModelAction<INavigating>(x => x.WhenNavigatingTo(navigationParameter)))
.Select(popup =>
Observable
.FromAsync(() => _popupNavigation.PushAsync(popup, animate))
.Do(_ => popup.ViewModel.InvokeViewModelAction<INavigated>(x =>
.Do(_ => popup.ViewModel?.InvokeViewModelAction<INavigated>(x =>
x.WhenNavigatedTo(navigationParameter))))
.Switch()
.Do(_ =>
@ -209,7 +209,7 @@ namespace Sextant.Plugins.Popup
/// <inheritdoc/>
public IObservable<Unit> RemovePopup(IViewModel viewModel, string? contract = null, bool animate = true)
{
if (viewModel == null)
if (viewModel is null)
{
throw new ArgumentNullException(nameof(viewModel));
}
@ -222,7 +222,7 @@ namespace Sextant.Plugins.Popup
private static void RemoveFromStackAndTick<T>(BehaviorSubject<IImmutableList<T>> stackSubject, T item)
{
if (stackSubject == null)
if (stackSubject is null)
{
throw new ArgumentNullException(nameof(stackSubject));
}
@ -237,7 +237,7 @@ namespace Sextant.Plugins.Popup
private SextantPopupPage LocatePopupFor(IViewModel viewModel, string? contract)
{
IViewFor? view = _viewLocator.ResolveView(viewModel, contract);
if (view == null)
if (view is null)
{
throw new InvalidOperationException($"No view could be located for type '{viewModel.GetType().FullName}', contract '{contract}'. Be sure Splat has an appropriate registration.");
}

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<AssemblyName>Sextant.Plugins.Popup</AssemblyName>

Просмотреть файл

@ -6,7 +6,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using ReactiveUI;
using Rg.Plugins.Popup.Pages;
@ -26,24 +25,21 @@ namespace Sextant.Plugins.Popup
/// <summary>
/// The view model property.
/// </summary>
public static readonly BindableProperty ViewModelProperty = BindableProperty.Create(
public static new readonly BindableProperty ViewModelProperty = BindableProperty.Create(
nameof(ViewModel),
typeof(TViewModel),
typeof(IViewFor<TViewModel>),
(IViewFor<TViewModel>)null,
null,
BindingMode.OneWay,
(BindableProperty.ValidateValueDelegate)null,
new BindableProperty.BindingPropertyChangedDelegate(OnViewModelChanged),
(BindableProperty.BindingPropertyChangingDelegate)null,
(BindableProperty.CoerceValueDelegate)null,
(BindableProperty.CreateDefaultValueDelegate)null);
null,
OnViewModelChanged);
/// <summary>
/// Gets or sets the ViewModel to display.
/// </summary>
public new TViewModel ViewModel
public new TViewModel? ViewModel
{
get => (TViewModel)GetValue(ViewModelProperty);
get => (TViewModel?)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
@ -51,10 +47,10 @@ namespace Sextant.Plugins.Popup
/// Gets or sets the ViewModel corresponding to this specific View.
/// This should be a BindableProperty if you're using XAML.
/// </summary>
object IViewFor.ViewModel
object? IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
set => ViewModel = (TViewModel?)value;
}
/// <inheritdoc/>
@ -64,10 +60,7 @@ namespace Sextant.Plugins.Popup
ViewModel = (BindingContext as TViewModel)!;
}
private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue)
{
bindableObject.BindingContext = newValue;
}
private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue;
}
/// <summary>
@ -82,29 +75,24 @@ namespace Sextant.Plugins.Popup
nameof(ViewModel),
typeof(object),
typeof(IViewFor<object>),
(object)null,
null,
BindingMode.OneWay,
(BindableProperty.ValidateValueDelegate)null,
new BindableProperty.BindingPropertyChangedDelegate(OnViewModelChanged),
(BindableProperty.BindingPropertyChangingDelegate)null,
(BindableProperty.CoerceValueDelegate)null,
(BindableProperty.CreateDefaultValueDelegate)null);
null,
OnViewModelChanged);
/// <summary>
/// Initializes a new instance of the <see cref="SextantPopupPage"/> class.
/// </summary>
protected SextantPopupPage()
{
protected SextantPopupPage() =>
BackgroundClick =
Observable.FromEvent<EventHandler, Unit>(
handler =>
{
void EventHandler(object sender, EventArgs args) => handler(Unit.Default);
return EventHandler;
},
x => BackgroundClicked += x,
x => BackgroundClicked -= x);
}
handler =>
{
void EventHandler(object? sender, EventArgs args) => handler(Unit.Default);
return EventHandler;
},
x => BackgroundClicked += x,
x => BackgroundClicked -= x);
/// <summary>
/// Gets the background click observable signal.
@ -115,7 +103,7 @@ namespace Sextant.Plugins.Popup
/// <summary>
/// Gets or sets the ViewModel to display.
/// </summary>
public object ViewModel
public object? ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
@ -128,9 +116,6 @@ namespace Sextant.Plugins.Popup
ViewModel = BindingContext;
}
private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue)
{
bindableObject.BindingContext = newValue;
}
private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue;
}
}

Просмотреть файл

@ -0,0 +1,187 @@
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/reactiveui/sextant")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v5.0", FrameworkDisplayName="")]
namespace Sextant
{
public class DefaultViewModelFactory : Sextant.IViewModelFactory
{
public DefaultViewModelFactory() { }
public TViewModel Create<TViewModel>(string? contract = null)
where TViewModel : Sextant.IViewModel { }
}
public static class DependencyResolverMixins
{
public static string NavigationView { get; }
public static Splat.IMutableDependencyResolver RegisterParameterViewStackService(this Splat.IMutableDependencyResolver dependencyResolver) { }
public static Splat.IMutableDependencyResolver RegisterView<TView, TViewModel>(this Splat.IMutableDependencyResolver dependencyResolver, string? contract = null)
where TView : ReactiveUI.IViewFor<TViewModel>, new ()
where TViewModel : class, Sextant.IViewModel { }
public static Splat.IMutableDependencyResolver RegisterView<TView, TViewModel>(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<ReactiveUI.IViewFor<TViewModel>> viewFactory, string? contract = null)
where TView : ReactiveUI.IViewFor
where TViewModel : class, Sextant.IViewModel { }
public static Splat.IMutableDependencyResolver RegisterViewModel<TViewModel>(this Splat.IMutableDependencyResolver dependencyResolver, string? contract = null)
where TViewModel : Sextant.IViewModel, new () { }
public static Splat.IMutableDependencyResolver RegisterViewModel<TViewModel>(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<TViewModel> viewModelFactory, string? contract = null)
where TViewModel : class, Sextant.IViewModel { }
public static Splat.IMutableDependencyResolver RegisterViewModel<TViewModel>(this Splat.IMutableDependencyResolver dependencyResolver, TViewModel viewModel, string? contract = null)
where TViewModel : class, Sextant.IViewModel { }
public static Splat.IMutableDependencyResolver RegisterViewModelFactory(this Splat.IMutableDependencyResolver dependencyResolver) { }
public static Splat.IMutableDependencyResolver RegisterViewModelFactory(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<Sextant.IViewModelFactory> factory) { }
public static Splat.IMutableDependencyResolver RegisterViewStackService(this Splat.IMutableDependencyResolver dependencyResolver) { }
[System.Obsolete("Use the Func<IView, IViewModelFactory, T> variant.")]
public static Splat.IMutableDependencyResolver RegisterViewStackService<T>(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<Sextant.IView, T> factory)
where T : Sextant.IViewStackService { }
public static Splat.IMutableDependencyResolver RegisterViewStackService<T>(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<Sextant.IView, Sextant.IViewModelFactory, T> factory)
where T : Sextant.IViewStackService { }
}
public interface IDestructible
{
void Destroy();
}
public interface INavigable : Sextant.INavigated, Sextant.INavigating, Sextant.IViewModel { }
public interface INavigated
{
System.IObservable<System.Reactive.Unit> WhenNavigatedFrom(Sextant.INavigationParameter parameter);
System.IObservable<System.Reactive.Unit> WhenNavigatedTo(Sextant.INavigationParameter parameter);
}
public interface INavigating
{
System.IObservable<System.Reactive.Unit> WhenNavigatingTo(Sextant.INavigationParameter parameter);
}
public interface INavigationParameter : System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>, System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>, System.Collections.IEnumerable { }
[System.Obsolete("Please use the IViewModel interface.")]
public interface IPageViewModel : Sextant.IViewModel { }
public interface IParameterViewStackService : Sextant.IViewStackService
{
System.IObservable<System.Reactive.Unit> PopPage(Sextant.INavigationParameter parameter, bool animate = true);
System.IObservable<System.Reactive.Unit> PushModal(Sextant.INavigable navigableModal, Sextant.INavigationParameter parameter, string? contract = null, bool withNavigationPage = true);
System.IObservable<System.Reactive.Unit> PushModal<TViewModel>(Sextant.INavigationParameter parameter, string? contract = null, bool withNavigationPage = true)
where TViewModel : Sextant.INavigable;
System.IObservable<System.Reactive.Unit> PushPage(Sextant.INavigable navigableViewModel, Sextant.INavigationParameter parameter, string? contract = null, bool resetStack = false, bool animate = true);
System.IObservable<System.Reactive.Unit> PushPage<TViewModel>(Sextant.INavigationParameter parameter, string? contract = null, bool resetStack = false, bool animate = true)
where TViewModel : Sextant.INavigable;
}
public interface IView
{
System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; }
System.IObservable<Sextant.IViewModel?> PagePopped { get; }
System.IObservable<System.Reactive.Unit> PopModal();
System.IObservable<System.Reactive.Unit> PopPage(bool animate = true);
System.IObservable<System.Reactive.Unit> PopToRootPage(bool animate = true);
System.IObservable<System.Reactive.Unit> PushModal(Sextant.IViewModel modalViewModel, string? contract, bool withNavigationPage = true);
System.IObservable<System.Reactive.Unit> PushPage(Sextant.IViewModel viewModel, string? contract, bool resetStack, bool animate = true);
}
public interface IViewModel
{
string Id { get; }
}
public interface IViewModelFactory
{
TViewModel Create<TViewModel>(string? contract = null)
where TViewModel : Sextant.IViewModel;
}
public interface IViewStackService
{
System.IObservable<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> ModalStack { get; }
System.IObservable<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> PageStack { get; }
Sextant.IView View { get; }
System.IObservable<System.Reactive.Unit> PopModal(bool animate = true);
System.IObservable<System.Reactive.Unit> PopPage(bool animate = true);
System.IObservable<System.Reactive.Unit> PopToRootPage(bool animate = true);
System.IObservable<System.Reactive.Unit> PushModal(Sextant.IViewModel modal, string? contract = null, bool withNavigationPage = true);
System.IObservable<System.Reactive.Unit> PushModal<TViewModel>(string? contract = null, bool withNavigationPage = true)
where TViewModel : Sextant.IViewModel;
System.IObservable<System.Reactive.Unit> PushPage(Sextant.INavigable page, string? contract = null, bool resetStack = false, bool animate = true);
System.IObservable<System.Reactive.Unit> PushPage(Sextant.IViewModel page, string? contract = null, bool resetStack = false, bool animate = true);
System.IObservable<System.Reactive.Unit> PushPage<TViewModel>(string? contract = null, bool resetStack = false, bool animate = true)
where TViewModel : Sextant.IViewModel;
System.IObservable<Sextant.IViewModel> TopModal();
System.IObservable<Sextant.IViewModel> TopPage();
}
public class NavigationParameter : System.Collections.Generic.Dictionary<string, object>, Sextant.INavigationParameter, System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>, System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>, System.Collections.IEnumerable
{
public NavigationParameter() { }
}
public sealed class ParameterViewStackService : Sextant.ParameterViewStackServiceBase
{
public ParameterViewStackService(Sextant.IView view) { }
public ParameterViewStackService(Sextant.IView view, Sextant.IViewModelFactory viewModelFactory) { }
}
public abstract class ParameterViewStackServiceBase : Sextant.ViewStackServiceBase, Sextant.IParameterViewStackService, Sextant.IViewStackService
{
protected ParameterViewStackServiceBase(Sextant.IView view, Sextant.IViewModelFactory viewModelFactory) { }
public System.IObservable<System.Reactive.Unit> PopPage(Sextant.INavigationParameter parameter, bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PushModal(Sextant.INavigable navigableModal, Sextant.INavigationParameter parameter, string? contract = null, bool withNavigationPage = true) { }
public System.IObservable<System.Reactive.Unit> PushModal<TViewModel>(Sextant.INavigationParameter parameter, string? contract = null, bool withNavigationPage = true)
where TViewModel : Sextant.INavigable { }
public System.IObservable<System.Reactive.Unit> PushPage(Sextant.INavigable navigableViewModel, Sextant.INavigationParameter parameter, string? contract = null, bool resetStack = false, bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PushPage<TViewModel>(Sextant.INavigationParameter parameter, string? contract = null, bool resetStack = false, bool animate = true)
where TViewModel : Sextant.INavigable { }
}
public class Sextant
{
public Sextant() { }
public Splat.IMutableDependencyResolver MutableLocator { get; }
public static Sextant.Sextant Instance { get; }
}
public static class SextantExtensions
{
public static void Initialize(this Sextant.Sextant sextant) { }
}
public static class ViewModelActionExtensions
{
public static object InvokeViewModelAction<T>(this object viewModel, System.Action<T> action)
where T : class { }
}
public static class ViewModelFactory
{
public static Sextant.IViewModelFactory Current { get; }
}
[System.Serializable]
public class ViewModelFactoryNotFoundException : System.Exception
{
public ViewModelFactoryNotFoundException() { }
public ViewModelFactoryNotFoundException(string message) { }
protected ViewModelFactoryNotFoundException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { }
public ViewModelFactoryNotFoundException(string message, System.Exception innerException) { }
}
public sealed class ViewStackService : Sextant.ViewStackServiceBase
{
public ViewStackService(Sextant.IView view) { }
public ViewStackService(Sextant.IView view, Sextant.IViewModelFactory? viewModelFactory) { }
}
public abstract class ViewStackServiceBase : Sextant.IViewStackService, Splat.IEnableLogger, System.IDisposable
{
protected ViewStackServiceBase(Sextant.IView view, Sextant.IViewModelFactory? viewModelFactory) { }
protected Sextant.IViewModelFactory Factory { get; }
protected Splat.IFullLogger Logger { get; }
public System.IObservable<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> ModalStack { get; }
protected System.Reactive.Subjects.BehaviorSubject<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> ModalSubject { get; }
protected System.Reactive.Disposables.CompositeDisposable NavigationDisposables { get; }
public System.IObservable<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> PageStack { get; }
protected System.Reactive.Subjects.BehaviorSubject<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> PageSubject { get; }
public Sextant.IView View { get; }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public System.IObservable<System.Reactive.Unit> PopModal(bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PopPage(bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PopToRootPage(bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PushModal(Sextant.IViewModel modal, string? contract = null, bool withNavigationPage = true) { }
public System.IObservable<System.Reactive.Unit> PushModal<TViewModel>(string? contract = null, bool withNavigationPage = true)
where TViewModel : Sextant.IViewModel { }
public System.IObservable<System.Reactive.Unit> PushPage(Sextant.INavigable viewModel, string? contract = null, bool resetStack = false, bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PushPage(Sextant.IViewModel viewModel, string? contract = null, bool resetStack = false, bool animate = true) { }
public System.IObservable<System.Reactive.Unit> PushPage<TViewModel>(string? contract = null, bool resetStack = false, bool animate = true)
where TViewModel : Sextant.IViewModel { }
public System.IObservable<Sextant.IViewModel> TopModal() { }
public System.IObservable<Sextant.IViewModel> TopPage() { }
protected static void AddToStackAndTick<T>(System.Reactive.Subjects.BehaviorSubject<System.Collections.Immutable.IImmutableList<T>> stackSubject, T item, bool reset) { }
protected static void PopRootAndTick<T>(System.Reactive.Subjects.BehaviorSubject<System.Collections.Immutable.IImmutableList<T>> stackSubject, System.Reactive.Disposables.CompositeDisposable disposable) { }
protected static T PopStackAndTick<T>(System.Reactive.Subjects.BehaviorSubject<System.Collections.Immutable.IImmutableList<T>> stackSubject) { }
}
}
namespace System.Reactive.Linq
{
public static class SubscribeSafeExtensions { }
public static class ToSignalExtension { }
public static class WhereNotNullExtension { }
}

Просмотреть файл

@ -1,3 +1,4 @@
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/reactiveui/sextant")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")]
namespace Sextant
{
@ -146,11 +147,11 @@ namespace Sextant
public sealed class ViewStackService : Sextant.ViewStackServiceBase
{
public ViewStackService(Sextant.IView view) { }
public ViewStackService(Sextant.IView view, Sextant.IViewModelFactory viewModelFactory) { }
public ViewStackService(Sextant.IView view, Sextant.IViewModelFactory? viewModelFactory) { }
}
public abstract class ViewStackServiceBase : Sextant.IViewStackService, Splat.IEnableLogger, System.IDisposable
{
protected ViewStackServiceBase(Sextant.IView view, Sextant.IViewModelFactory viewModelFactory) { }
protected ViewStackServiceBase(Sextant.IView view, Sextant.IViewModelFactory? viewModelFactory) { }
protected Sextant.IViewModelFactory Factory { get; }
protected Splat.IFullLogger Logger { get; }
public System.IObservable<System.Collections.Immutable.IImmutableList<Sextant.IViewModel>> ModalStack { get; }

Просмотреть файл

@ -23,23 +23,25 @@ namespace Sextant.Tests
[ExcludeFromCodeCoverage]
public class ApiApprovalTests
{
private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
private static readonly Regex _removeCoverletSectionRegex = new(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
/// <summary>
/// Tests to make sure the splat project is approved.
/// </summary>
[Fact]
public void Sextant()
{
CheckApproval(typeof(IViewStackService).Assembly);
}
public void Sextant() => CheckApproval(typeof(IViewStackService).Assembly);
private static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null)
private static void CheckApproval(Assembly assembly, [CallerMemberName]string? memberName = null, [CallerFilePath]string? filePath = null)
{
var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName();
var sourceDirectory = Path.GetDirectoryName(filePath);
if (sourceDirectory is null)
{
throw new ArgumentException("The directory name is empty for path: " + filePath);
}
var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt");
var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt");
@ -55,7 +57,7 @@ namespace Sextant.Tests
var approvedPublicApi = File.ReadAllText(approvedFileName);
var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly, null));
var receivedPublicApi = Filter(assembly.GeneratePublicApi(null));
if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture))
{

Просмотреть файл

@ -29,7 +29,7 @@ namespace Sextant.Tests
public void Should_Destroy()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.Disposable.IsDisposed.Should().BeFalse();

Просмотреть файл

@ -28,7 +28,7 @@ namespace Sextant.Tests
public void Should_Unwrap_Parameters()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatedTo(new NavigationParameter { { "hello", "world" }, { "life", 42 } }).Subscribe();
@ -45,7 +45,7 @@ namespace Sextant.Tests
public void Should_Return_Null_If_No_Values_Provided()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatedTo(new NavigationParameter());
@ -61,7 +61,7 @@ namespace Sextant.Tests
public void Should_Throw_If_Key_Not_Found()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
var result = Record.Exception(() => sut.WhenNavigatedTo(new NavigationParameter { { "hello", "world" } }).Subscribe());
@ -83,7 +83,7 @@ namespace Sextant.Tests
public void Should_Unwrap_Parameters()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatedFrom(new NavigationParameter { { "hello", "world" }, { "life", 42 } }).Subscribe();
@ -100,7 +100,7 @@ namespace Sextant.Tests
public void Should_Return_Null_If_No_Values_Provided()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatedFrom(new NavigationParameter());
@ -116,7 +116,7 @@ namespace Sextant.Tests
public void Should_Throw_If_Key_Not_Found()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
var result = Record.Exception(() => sut.WhenNavigatedFrom(new NavigationParameter { { "hello", "world" } }).Subscribe());

Просмотреть файл

@ -28,7 +28,7 @@ namespace Sextant.Tests
public void Should_Unwrap_Parameters()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatingTo(new NavigationParameter { { "hello", "world" }, { "life", 42 } }).Subscribe();
@ -45,7 +45,7 @@ namespace Sextant.Tests
public void Should_Return_Null_If_No_Values_Provided()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
sut.WhenNavigatingTo(new NavigationParameter());
@ -61,7 +61,7 @@ namespace Sextant.Tests
public void Should_Throw_If_Key_Not_Found()
{
// Given
ParameterViewModel sut = new ParameterViewModel();
ParameterViewModel sut = new();
// When
var result = Record.Exception(() => sut.WhenNavigatingTo(new NavigationParameter { { "hello", "world" } }).Subscribe());

Просмотреть файл

@ -57,6 +57,6 @@ namespace Sextant.Tests
public ParameterViewStackService WithFactory(IViewModelFactory viewModelFactory) =>
this.With(ref _viewModelFactory, viewModelFactory);
private ParameterViewStackService Build() => new ParameterViewStackService(_view, _viewModelFactory);
private ParameterViewStackService Build() => new(_view, _viewModelFactory);
}
}

Просмотреть файл

@ -28,10 +28,7 @@ namespace Sextant.Tests
/// <summary>
/// Initializes a new instance of the <see cref="TheConstructor"/> class.
/// </summary>
public TheConstructor()
{
Locator.GetLocator().UnregisterAll<IViewModelFactory>();
}
public TheConstructor() => Locator.GetLocator().UnregisterAll<IViewModelFactory>();
/// <summary>
/// Test that the object constructed uses the static instance of ViewModelFactory.
@ -40,7 +37,7 @@ namespace Sextant.Tests
public void Should_Throw_If_View_Model_Factory_Current_Null()
{
// Given, When
var result = Record.Exception(() => new ParameterViewStackServiceFixture().WithFactory(null));
var result = Record.Exception(() => new ParameterViewStackServiceFixture().WithFactory(null!));
// Then
result.Should().BeOfType<ViewModelFactoryNotFoundException>();
@ -56,7 +53,7 @@ namespace Sextant.Tests
Locator.CurrentMutable.RegisterViewModelFactory();
// When
var result = Record.Exception(() => new ParameterViewStackServiceFixture().WithFactory(null));
var result = Record.Exception(() => new ParameterViewStackServiceFixture().WithFactory(null!));
// Then
result.Should().BeNull();
@ -81,7 +78,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushPage(null, navigationParameter))
await Record.ExceptionAsync(async () => await sut.PushPage(null!, navigationParameter))
.ConfigureAwait(false);
// Then
@ -101,7 +98,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushPage(viewModel, (INavigationParameter)null))
await Record.ExceptionAsync(async () => await sut.PushPage(viewModel, null!))
.ConfigureAwait(false);
// Then
@ -264,7 +261,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushPage<NavigableViewModelMock>(null))
await Record.ExceptionAsync(async () => await sut.PushPage<NavigableViewModelMock>(null!))
.ConfigureAwait(false);
// Then
@ -338,7 +335,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushModal(null, navigationParameter))
await Record.ExceptionAsync(async () => await sut.PushModal(null!, navigationParameter))
.ConfigureAwait(false);
// Then
@ -358,7 +355,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushModal(viewModel, null))
await Record.ExceptionAsync(async () => await sut.PushModal(viewModel, null!))
.ConfigureAwait(false);
// Then
@ -498,7 +495,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PushModal<NavigableViewModelMock>(null))
await Record.ExceptionAsync(async () => await sut.PushModal<NavigableViewModelMock>(null!))
.ConfigureAwait(false);
// Then
@ -572,7 +569,7 @@ namespace Sextant.Tests
// When
var result =
await Record.ExceptionAsync(async () => await sut.PopPage(null))
await Record.ExceptionAsync(async () => await sut.PopPage(null!))
.ConfigureAwait(false);
// Then

Просмотреть файл

@ -37,6 +37,6 @@ namespace Sextant.Tests
public ViewStackServiceFixture WithFactory(IViewModelFactory viewModelFactory) =>
this.With(ref _viewModelFactory, viewModelFactory);
private ViewStackService Build() => new ViewStackService(_view, _viewModelFactory);
private ViewStackService Build() => new(_view, _viewModelFactory);
}
}

Просмотреть файл

@ -34,7 +34,7 @@ namespace Sextant.Tests
return Observable.Return(Unit.Default);
}
public static IObservable<Unit> PushModal(this ViewStackService viewStackService, IViewModel viewModel, string contract = null, int pages = 1)
public static IObservable<Unit> PushModal(this ViewStackService viewStackService, IViewModel viewModel, string? contract = null, int pages = 1)
{
for (var i = 0; i < pages; i++)
{
@ -44,7 +44,7 @@ namespace Sextant.Tests
return Observable.Return(Unit.Default);
}
public static IObservable<Unit> PushPage(this ViewStackService viewStackService, INavigable viewModel, string contract = null, int pages = 1)
public static IObservable<Unit> PushPage(this ViewStackService viewStackService, INavigable viewModel, string? contract = null, int pages = 1)
{
for (var i = 0; i < pages; i++)
{

Просмотреть файл

@ -30,10 +30,7 @@ namespace Sextant.Tests
/// <summary>
/// Initializes a new instance of the <see cref="TheConstructor"/> class.
/// </summary>
public TheConstructor()
{
Locator.GetLocator().UnregisterAll<IViewModelFactory>();
}
public TheConstructor() => Locator.GetLocator().UnregisterAll<IViewModelFactory>();
/// <summary>
/// Test that the object constructed uses the static instance of ViewModelFactory.
@ -42,7 +39,7 @@ namespace Sextant.Tests
public void Should_Throw_If_View_Model_Factory_Current_Null()
{
// Given, When
var result = Record.Exception(() => (ViewStackService)new ViewStackServiceFixture().WithFactory(null));
var result = Record.Exception(() => (ViewStackService)new ViewStackServiceFixture().WithFactory(null!));
// Then
result.Should().BeOfType<ViewModelFactoryNotFoundException>();
@ -58,7 +55,7 @@ namespace Sextant.Tests
Locator.CurrentMutable.RegisterViewModelFactory();
// When
var result = Record.Exception(() => (ViewStackService)new ViewStackServiceFixture().WithFactory(null));
var result = Record.Exception(() => (ViewStackService)new ViewStackServiceFixture().WithFactory(null!));
// Then
result.Should().BeNull();
@ -402,7 +399,7 @@ namespace Sextant.Tests
}
/// <summary>
/// Tests to verify the navigatino stack is cleared.
/// Tests to verify the navigation stack is cleared.
/// </summary>
/// <returns>A completion notification.</returns>
[Fact]
@ -421,7 +418,7 @@ namespace Sextant.Tests
}
/// <summary>
/// Tests to verify the navigatino stack is cleared.
/// Tests to verify the navigation stack is cleared.
/// </summary>
/// <returns>A completion notification.</returns>
[Fact]
@ -432,10 +429,7 @@ namespace Sextant.Tests
ViewStackService sut = new ViewStackServiceFixture();
await sut.PushPage(new NavigableViewModelMock(), pages: 3);
sut.View.PagePopped.Subscribe(_ =>
{
count++;
});
sut.View.PagePopped.Subscribe(_ => count++);
// When
await sut.PopToRootPage();
@ -608,7 +602,7 @@ namespace Sextant.Tests
ViewStackService sut = new ViewStackServiceFixture();
// When
var result = await Record.ExceptionAsync(async () => await sut.PushModal(null)).ConfigureAwait(false);
var result = await Record.ExceptionAsync(async () => await sut.PushModal(null!)).ConfigureAwait(false);
// Then
result.Should().BeOfType<ArgumentNullException>();
@ -704,7 +698,7 @@ namespace Sextant.Tests
ViewStackService sut = new ViewStackServiceFixture();
// When
var result = await Record.ExceptionAsync(async () => await sut.PushPage(null)).ConfigureAwait(false);
var result = await Record.ExceptionAsync(async () => await sut.PushPage(null!)).ConfigureAwait(false);
// Then
result.Should().BeOfType<ArgumentNullException>().Which.ParamName.Should().Be("viewModel");
@ -762,7 +756,7 @@ namespace Sextant.Tests
await sut.PushPage(new NavigableViewModelMock());
// Then
await sut.View.Received().PushPage(Arg.Any<IViewModel>(), null, false, true);
await sut.View.Received().PushPage(Arg.Any<IViewModel>(), null, false);
}
/// <summary>

Просмотреть файл

@ -1,7 +1,6 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoWarn>$(NoWarn);1591;CA1707;SA1633;CA1034;CA2000</NoWarn>
<TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>

Просмотреть файл

@ -22,10 +22,7 @@ namespace Sextant.Tests
/// <summary>
/// Initializes a new instance of the <see cref="CurrentPropertyTests"/> class.
/// </summary>
public CurrentPropertyTests()
{
Locator.CurrentMutable.UnregisterAll<IViewModelFactory>();
}
public CurrentPropertyTests() => Locator.CurrentMutable.UnregisterAll<IViewModelFactory>();
/// <summary>
/// Should throw if the IViewFactory is not registered.
@ -47,7 +44,7 @@ namespace Sextant.Tests
public void Should_Return_View_Model_Factory()
{
// Given, When
Splat.Locator.CurrentMutable.Register(() => new DefaultViewModelFactory(), typeof(IViewModelFactory));
Locator.CurrentMutable.Register(() => new DefaultViewModelFactory(), typeof(IViewModelFactory));
var viewModelFactory = ViewModelFactory.Current;
// Then

Просмотреть файл

@ -0,0 +1,48 @@
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/reactiveui/sextant")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v5.0", FrameworkDisplayName="")]
namespace Sextant.XamForms
{
public static class DependencyResolverMixins
{
public static string NavigationView { get; }
public static Sextant.XamForms.NavigationView? GetNavigationView(this Splat.IReadonlyDependencyResolver dependencyResolver, string? contract = null) { }
public static Splat.IMutableDependencyResolver RegisterNavigationView(this Splat.IMutableDependencyResolver dependencyResolver) { }
public static Splat.IMutableDependencyResolver RegisterNavigationView(this Splat.IMutableDependencyResolver dependencyResolver, System.Reactive.Concurrency.IScheduler mainThreadScheduler, System.Reactive.Concurrency.IScheduler backgroundScheduler) { }
public static Splat.IMutableDependencyResolver RegisterNavigationView<TView>(this Splat.IMutableDependencyResolver dependencyResolver, System.Func<TView> navigationViewFactory)
where TView : Sextant.IView { }
}
public class NavigationView : Xamarin.Forms.NavigationPage, Sextant.IView, Splat.IEnableLogger
{
public NavigationView() { }
public NavigationView(System.Reactive.Concurrency.IScheduler mainScheduler, System.Reactive.Concurrency.IScheduler backgroundScheduler, ReactiveUI.IViewLocator viewLocator) { }
public NavigationView(System.Reactive.Concurrency.IScheduler mainScheduler, System.Reactive.Concurrency.IScheduler backgroundScheduler, ReactiveUI.IViewLocator viewLocator, Xamarin.Forms.Page rootPage) { }
public System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; }
public System.IObservable<Sextant.IViewModel> PagePopped { get; }
public System.IObservable<System.Reactive.Unit> PopModal() { }
public System.IObservable<System.Reactive.Unit> PopPage(bool animate) { }
public System.IObservable<System.Reactive.Unit> PopToRootPage(bool animate) { }
public System.IObservable<System.Reactive.Unit> PushModal(Sextant.IViewModel modalViewModel, string? contract, bool withNavigationPage = true) { }
public System.IObservable<System.Reactive.Unit> PushPage(Sextant.IViewModel viewModel, string? contract, bool resetStack, bool animate) { }
}
public static class SextantExtensions
{
public static void InitializeForms(this Sextant.Sextant sextant) { }
}
public static class SextantHelper
{
[System.Obsolete("Use the Initialize method.")]
public static Sextant.XamForms.NavigationView Initialise<TViewModel>(System.Reactive.Concurrency.IScheduler? mainThreadScheduler = null, System.Reactive.Concurrency.IScheduler? backgroundScheduler = null, ReactiveUI.IViewLocator? viewLocator = null)
where TViewModel : class, Sextant.IViewModel { }
[System.Obsolete("Use the dependency resolver mixins.")]
public static Sextant.XamForms.NavigationView Initialize<TViewModel>(System.Reactive.Concurrency.IScheduler? mainThreadScheduler = null, System.Reactive.Concurrency.IScheduler? backgroundScheduler = null, ReactiveUI.IViewLocator? viewLocator = null)
where TViewModel : class, Sextant.IViewModel { }
[System.Obsolete("Use the dependency resolver mixins.")]
public static void RegisterNavigation<TView, TViewModel>(System.Reactive.Concurrency.IScheduler? mainThreadScheduler = null, System.Reactive.Concurrency.IScheduler? backgroundScheduler = null, ReactiveUI.IViewLocator? viewLocator = null)
where TView : ReactiveUI.IViewFor
where TViewModel : class, Sextant.IViewModel { }
[System.Obsolete("Use the dependency resolver mixins.")]
public static void RegisterView<TView, TViewModel>(string? contract = null)
where TView : ReactiveUI.IViewFor, new ()
where TViewModel : class, Sextant.IViewModel { }
}
}

Просмотреть файл

@ -1,3 +1,4 @@
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/reactiveui/sextant")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")]
namespace Sextant.XamForms
{

Просмотреть файл

@ -23,23 +23,25 @@ namespace Sextant.XamForms.Tests
[ExcludeFromCodeCoverage]
public class ApiApprovalTests
{
private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
private static readonly Regex _removeCoverletSectionRegex = new(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);
/// <summary>
/// Tests to make sure the splat project is approved.
/// </summary>
[Fact]
public void SextantXamForms()
{
CheckApproval(typeof(NavigationView).Assembly);
}
public void SextantXamForms() => CheckApproval(typeof(NavigationView).Assembly);
private static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null)
private static void CheckApproval(Assembly assembly, [CallerMemberName]string? memberName = null, [CallerFilePath]string? filePath = null)
{
var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName();
var sourceDirectory = Path.GetDirectoryName(filePath);
if (sourceDirectory is null)
{
throw new ArgumentException("The directory name is empty for path: " + filePath);
}
var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt");
var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt");
@ -55,7 +57,7 @@ namespace Sextant.XamForms.Tests
var approvedPublicApi = File.ReadAllText(approvedFileName);
var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly, null));
var receivedPublicApi = Filter(assembly.GeneratePublicApi());
if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture))
{

Просмотреть файл

@ -1,7 +1,6 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<NoWarn>$(NoWarn);1591;CA1707;SA1633;CA1034</NoWarn>
<TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>

Просмотреть файл

@ -15,7 +15,7 @@ namespace Sextant.XamForms.Tests
public sealed class SextantExtensionTests
{
/// <summary>
/// Tests the Sextant Initalize Forms method.
/// Tests the Sextant Initialize Forms method.
/// </summary>
public sealed class TheInitializeFormsMethod
{

Просмотреть файл

@ -4,12 +4,9 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Concurrency;
using System.Text;
using ReactiveUI;
using Sextant.XamForms;
using Splat;
namespace Sextant.XamForms

Просмотреть файл

@ -4,9 +4,6 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Text;
using Splat;
namespace Sextant.XamForms
{

Просмотреть файл

@ -4,7 +4,6 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Diagnostics;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
@ -21,7 +20,6 @@ namespace Sextant.XamForms
public class NavigationView : NavigationPage, IView, IEnableLogger
{
private readonly IScheduler _backgroundScheduler;
private readonly IScheduler _mainScheduler;
private readonly IViewLocator _viewLocator;
private readonly IFullLogger _logger;
@ -44,21 +42,27 @@ namespace Sextant.XamForms
: base(rootPage)
{
_backgroundScheduler = backgroundScheduler;
_mainScheduler = mainScheduler;
MainThreadScheduler = mainScheduler;
_viewLocator = viewLocator;
_logger = this.Log();
PagePopped =
Observable
.FromEvent<EventHandler<NavigationEventArgs>, object>(
.FromEvent<EventHandler<NavigationEventArgs>, IViewModel>(
handler =>
{
void Handler(object sender, NavigationEventArgs args) => handler(args.Page.BindingContext);
void Handler(object? sender, NavigationEventArgs args)
{
if (args.Page.BindingContext is IViewModel viewModel)
{
handler(viewModel);
}
}
return Handler;
},
x => Popped += x,
x => Popped -= x)
.Cast<IViewModel>();
x => Popped -= x);
}
/// <summary>
@ -70,25 +74,31 @@ namespace Sextant.XamForms
public NavigationView(IScheduler mainScheduler, IScheduler backgroundScheduler, IViewLocator viewLocator)
{
_backgroundScheduler = backgroundScheduler;
_mainScheduler = mainScheduler;
MainThreadScheduler = mainScheduler;
_viewLocator = viewLocator;
_logger = this.Log();
PagePopped =
Observable
.FromEvent<EventHandler<NavigationEventArgs>, object>(
.FromEvent<EventHandler<NavigationEventArgs>, IViewModel>(
handler =>
{
void Handler(object sender, NavigationEventArgs args) => handler(args.Page.BindingContext);
void Handler(object? sender, NavigationEventArgs args)
{
if (args.Page.BindingContext is IViewModel viewModel)
{
handler(viewModel);
}
}
return Handler;
},
x => Popped += x,
x => Popped -= x)
.Cast<IViewModel>();
x => Popped -= x);
}
/// <inheritdoc />
public IScheduler MainThreadScheduler => _mainScheduler;
public IScheduler MainThreadScheduler { get; }
/// <inheritdoc />
public IObservable<IViewModel> PagePopped { get; }
@ -99,7 +109,7 @@ namespace Sextant.XamForms
.PopModalAsync()
.ToObservable()
.Select(_ => Unit.Default)
.ObserveOn(_mainScheduler); // XF completes the pop operation on a background thread :/
.ObserveOn(MainThreadScheduler); // XF completes the pop operation on a background thread :/
/// <inheritdoc />
public IObservable<Unit> PopPage(bool animate) =>
@ -107,7 +117,7 @@ namespace Sextant.XamForms
.PopAsync(animate)
.ToObservable()
.Select(_ => Unit.Default)
.ObserveOn(_mainScheduler); // XF completes the pop operation on a background thread :/
.ObserveOn(MainThreadScheduler); // XF completes the pop operation on a background thread :/
/// <inheritdoc />
public IObservable<Unit> PopToRootPage(bool animate) =>
@ -115,7 +125,7 @@ namespace Sextant.XamForms
.PopToRootAsync(animate)
.ToObservable()
.Select(_ => Unit.Default)
.ObserveOn(_mainScheduler);
.ObserveOn(MainThreadScheduler);
/// <inheritdoc />
public IObservable<Unit> PushModal(IViewModel modalViewModel, string? contract, bool withNavigationPage = true) =>
@ -125,12 +135,7 @@ namespace Sextant.XamForms
{
var page = LocatePageFor(modalViewModel, contract);
SetPageTitle(page, modalViewModel.Id);
if (withNavigationPage)
{
return new NavigationPage(page);
}
return page;
return withNavigationPage ? new NavigationPage(page) : page;
},
CurrentThreadScheduler.Instance)
.ObserveOn(CurrentThreadScheduler.Instance)
@ -180,11 +185,17 @@ namespace Sextant.XamForms
.ToObservable();
});
private static void SetPageTitle(Page page, string resourceKey) =>
// var title = Localize.GetString(resourceKey);
// TODO: ensure resourceKey isn't null and is localized.
page.Title = resourceKey;
private Page LocatePageFor(object viewModel, string? contract)
{
var view = _viewLocator.ResolveView(viewModel, contract);
if (view == null)
if (view is null)
{
throw new InvalidOperationException($"No view could be located for type '{viewModel.GetType().FullName}', contract '{contract}'. Be sure Splat has an appropriate registration.");
}
@ -198,12 +209,5 @@ namespace Sextant.XamForms
return page;
}
private void SetPageTitle(Page page, string resourceKey)
{
// var title = Localize.GetString(resourceKey);
// TODO: ensure resourceKey isn't null and is localized.
page.Title = resourceKey;
}
}
}

Просмотреть файл

@ -1,22 +1,21 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
<AssemblyName>Sextant.XamForms</AssemblyName>
<RootNamespace>Sextant</RootNamespace>
<RootNamespace>Sextant.XamForms</RootNamespace>
<PackageId>Sextant.XamForms</PackageId>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<WarningsAsErrors>CS8625;CS8604;CS8600;CS8614;CS8603;CS8618;CS8619</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReactiveUI" Version="12.*" />
<PackageReference Include="ReactiveUI" Version="13.*" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
<PackageReference Include="Xamarin.Forms" Version="4.5.*" />
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.6.*" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mixins/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Просмотреть файл

@ -24,10 +24,8 @@ namespace Sextant.XamForms
[Obsolete("Use the dependency resolver mixins.")]
public static void RegisterView<TView, TViewModel>(string? contract = null)
where TView : IViewFor, new()
where TViewModel : class, IViewModel
{
where TViewModel : class, IViewModel =>
Locator.CurrentMutable.Register(() => new TView(), typeof(IViewFor<TViewModel>), contract);
}
/// <summary>
/// Registers a value for navigation.
@ -72,9 +70,8 @@ namespace Sextant.XamForms
var viewStackService = new ViewStackService(navigationView);
Locator.CurrentMutable.Register<IViewStackService>(() => viewStackService);
var instance = Activator.CreateInstance(typeof(TViewModel), viewStackService) as TViewModel;
if (instance == null)
if (!(Activator.CreateInstance(typeof(TViewModel), viewStackService) is TViewModel instance))
{
throw new InvalidOperationException($"Could not initialize a view for view model {typeof(TViewModel)}");
}
@ -94,9 +91,7 @@ namespace Sextant.XamForms
/// <returns>The navigation view.</returns>
[Obsolete("Use the " + nameof(Initialize) + " method.")]
public static NavigationView Initialise<TViewModel>(IScheduler? mainThreadScheduler = null, IScheduler? backgroundScheduler = null, IViewLocator? viewLocator = null)
where TViewModel : class, IViewModel
{
return Initialize<TViewModel>(mainThreadScheduler, backgroundScheduler, viewLocator);
}
where TViewModel : class, IViewModel =>
Initialize<TViewModel>(mainThreadScheduler, backgroundScheduler, viewLocator);
}
}

Просмотреть файл

@ -11,7 +11,7 @@ namespace Sextant.IOS.Runner
/// <summary>
/// The iOS application.
/// </summary>
public class Application
public static class Application
{
// This is the main entry point of the application.
@ -19,12 +19,11 @@ namespace Sextant.IOS.Runner
/// Defines the entry point of the application.
/// </summary>
/// <param name="args">The arguments.</param>
private static void Main(string[] args)
{
private static void Main(string[] args) =>
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}
#pragma warning restore SA1649 // File name should match first type name

Просмотреть файл

@ -18,10 +18,7 @@ namespace Sextant.IOS.Runner
/// Initializes a new instance of the <see cref="PageViewModelMock"/> class.
/// </summary>
/// <param name="id">The id of the page.</param>
public PageViewModelMock(string id = null)
{
_id = id;
}
public PageViewModelMock(string id = null) => _id = id;
/// <summary>
/// Gets the ID of the page.

Просмотреть файл

@ -0,0 +1,8 @@
is_global = true
build_property.TargetFramework =
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids = {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
build_property.PublishSingleFile =
build_property.IncludeAllContentForSelfExtract =
build_property._SupportedPlatformList =

Просмотреть файл

@ -11,6 +11,7 @@
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>Blank</AssemblyName>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>

Просмотреть файл

@ -17,9 +17,6 @@ namespace Sextant.IOS.Runner
internal class TestViewLocator
: IViewLocator
{
public IViewFor ResolveView<T>(T viewModel, string contract = null)
{
return new PageUiViewController();
}
public IViewFor ResolveView<T>(T viewModel, string contract = null) => new PageUiViewController();
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -38,7 +38,7 @@ namespace Sextant
IObservable<Unit> PopPage(bool animate = true);
/// <summary>
/// Pops the the root page.
/// Pops the root page.
/// </summary>
/// <param name="animate">if set to <c>true</c> [animate].</param>
/// <returns>An observable that signals when the pop has been completed.</returns>

Просмотреть файл

@ -83,7 +83,7 @@ namespace Sextant
}
IView view = Locator.Current.GetService<IView>(NavigationView);
dependencyResolver.RegisterLazySingleton<T>(() => factory(view));
dependencyResolver.RegisterLazySingleton(() => factory(view));
return dependencyResolver;
}
@ -109,7 +109,7 @@ namespace Sextant
IView view = Locator.Current.GetService<IView>(NavigationView);
IViewModelFactory viewModelFactory = Locator.Current.GetService<IViewModelFactory>();
dependencyResolver.RegisterLazySingleton<T>(() => factory(view, viewModelFactory));
dependencyResolver.RegisterLazySingleton(() => factory(view, viewModelFactory));
return dependencyResolver;
}
@ -158,7 +158,7 @@ namespace Sextant
/// <typeparam name="TViewModel">The type of the view model.</typeparam>
/// <param name="dependencyResolver">The dependency resolver.</param>
/// <param name="contract">The contract.</param>
/// <returns>The dependencyResovler.</returns>
/// <returns>The dependency resolver to use.</returns>
public static IMutableDependencyResolver RegisterView<TView, TViewModel>(this IMutableDependencyResolver dependencyResolver, string? contract = null)
where TView : IViewFor<TViewModel>, new()
where TViewModel : class, IViewModel

Просмотреть файл

@ -32,12 +32,12 @@ namespace Sextant
bool resetStack = false,
bool animate = true)
{
if (navigableViewModel == null)
if (navigableViewModel is null)
{
throw new ArgumentNullException(nameof(navigableViewModel));
}
if (parameter == null)
if (parameter is null)
{
throw new ArgumentNullException(nameof(parameter));
}
@ -45,7 +45,7 @@ namespace Sextant
navigableViewModel
.WhenNavigatingTo(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigating =>
.Subscribe(_ =>
Logger.Debug(
$"Called `WhenNavigatingTo` on '{navigableViewModel.Id}' passing parameter {parameter}"));
@ -59,19 +59,19 @@ namespace Sextant
navigableViewModel
.WhenNavigatedTo(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigated => Logger.Debug($"Called `WhenNavigatedTo` on '{navigableViewModel.Id}' passing parameter {parameter}"));
.Subscribe(_ => Logger.Debug($"Called `WhenNavigatedTo` on '{navigableViewModel.Id}' passing parameter {parameter}"));
});
}
/// <inheritdoc />
public IObservable<Unit> PushModal(INavigable navigableModal, INavigationParameter parameter, string? contract = null, bool withNavigationPage = true)
{
if (navigableModal == null)
if (navigableModal is null)
{
throw new ArgumentNullException(nameof(navigableModal));
}
if (parameter == null)
if (parameter is null)
{
throw new ArgumentNullException(nameof(parameter));
}
@ -79,7 +79,7 @@ namespace Sextant
navigableModal
.WhenNavigatingTo(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigating =>
.Subscribe(_ =>
Logger.Debug($"Called `WhenNavigatingTo` on '{navigableModal.Id}' passing parameter {parameter}"));
return View
@ -92,7 +92,7 @@ namespace Sextant
navigableModal
.WhenNavigatedTo(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigated => Logger.Debug($"Called `WhenNavigatedTo` on '{navigableModal.Id}' passing parameter {parameter}"));
.Subscribe(_ => Logger.Debug($"Called `WhenNavigatedTo` on '{navigableModal.Id}' passing parameter {parameter}"));
});
}
@ -115,31 +115,30 @@ namespace Sextant
/// <inheritdoc />
public IObservable<Unit> PopPage(INavigationParameter parameter, bool animate = true)
{
if (parameter == null)
if (parameter is null)
{
throw new ArgumentNullException(nameof(parameter));
}
IViewModel poppedPage = TopPage().FirstOrDefaultAsync().Wait();
IViewModel? poppedPage = TopPage().FirstOrDefaultAsync().Wait();
return View
.PopPage(animate)
.Do(_ =>
{
poppedPage
.InvokeViewModelAction<INavigable>(x =>
poppedPage?.InvokeViewModelAction<INavigable>(x =>
x.WhenNavigatedFrom(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigatedFrom =>
.Subscribe(_ =>
Logger.Debug($"Called `WhenNavigatedFrom` on '{poppedPage.Id}' passing parameter {parameter}")))
.InvokeViewModelAction<IDestructible>(x => x.Destroy());
IViewModel topPage = TopPage().FirstOrDefaultAsync().Wait();
IViewModel? topPage = TopPage().FirstOrDefaultAsync().Wait();
if (topPage is INavigated navigated)
{
navigated
.WhenNavigatedTo(parameter)
.ObserveOn(View.MainThreadScheduler)
.Subscribe(navigatedTo =>
.Subscribe(_ =>
Logger.Debug($"Called `WhenNavigatedTo` passing parameter {parameter}"));
}
});

Просмотреть файл

@ -22,17 +22,19 @@ namespace Sextant
public static object InvokeViewModelAction<T>(this object viewModel, Action<T> action)
where T : class
{
if (action == null)
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
if (viewModel is IViewModel element)
if (viewModel is not IViewModel element)
{
if (element is T viewModelAsT)
{
action(viewModelAsT);
}
return viewModel;
}
if (element is T viewModelAsT)
{
action(viewModelAsT);
}
return viewModel;

Просмотреть файл

@ -17,7 +17,7 @@ namespace Sextant
/// </summary>
/// <param name="view">The view.</param>
public ViewStackService(IView view)
: this(view, null!)
: this(view, null)
{
}
@ -26,7 +26,7 @@ namespace Sextant
/// </summary>
/// <param name="view">The view.</param>
/// <param name="viewModelFactory">The view model factory.</param>
public ViewStackService(IView view, IViewModelFactory viewModelFactory)
public ViewStackService(IView view, IViewModelFactory? viewModelFactory)
: base(view, viewModelFactory)
{
}

Просмотреть файл

@ -28,7 +28,7 @@ namespace Sextant
/// </summary>
/// <param name="view">The view.</param>
/// <param name="viewModelFactory">The view model factory.</param>
protected ViewStackServiceBase(IView view, IViewModelFactory viewModelFactory)
protected ViewStackServiceBase(IView view, IViewModelFactory? viewModelFactory)
{
Logger = this.Log();
View = view ?? throw new ArgumentNullException(nameof(view));
@ -66,12 +66,12 @@ namespace Sextant
/// <summary>
/// Gets the current view on the stack.
/// </summary>
public IView View { get; private set; }
public IView View { get; }
/// <summary>
/// Gets the logger.
/// </summary>
protected IFullLogger Logger { get; private set; }
protected IFullLogger Logger { get; }
/// <summary>
/// Gets the view model factory.
@ -81,17 +81,17 @@ namespace Sextant
/// <summary>
/// Gets the modal subject.
/// </summary>
protected BehaviorSubject<IImmutableList<IViewModel>> ModalSubject { get; private set; }
protected BehaviorSubject<IImmutableList<IViewModel>> ModalSubject { get; }
/// <summary>
/// Gets the page subject.
/// </summary>
protected BehaviorSubject<IImmutableList<IViewModel>> PageSubject { get; private set; }
protected BehaviorSubject<IImmutableList<IViewModel>> PageSubject { get; }
/// <summary>
/// Gets the navigation disposables.
/// </summary>
protected CompositeDisposable NavigationDisposables { get; } = new CompositeDisposable();
protected CompositeDisposable NavigationDisposables { get; } = new();
/// <summary>
/// Pops the <see cref="INavigable" /> off the stack.
@ -110,10 +110,8 @@ namespace Sextant
public IObservable<Unit> PopPage(bool animate = true)
{
var top = TopPage().Wait();
return View.PopPage(animate).Do(_ =>
{
top.InvokeViewModelAction<IDestructible>(x => x.Destroy());
});
return View.PopPage(animate)
.Do(_ => top.InvokeViewModelAction<IDestructible>(x => x.Destroy()));
}
/// <inheritdoc />
@ -124,7 +122,7 @@ namespace Sextant
/// <inheritdoc />
public IObservable<Unit> PushModal(IViewModel modal, string? contract = null, bool withNavigationPage = true)
{
if (modal == null)
if (modal is null)
{
throw new ArgumentNullException(nameof(modal));
}
@ -158,7 +156,7 @@ namespace Sextant
/// <inheritdoc />
public IObservable<Unit> PushPage(IViewModel viewModel, string? contract = null, bool resetStack = false, bool animate = true)
{
if (viewModel == null)
if (viewModel is null)
{
throw new ArgumentNullException(nameof(viewModel));
}
@ -214,14 +212,7 @@ namespace Sextant
var stack = stackSubject.Value;
if (reset)
{
stack = new[] { item }.ToImmutableList();
}
else
{
stack = stack.Add(item);
}
stack = reset ? new[] { item }.ToImmutableList() : stack.Add(item);
stackSubject.OnNext(stack);
}
@ -263,14 +254,14 @@ namespace Sextant
protected static void PopRootAndTick<T>(BehaviorSubject<IImmutableList<T>> stackSubject, CompositeDisposable disposable)
{
IImmutableList<T> poppedStack = ImmutableList<T>.Empty;
if (stackSubject == null || stackSubject.Value.Count == 0)
if (stackSubject is null || stackSubject.Value.Count == 0)
{
throw new InvalidOperationException("Stack is empty.");
}
stackSubject
.Take(1)
.Where(stack => stack != null)
.Where(stack => stack is not null)
.Subscribe(stack =>
{
if (stack.Count > 1)
@ -279,7 +270,7 @@ namespace Sextant
foreach (T popped in stack.RemoveRange(poppedStack).Reverse())
{
if (popped == null)
if (popped is null)
{
continue;
}

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
using System;
namespace Sextant
{
/// <summary>
/// Extensions methods to setup the <see cref="Sextant"/> instance.
/// </summary>
public static class SextantExtensions
{
/// <summary>
/// Initializes the specified sextant.
/// </summary>
/// <param name="sextant">The sextant.</param>
public static void Initialize(this Sextant sextant)
{
if (sextant is null)
{
throw new ArgumentNullException(nameof(sextant));
}
sextant.MutableLocator.RegisterViewStackService();
}
}
}

Просмотреть файл

@ -4,10 +4,6 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
namespace Sextant
@ -25,10 +21,7 @@ namespace Sextant
/// </summary>
public string? DeviceFamily
{
get
{
return _queriedDeviceFamily;
}
get => _queriedDeviceFamily;
set
{

Просмотреть файл

@ -4,10 +4,8 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Concurrency;
using System.Text;
using ReactiveUI;
using Splat;
@ -138,7 +136,7 @@ namespace Sextant
throw new ArgumentNullException(nameof(dependencyResolver));
}
var uwpViewTypeResolver = Locator.Current.GetService<ViewTypeResolver>(contract);
var uwpViewTypeResolver = dependencyResolver.GetService<ViewTypeResolver>(contract);
return uwpViewTypeResolver.ResolveViewType<TViewModel>();
}
@ -163,8 +161,7 @@ namespace Sextant
throw new ArgumentNullException(nameof(viewModel));
}
var vm = viewModel;
var uwpViewTypeResolver = Locator.Current.GetService<ViewTypeResolver>(contract);
var uwpViewTypeResolver = dependencyResolver.GetService<ViewTypeResolver>(contract);
return uwpViewTypeResolver.ResolveViewType<TViewModel>();
}
}

Просмотреть файл

@ -4,10 +4,6 @@
// See the LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Text;
using Splat;
namespace Sextant
{

Просмотреть файл

@ -14,15 +14,7 @@ namespace Sextant
/// </summary>
public class ViewTypeResolver
{
private Dictionary<(string vmTypeName, string? contract), Type> _typeDictionary;
/// <summary>
/// Initializes a new instance of the <see cref="ViewTypeResolver"/> class.
/// </summary>
public ViewTypeResolver()
{
_typeDictionary = new Dictionary<(string vmTypeName, string? contract), Type>();
}
private readonly Dictionary<(string VmTypeName, string? Contract), Type> _typeDictionary = new();
/// <summary>
/// Register view Type with viewmodel Type.
@ -51,12 +43,9 @@ namespace Sextant
public Type? ResolveViewType<TViewModel>(string? contract = null)
where TViewModel : class
{
if (_typeDictionary.ContainsKey((typeof(TViewModel).AssemblyQualifiedName, contract)))
{
return _typeDictionary[(typeof(TViewModel).AssemblyQualifiedName, contract)];
}
_typeDictionary.TryGetValue((typeof(TViewModel).AssemblyQualifiedName, contract), out var value);
return null;
return value;
}
/// <summary>
@ -72,12 +61,9 @@ namespace Sextant
throw new ArgumentNullException(nameof(viewModelType));
}
if (_typeDictionary.ContainsKey((viewModelType.AssemblyQualifiedName, contract)))
{
return _typeDictionary[(viewModelType.AssemblyQualifiedName, contract)];
}
_typeDictionary.TryGetValue((viewModelType.AssemblyQualifiedName, contract), out var value);
return null;
return value;
}
}
}

Просмотреть файл

@ -5,22 +5,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using ReactiveUI;
using Splat;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
@ -31,23 +27,21 @@ namespace Sextant
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public partial class NavigationView : Page, IView, IEnableLogger
public partial class NavigationView : IView, IEnableLogger
{
/// <summary>
/// A depedendency property for the back button.
/// A dependency property for the back button.
/// </summary>
public static readonly DependencyProperty IsBackButtonVisibleProperty =
DependencyProperty.Register("IsBackButtonVisible", typeof(bool), typeof(NavigationView), new PropertyMetadata(true));
private readonly IScheduler _backgroundScheduler;
private readonly IScheduler _mainScheduler;
private readonly IViewLocator _viewLocator;
private IFullLogger _logger;
private readonly IFullLogger _logger;
private readonly Stack<IViewModel?> _mirroredPageStack;
private readonly Stack<IViewModel?> _mirroredModalStack;
private ContentDialog? _contentDialog;
private IViewModel? _lastPoppedViewModel;
private Stack<IViewModel?> _mirroredPageStack;
private Stack<IViewModel?> _mirroredModalStack;
/// <summary>
/// Initializes a new instance of the <see cref="NavigationView"/> class.
@ -69,8 +63,8 @@ namespace Sextant
_logger = this.Log();
_backgroundScheduler = backgroundScheduler;
_mainScheduler = mainScheduler;
BackgroundScheduler = backgroundScheduler;
MainThreadScheduler = mainScheduler;
_viewLocator = viewLocator;
_mirroredPageStack = new Stack<IViewModel?>();
@ -80,28 +74,14 @@ namespace Sextant
PagePopped = Observable
.FromEvent<NavigatedEventHandler, NavigationEventArgs>(
handler =>
{
return (sender, e) => handler(e);
},
handler => (_, e) => handler(e),
x => mainFrame.Navigated += x,
x => mainFrame.Navigated -= x)
.Do(args =>
{
if (mainFrame.CanGoBack)
{
IsBackButtonVisible = true;
}
else
{
IsBackButtonVisible = false;
}
})
.Do(_ => IsBackButtonVisible = mainFrame.CanGoBack)
.Where(ep => ep.NavigationMode == NavigationMode.Back)
.Select(ep =>
{
var view = ep.Content as IViewFor;
if (view == null)
if (!(ep.Content is IViewFor view))
{
_logger.Debug($"The view ({ep.Content.GetType()}) does not implement IViewFor<>. Cannot set ViewModel from a back navigation.");
}
@ -115,7 +95,7 @@ namespace Sextant
// But we want the view that was just removed. We need to send the old view's viewmodel to IViewStackService so that the ViewModel can be removed from the stack.
return _lastPoppedViewModel;
})
.Where(x => x != null);
.Where(x => x is not null);
BackRequested.Subscribe();
}
@ -128,35 +108,37 @@ namespace Sextant
get
{
var popups = VisualTreeHelper.GetOpenPopups(Window.Current);
return popups.FirstOrDefault(x => x.Child is ContentDialog) != null;
return popups.FirstOrDefault(x => x.Child is ContentDialog) is not null;
}
}
/// <summary>
/// Gets the background scheduler.
/// </summary>
public IScheduler BackgroundScheduler { get; }
/// <summary>
/// Gets or sets a value indicating whether the default back button is visible.
/// </summary>
public bool IsBackButtonVisible
{
get { return (bool)GetValue(IsBackButtonVisibleProperty); }
set { SetValue(IsBackButtonVisibleProperty, value); }
get => (bool)GetValue(IsBackButtonVisibleProperty);
set => SetValue(IsBackButtonVisibleProperty, value);
}
/// <inheritdoc />
public IScheduler MainThreadScheduler => _mainScheduler;
public IScheduler MainThreadScheduler { get; }
/// <inheritdoc />
public IObservable<IViewModel?> PagePopped { get; }
/// <summary>
/// Gets combined backrequested observable from system, backbutton, and xbox controller sources.
/// Gets combined back requested observable from system, back button, and xbox controller sources.
/// </summary>
public IObservable<Unit> BackRequested => Observable.Merge(
Observable
.FromEvent<EventHandler<BackRequestedEventArgs>, BackRequestedEventArgs>(
handler =>
{
return (_, e) => handler(e);
},
handler => (_, e) => handler(e),
x => SystemNavigationManager.GetForCurrentView().BackRequested += x,
x => SystemNavigationManager.GetForCurrentView().BackRequested -= x)
.Do(ev =>
@ -170,10 +152,12 @@ namespace Sextant
.Select(_ => Unit.Default),
Observable
.FromEvent<PointerEventHandler, PointerRoutedEventArgs>(
handler => (_, e) => handler(e),
x => PointerPressed += x,
x => PointerPressed -= x)
.Where(args => args.GetCurrentPoint(this as UIElement).Properties.PointerUpdateKind == Windows.UI.Input.PointerUpdateKind.XButton1Pressed)
handler => (_, e) => handler(e),
x => PointerPressed += x,
x => PointerPressed -= x)
.Where(args =>
args.GetCurrentPoint(this).Properties.PointerUpdateKind ==
Windows.UI.Input.PointerUpdateKind.XButton1Pressed)
.Do(args =>
{
if (mainFrame.CanGoBack)
@ -184,49 +168,54 @@ namespace Sextant
})
.Select(_ => Unit.Default),
Observable
.FromEvent<RoutedEventHandler, RoutedEventArgs>(
handler =>
{
void RoutedHandler(object sender, RoutedEventArgs e) => handler(e);
return RoutedHandler;
},
x => backButton.Click += x,
x => backButton.Click -= x)
.Do(args =>
.FromEvent<RoutedEventHandler, Unit>(
handler =>
{
void RoutedHandler(object? sender, RoutedEventArgs e) => handler(Unit.Default);
return RoutedHandler;
},
x => backButton.Click += x,
x => backButton.Click -= x)
.Do(_ =>
{
if (mainFrame.CanGoBack)
{
PopPage(true);
}
})
.Select(_ => Unit.Default));
}));
/// <inheritdoc />
public IObservable<Unit> PopModal()
{
if (_contentDialog != null)
if (_contentDialog is null)
{
_mirroredModalStack.TryPop(out _);
_contentDialog.Hide();
if (_mirroredModalStack.TryPeek(out var modal))
{
if (modal == null)
{
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
}
_contentDialog = new ContentDialog();
_contentDialog.FullSizeDesired = true;
_contentDialog.IsPrimaryButtonEnabled = false;
_contentDialog.IsSecondaryButtonEnabled = false;
_contentDialog.Content = LocatePageFor(modal, null);
_ = _contentDialog.ShowAsync();
}
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
_mirroredModalStack.TryPop(out _);
_contentDialog.Hide();
if (!_mirroredModalStack.TryPeek(out var modal))
{
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
if (modal is null)
{
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
_contentDialog = new ContentDialog
{
FullSizeDesired = true,
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
Content = LocatePageFor(modal, null)
};
_ = _contentDialog.ShowAsync();
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
/// <inheritdoc />
@ -244,18 +233,18 @@ namespace Sextant
_mirroredPageStack.Pop();
var view = mainFrame.Content as IViewFor;
if (view == null)
if (!(mainFrame.Content is IViewFor view))
{
_logger.Debug($"The view ({mainFrame.Content.GetType()}) does not implement IViewFor<>. Cannot get ViewModel.");
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
var contentTypeName = mainFrame.Content?.GetType().ToString() ?? "Unknown Type";
_logger.Debug($"The view ({contentTypeName}) does not implement IViewFor<>. Cannot get ViewModel.");
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
_lastPoppedViewModel = view.ViewModel as IViewModel;
mainFrame.GoBack(animation);
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
/// <inheritdoc />
@ -281,29 +270,25 @@ namespace Sextant
_mirroredPageStack.Pop();
}
var view = mainFrame.Content as IViewFor;
if (view == null)
if (!(mainFrame.Content is IViewFor view))
{
_logger.Debug($"The view ({mainFrame.Content.GetType()}) does not implement IViewFor<>. Cannot get ViewModel.");
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
var contentTypeName = mainFrame.Content?.GetType().ToString() ?? "Unknown Type";
_logger.Debug($"The view ({contentTypeName}) does not implement IViewFor<>. Cannot get ViewModel.");
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
_lastPoppedViewModel = view.ViewModel as IViewModel;
mainFrame.GoBack(animation);
return Observable.Return(Unit.Default).ObserveOn(_mainScheduler);
return Observable.Return(Unit.Default).ObserveOn(MainThreadScheduler);
}
/// <inheritdoc />
public IObservable<Unit> PushModal(IViewModel modalViewModel, string? contract, bool withNavigationPage = true) =>
Observable
.Start(
() =>
{
// ignore withNavigationPage, not necessary for UWP.
return LocatePageFor(modalViewModel, contract);
},
() => LocatePageFor(modalViewModel, contract), // ignore withNavigationPage, not necessary for UWP.
CurrentThreadScheduler.Instance)
.ObserveOn(CurrentThreadScheduler.Instance)
.SelectMany(
@ -311,16 +296,18 @@ namespace Sextant
{
_mirroredModalStack.Push(modalViewModel);
if (_contentDialog != null && ModalVisible)
if (_contentDialog is not null && ModalVisible)
{
_contentDialog.Hide();
}
_contentDialog = new ContentDialog();
_contentDialog.FullSizeDesired = true;
_contentDialog.IsPrimaryButtonEnabled = false;
_contentDialog.IsSecondaryButtonEnabled = false;
_contentDialog.Content = page;
_contentDialog = new ContentDialog
{
FullSizeDesired = true,
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
Content = page
};
_ = _contentDialog.ShowAsync();
@ -335,11 +322,7 @@ namespace Sextant
bool animate) =>
Observable
.Start(
() =>
{
// ignore withNavigationPage, not necessary for UWP.
return LocatePageTypeFor(viewModel, contract);
},
() => LocatePageTypeFor(viewModel, contract), // ignore withNavigationPage, not necessary for UWP.
CurrentThreadScheduler.Instance)
.ObserveOn(CurrentThreadScheduler.Instance)
.SelectMany(
@ -351,13 +334,14 @@ namespace Sextant
_mirroredPageStack.Push(viewModel);
mainFrame.Navigate(pageType, null, new SuppressNavigationTransitionInfo());
if (mainFrame.Content is IViewFor)
if (mainFrame.Content is IViewFor viewForReset)
{
((IViewFor)mainFrame.Content).ViewModel = viewModel;
viewForReset.ViewModel = viewModel;
}
else
{
_logger.Debug($"The view ({mainFrame.Content.GetType()}) does not implement IViewFor<>. Cannot set ViewModel of type, {viewModel.GetType()}, on view.");
var contentTypeName = mainFrame.Content?.GetType().ToString() ?? "Unknown Type";
_logger.Debug($"The view ({contentTypeName}) does not implement IViewFor<>. Cannot set ViewModel of type, {viewModel.GetType()}, on view.");
}
mainFrame.BackStack.Clear();
@ -378,41 +362,26 @@ namespace Sextant
_mirroredPageStack.Push(viewModel);
mainFrame.Navigate(pageType, null, animation);
if (mainFrame.Content is IViewFor)
if (mainFrame.Content is IViewFor viewFor)
{
((IViewFor)mainFrame.Content).ViewModel = viewModel;
viewFor.ViewModel = viewModel;
}
else
{
_logger.Debug($"The view ({mainFrame.Content.GetType()}) does not implement IViewFor<>. Cannot set ViewModel of type, {viewModel.GetType()}, on view.");
var contentTypeName = mainFrame.Content?.GetType().ToString() ?? "Unknown Type";
_logger.Debug($"The view ({contentTypeName}) does not implement IViewFor<>. Cannot set ViewModel of type, {viewModel.GetType()}, on view.");
}
return Observable.Return(Unit.Default);
});
private IViewModel? CurrentViewModel() => (IViewModel?)(mainFrame.Content as IViewFor)?.ViewModel;
private IView LocateNavigationFor(IViewModel viewModel)
{
var view = _viewLocator.ResolveView(viewModel, "NavigationView");
var navigationPage = view as IView;
if (navigationPage is null)
{
_logger.Debug($"No navigation view could be located for type '{viewModel.GetType().FullName}', using the default navigation page.");
navigationPage = Locator.Current.GetService<IView>(nameof(NavigationView)) ?? Locator.Current.GetService<IView>();
}
return navigationPage;
}
private Type LocatePageTypeFor(object viewModel, string? contract)
private static Type LocatePageTypeFor(object viewModel, string? contract)
{
var uwpViewTypeResolver = Locator.Current.GetService<ViewTypeResolver>(contract);
var viewType = uwpViewTypeResolver.ResolveViewType(viewModel.GetType());
if (viewType == null)
if (viewType is null)
{
throw new InvalidOperationException($"No view could be located for type '{viewModel.GetType().FullName}', contract '{contract}'. Be sure Splat has an appropriate registration.");
}
@ -425,17 +394,17 @@ namespace Sextant
var view = _viewLocator.ResolveView(viewModel, contract);
var page = view as Page;
if (view == null)
if (view is null)
{
throw new InvalidOperationException($"No view could be located for type '{viewModel.GetType().FullName}', contract '{contract}'. Be sure Splat has an appropriate registration.");
}
if (view == null)
if (view is null)
{
throw new InvalidOperationException($"Cannot find view for '{viewModel.GetType().FullName}', contract '{contract}' does not implement IViewFor.");
}
if (page == null)
if (page is null)
{
throw new InvalidOperationException($"Resolved view '{view.GetType().FullName}' for type '{viewModel.GetType().FullName}', contract '{contract}' is not a Page.");
}

Просмотреть файл

@ -30,8 +30,8 @@ namespace Sextant
private readonly IScheduler _backgroundScheduler;
private readonly IScheduler _mainScheduler;
private readonly IViewLocator _viewLocator;
private readonly Stack<UIViewController> _navigationPages = new Stack<UIViewController>();
private readonly Subject<IViewModel?> _pagePopped = new Subject<IViewModel?>();
private readonly Stack<UIViewController> _navigationPages = new();
private readonly Subject<IViewModel?> _pagePopped = new();
/// <summary>
/// Initializes a new instance of the <see cref="NavigationViewController" /> class.
@ -110,9 +110,8 @@ namespace Sextant
IViewModel viewModel,
string? contract,
bool resetStack,
bool animate = true)
{
return Observable.Start(
bool animate = true) =>
Observable.Start(
() =>
{
var page = LocatePageFor(viewModel, contract);
@ -150,7 +149,6 @@ namespace Sextant
return Disposable.Empty;
});
});
}
/// <inheritdoc/>
public override UIViewController PopViewController(bool animated)
@ -168,13 +166,13 @@ namespace Sextant
var viewFor = _viewLocator.ResolveView(viewModel, contract);
var page = viewFor as UIViewController;
if (viewFor == null)
if (viewFor is null)
{
throw new InvalidOperationException(
$"No view could be located for type '{viewModel.GetType().FullName}', contract '{contract}'. Be sure Splat has an appropriate registration.");
}
if (page == null)
if (page is null)
{
throw new InvalidOperationException(
$"Resolved view '{viewFor.GetType().FullName}' for type '{viewModel.GetType().FullName}', contract '{contract}' is not a Page.");
@ -185,9 +183,6 @@ namespace Sextant
return page;
}
private void SetPageTitle(UIViewController page, string resourceKey)
{
page.Title = resourceKey;
}
private void SetPageTitle(UIViewController page, string resourceKey) => page.Title = resourceKey;
}
}

Просмотреть файл

@ -14,20 +14,18 @@ namespace Sextant
/// </summary>
public class Sextant
{
private static readonly Lazy<Sextant> _sextant = new Lazy<Sextant>();
private static readonly Lazy<Sextant> _sextant = new();
static Sextant()
{
static Sextant() =>
Locator.RegisterResolverCallbackChanged(() =>
{
if (Locator.CurrentMutable == null)
if (Locator.CurrentMutable is null)
{
return;
}
Instance.Initialize();
});
}
/// <summary>
/// Gets the instance of <see cref="Sextant"/>.

Просмотреть файл

@ -1,20 +1,19 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;Xamarin.iOS10</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461;uap10.0.16299</TargetFrameworks>
<TargetFrameworks>netstandard2.0;Xamarin.iOS10;Xamarin.TVOS10;net5.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net461;net472;uap10.0.16299</TargetFrameworks>
<AssemblyName>Sextant</AssemblyName>
<RootNamespace>Sextant</RootNamespace>
<PackageId>Sextant</PackageId>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<WarningsAsErrors>CS8625;CS8604;CS8600;CS8614;CS8603;CS8618;CS8619</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Platforms\**\*.cs" />
<None Include="Platforms\**\*.cs" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="ReactiveUI" Version="12.*" />
<PackageReference Include="ReactiveUI" Version="13.*" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
@ -24,8 +23,29 @@
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net461')) ">
<Compile Include="Platforms\net461\**\*.cs" />
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.WatchOS')) ">
<Compile Include="Platforms\apple-common\**\*.cs" />
<Compile Include="Platforms\ios\**\*.cs" />
<Compile Include="Platforms\uikit-common\**\*.cs" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.TVOS')) ">
<Compile Include="Platforms\apple-common\**\*.cs" />
<Compile Include="Platforms\ios\**\*.cs" />
<Compile Include="Platforms\uikit-common\**\*.cs" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.Mac')) ">
<Compile Include="Platforms\apple-common\**\*.cs" />
<Compile Include="Platforms\ios\**\*.cs" />
<Compile Include="Platforms\uikit-common\**\*.cs" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) ">
<Compile Include="Platforms\net4\**\*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap')) ">
@ -37,4 +57,8 @@
<Compile Include="Platforms\netstandard2.0\**\*.cs" />
</ItemGroup>
</Project>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net5')) ">
<Compile Include="Platforms\net5\**\*.cs" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,13 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=abstractions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=navigation/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cnet4/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cnet5/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cnetstandard2_002E0/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cuap/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cuap_005Cmixins/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=platforms_005Cuikit_002Dcommon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=system/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=system_005Creactive/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=system_005Creactive_005Clinq/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Просмотреть файл

@ -28,9 +28,8 @@ namespace System.Reactive.Linq
this IObservable<T> observable,
[CallerMemberName]string? callerMemberName = null,
[CallerFilePath]string? callerFilePath = null,
[CallerLineNumber]int callerLineNumber = 0)
{
return observable
[CallerLineNumber]int callerLineNumber = 0) =>
observable
.Subscribe(
_ => { },
ex =>
@ -40,6 +39,5 @@ namespace System.Reactive.Linq
Debugger.Break();
});
}
}
}

Просмотреть файл

@ -20,7 +20,7 @@ namespace System.Reactive.Linq
[Obsolete("This extension method causes conflicts in the System.Reactive.Linq namespace")]
public static IObservable<Unit> ToSignal<T>(this IObservable<T> observable)
{
if (observable == null)
if (observable is null)
{
throw new ArgumentNullException(nameof(observable));
}

Просмотреть файл

@ -17,9 +17,6 @@ namespace System.Reactive.Linq
/// <param name="observable">The observable to add the condition to.</param>
/// <returns>An observable which will not signal unless the value is not null.</returns>
[Obsolete("This extension method causes conflicts in the System.Reactive.Linq namespace")]
public static IObservable<T> WhereNotNull<T>(this IObservable<T> observable)
{
return observable.Where(x => x != null);
}
public static IObservable<T> WhereNotNull<T>(this IObservable<T?> observable) => observable.Where(x => x is not null).Select(x => x!);
}
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace Sextant
get
{
var locator = Locator.Current.GetService<IViewModelFactory>();
if (locator == null)
if (locator is null)
{
throw new ViewModelFactoryNotFoundException("Could not find a default ViewModelFactory. This should never happen, your dependency resolver is broken");
}