C# Markup Hot Reload

Utilização Básica

Para usar .NET Hot Reload, enquanto depura ativamente a sua aplicação de .NET MAUI, modifique o seu código C# e depois clique no botão Aplicar Alterações de Código (também conhecido como o botão 🔥) na barra de ferramentas Visual Studio.

C# Hot Reload Demo

Warning

Ao modificar código da interface, o botão Aplicar Alterações ao Código (também conhecido como o 🔥 botão) atualiza imediatamente o código C# em execução, mas pode não atualizar imediatamente a sua interface (veja Uso Avançado). Isto deve-se ao facto de o .NET MAUI não ter conhecimento das alterações subjacentes que acabou de efetuar ao Intermediate Language em execução.

A boa notícia é que o código em execução foi mesmo atualizado, e só precisamos de dizer ao .NET MAUI para redesenhar a interface atualizada no ecrã

Uma solução simples consiste em forçar o .NET MAUI a atualizar a interface de utilizador, navegando para fora da página atual onde foi aplicada a alteração do .NET Hot Reload e regressando depois a essa página. Isto obriga o .NET MAUI a redesenhar a interface no ecrã.

Consulte Utilização Avançada para obter mais informações sobre como indicar automaticamente ao .NET MAUI que redesenhe a interface do utilizador atualizada.

Uso Avançado

Existe uma lacuna no ecossistema .NET entre .NET MAUI e .NET Hot Reload: a interface da tua aplicação .NET MAUI não atualiza automaticamente após pressionar o botão Aplicar Alterações de Código (também conhecido como o botão 🔥). Embora o seu código tenha sido atualizado na Intermediate Language subjacente à aplicação, nada instruiu o .NET MAUI a voltar a desenhar a interface de utilizador atualizada no ecrã.

A boa notícia é que podemos indicar manualmente ao .NET MAUI que redesenhe a interface de utilizador, implementando ICommunityToolkitHotReloadHandler e registando-o no contentor de Injeção de Dependências do .NET MAUI (example below).

MetadataUpdateHandler

Quando o .NET Hot Reload é executado, apresenta cada Type atualizado, fornecendo um Type[] via System.Reflection.Metadata.MetadataUpdateHandler.

O .NET MAUI C# Markup Community Toolkit apresenta os tipos alterados via ICommunityToolkitHotReloadHandler.OnHotReload(IReadOnlyList<Type> types).

Registar a sua implementação de ICommunityToolkitHotReloadHandler no contentor de Injeção de Dependências da .NET MAUI garante que o método OnHotReload será automaticamente acionado sempre que o botão Aplicar Alterações de Código (também conhecido como o botão 🔥) for pressionado:

builder.Services.AddSingleton<ICommunityToolkitHotReloadHandler, HotReloadHandler>();

Exemplo de Implementação de ICommunityToolkitHotReloadHandler

Este exemplo demonstra como implementar ICommunityToolkitHotReloadHandler para .NET MAUI dizer que deva redesenhar automaticamente a interface da sua aplicação quando o botão Aplicar Alterações de Código (também conhecido como o botão 🔥) é pressionado.

Observação

Este não é um exemplo abrangente que funcione para todas as aplicações. Este exemplo funcionará para a maioria das aplicações, mas como cada aplicação .NET MAUI é arquitetada + implementada de forma diferente, recomendamos modificar o código de exemplo para melhor funcionar para a sua base de código.

MauiProgram.cs

Em MauiProgram.cs, adicione a sua implementação de ICommunityToolkitHotReloadHandler ao contentor de Injeção de Dependências da .NET MAUI:

public class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		// ...
        // Additional code ommitted for brevity
        // ...

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

		// ...
        // Additional code ommitted for brevity
        // ...
	}
}

HotReloadHandler.cs

Na implementação de ICommunityToolkitHotReloadHandler, indicamos ao .NET MAUI que redesenhe a interface do utilizador no ecrã.

O exemplo abaixo trata tanto de arquiteturas Shell como não Shell e inclui suporte para páginas apresentadas de forma modal.

Observação

Este não é um exemplo abrangente que funcione para todas as aplicações. Este exemplo funcionará para a maioria das aplicações, mas como cada aplicação .NET MAUI é arquitetada + implementada de forma diferente, recomendamos modificar o código de exemplo para melhor funcionar para a sua base de código.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace CommunityToolkit.Maui.Markup.Sample;

class HotReloadHandler : ICommunityToolkitHotReloadHandler
{
	public async void OnHotReload(IReadOnlyList<Type> types)
	{
		if (Application.Current?.Windows is null)
		{
			Trace.WriteLine($"{nameof(HotReloadHandler)} Failed: {nameof(Application)}.{nameof(Application.Current)}.{nameof(Application.Current.Windows)} is null");
			return;
		}

		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;
	}
}