Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for .NET Hot Reload #232

Merged
merged 23 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc30e70
Add CommunityToolkitMetadataUpdateHandler
TheCodeTraveler Jun 11, 2023
f1831b3
Include `Type[]` in `ReloadApplication` event
TheCodeTraveler Jun 13, 2023
22f22bb
Add `HotReloadHandler`
TheCodeTraveler Jun 13, 2023
ae1f9de
`dotnet format`
TheCodeTraveler Jun 14, 2023
8059a5c
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jun 27, 2023
0feaede
Change `Type[]` -> `IReadOnlyList<Type>`
TheCodeTraveler Jun 28, 2023
f683257
Update HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
691609b
Update HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
20571e0
Update CommunityToolkit.Maui.Markup.csproj
TheCodeTraveler Jun 28, 2023
b91d188
Update CommunityToolkitMetadataUpdateHandler.cs
TheCodeTraveler Jun 28, 2023
00744d6
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jun 28, 2023
07910a5
Update samples/CommunityToolkit.Maui.Markup.Sample/HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
5a5f7af
Add `ICommunityToolkitHotReloadHandler.cs`
TheCodeTraveler Jun 28, 2023
18c9ba9
Add `Window` support
TheCodeTraveler Jun 30, 2023
4877e48
Replace `#if DEBUG` with `[Conditional("DEBUG")]`
TheCodeTraveler Jun 30, 2023
2da5231
Reference `IServiceProvider` via `Application.Current?.Handler.MauiCo…
TheCodeTraveler Jun 30, 2023
ffcd077
Goto correct shell route
TheCodeTraveler Jun 30, 2023
793364e
Leverage overloaded method
TheCodeTraveler Jun 30, 2023
438ea6a
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jul 1, 2023
483733b
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jul 6, 2023
af30db7
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Sep 26, 2023
04bac84
Change `Debug.WriteLine` -> `Trace.WriteLine`
TheCodeTraveler Oct 1, 2023
7c9f3a5
Add Null Coalescing Operator to `Handler`
TheCodeTraveler Oct 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions samples/CommunityToolkit.Maui.Markup.Sample/AppShell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ public AppShell(NewsPage newsPage)
public static string GetRoute<TPage, TViewModel>() where TPage : BaseContentPage<TViewModel>
where TViewModel : BaseViewModel
{
if (!pageRouteMappingDictionary.TryGetValue(typeof(TPage), out var route))
return GetRoute(typeof(TPage));
}

public static string GetRoute(Type type)
{
if (!pageRouteMappingDictionary.TryGetValue(type, out var route))
{
throw new KeyNotFoundException($"No map for ${typeof(TPage)} was found on navigation mappings. Please register your ViewModel in {nameof(AppShell)}.{nameof(pageRouteMappingDictionary)}");
throw new KeyNotFoundException($"No map for ${type} was found on navigation mappings. Please register your ViewModel in {nameof(AppShell)}.{nameof(pageRouteMappingDictionary)}");
}

return route;
Expand Down
75 changes: 75 additions & 0 deletions samples/CommunityToolkit.Maui.Markup.Sample/HotReloadHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace CommunityToolkit.Maui.Markup.Sample;

class HotReloadHandler : ICommunityToolkitHotReloadHandler
{
public async void OnHotReload(IReadOnlyList<Type> types)
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
{
if (Application.Current?.Windows is null)
{
Debug.WriteLine($"{nameof(HotReloadHandler)} Failed: {nameof(Application)}.{nameof(Application.Current)}.{nameof(Application.Current.Windows)} is null");
return;
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
}

foreach (var window in Application.Current.Windows)
{
if (window.Page is not Page currentPage)
{
return;
}

foreach (var type in types)
{
if (type.IsSubclassOf(typeof(Page)))
{
if (window.Page is AppShell shell)
{
if (shell.CurrentPage is Page visiblePage
&& visiblePage.GetType() == type)
{
var currentPageShellRoute = AppShell.GetRoute(type);

await currentPage.Dispatcher.DispatchAsync(async () =>
{
await shell.GoToAsync(currentPageShellRoute, false);
shell.Navigation.RemovePage(visiblePage);
});

break;
}
}
else
{
if (TryGetModalStackPage(window, out var modalPage))
{
await currentPage.Dispatcher.DispatchAsync(async () =>
{
await currentPage.Navigation.PopModalAsync(false);
await currentPage.Navigation.PushModalAsync(modalPage, false);
});
}
else
{
await currentPage.Dispatcher.DispatchAsync(async () =>
{
await currentPage.Navigation.PopAsync(false);
await currentPage.Navigation.PushAsync(modalPage, false);
});
}

break;
}
}
}
}
}


static bool TryGetModalStackPage(Window window, [NotNullWhen(true)] out Page? page)
{
page = window.Navigation.ModalStack.LastOrDefault();
return page is not null;
}
}
3 changes: 3 additions & 0 deletions samples/CommunityToolkit.Maui.Markup.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public static MauiApp CreateMauiApp()
builder.Services.AddTransient<SettingsPage, SettingsViewModel>();
builder.Services.AddTransient<NewsDetailPage, NewsDetailViewModel>();

// C# Hot Reload Handler
builder.Services.AddSingleton<ICommunityToolkitHotReloadHandler, HotReloadHandler>();

return builder.Build();

static TimeSpan sleepDurationProvider(int attemptNumber) => TimeSpan.FromSeconds(Math.Pow(2, attemptNumber));
Expand Down
13 changes: 13 additions & 0 deletions src/CommunityToolkit.Maui.Markup/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ public static class AppBuilderExtensions
/// <returns><see cref="MauiAppBuilder"/> initialized for <see cref="CommunityToolkit.Maui.Markup"/></returns>
public static MauiAppBuilder UseMauiCommunityToolkitMarkup(this MauiAppBuilder builder)
{
RegisterReloadApplicationEventHandler();
return builder;
}

[System.Diagnostics.Conditional("DEBUG")]
static void RegisterReloadApplicationEventHandler()
{
CommunityToolkitMetadataUpdateHandler.ReloadApplication += ReloadApplication;
}

static void ReloadApplication(object? sender, IReadOnlyList<Type> e)
{
var hotReloadHandler = Application.Current?.Handler.MauiContext?.Services.GetService<ICommunityToolkitHotReloadHandler>();
hotReloadHandler?.OnHotReload(e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(CommunityToolkit.Maui.Markup.CommunityToolkitMetadataUpdateHandler))]
namespace CommunityToolkit.Maui.Markup;

static class CommunityToolkitMetadataUpdateHandler
{
static readonly WeakEventManager reloadApplicationEventHandler = new();

public static event EventHandler<IReadOnlyList<Type>> ReloadApplication
{
add => reloadApplicationEventHandler.AddEventHandler(value);
remove => reloadApplicationEventHandler.RemoveEventHandler(value);
}

[System.Diagnostics.Conditional("DEBUG")]
static void UpdateApplication(Type[]? types)
{
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
reloadApplicationEventHandler.HandleEvent(null, types?.ToList() ?? Enumerable.Empty<Type>(), nameof(ReloadApplication));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace CommunityToolkit.Maui.Markup;

/// <summary>
/// Interface for handling C# Hot Reload requests
/// </summary>
public interface ICommunityToolkitHotReloadHandler
{
/// <summary>
/// Executes when C# Hot Reload is invoked
/// </summary>
/// <param name="types">A <see cref="IReadOnlyList{Type}"/> contianing the types that have been changed in code.</param>
void OnHotReload(IReadOnlyList<Type> types);
}