Partilhar via


Adicionar injeção de dependência

A injeção de dependência (DI) ajuda você a gerenciar o ciclo de vida de seus ViewModels e serviços. Isso torna seu código mais testável e fácil de manter. Nesta etapa, você configura a DI em seu aplicativo e atualiza seus modelos para usar um serviço de arquivo para operações de arquivo.

Para obter mais informações sobre a estrutura de injeção de dependência do .NET, consulte Injeção de dependência do .NET e o tutorial Usar injeção de dependência no .NET .

Instalar os pacotes Microsoft.Extensions

Adicione suporte de DI aos seus projetos.

  1. Instale Microsoft.Extensions.DependencyInjection em projetos WinUINotes e WinUINotes.Bus :

    dotnet add WinUINotes package Microsoft.Extensions.DependencyInjection
    dotnet add WinUINotes.Bus package Microsoft.Extensions.DependencyInjection
    

Criar uma interface de serviço de arquivo e implementação

  1. No projeto WinUINotes.Bus , crie uma nova pasta chamada Services.

  2. Adicione um ficheiro de interface IFileService.cs:

    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Windows.Storage;
    
    namespace WinUINotes.Services
    {
        public interface IFileService
        {
            Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync();
            Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync(IStorageFolder storageFolder);
            Task<string> GetTextFromFileAsync(IStorageFile file);
            Task CreateOrUpdateFileAsync(string filename, string contents);
            Task DeleteFileAsync(string filename);
            bool FileExists(string filename);
            IStorageFolder GetLocalFolder();
        }
    }
    

    A interface de serviço de arquivo define métodos para operações de arquivo. Abstrai os detalhes da manipulação de arquivos nos ViewModels e Models. Os parâmetros e valores de retorno são todos tipos básicos de .NET ou interfaces. Este design garante que o serviço possa ser facilmente simulado ou substituído em testes de unidade, promovendo acoplamento solto e estabilidade.

  3. Adicione o arquivo WindowsFileService.csde implementação :

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Windows.Storage;
    
    namespace WinUINotes.Services
    {
        public class WindowsFileService : IFileService
        {
             public StorageFolder storageFolder;
    
             public WindowsFileService(IStorageFolder storageFolder)
             {
                 this.storageFolder = (StorageFolder)storageFolder;
    
                 if (this.storageFolder is null)
                 {
                     throw new ArgumentException("storageFolder must be of type StorageFolder", nameof(storageFolder));
                 }
             }
    
             public async Task CreateOrUpdateFileAsync(string filename, string contents)
             {
                 // Save the note to a file.
                 StorageFile storageFile = (StorageFile)await storageFolder.TryGetItemAsync(filename);
                 if (storageFile is null)
                 {
                     storageFile = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
                 }
                 await FileIO.WriteTextAsync(storageFile, contents);
             }
    
         public async Task DeleteFileAsync(string filename)
         {
             // Delete the note from the file system.
             StorageFile storageFile = (StorageFile)await storageFolder.TryGetItemAsync(filename);
             if (storageFile is not null)
             {
                 await storageFile.DeleteAsync();
             }
         }
    
         public bool FileExists(string filename)
         {
             StorageFile storageFile = (StorageFile)storageFolder.TryGetItemAsync(filename).AsTask().Result;
             return storageFile is not null;
         }
    
         public IStorageFolder GetLocalFolder()
         {
             return storageFolder;
         }
    
         public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync()
         {
             return await storageFolder.GetItemsAsync();
         }
    
         public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync(IStorageFolder folder)
         {
             return await folder.GetItemsAsync();
         }
    
         public async Task<string> GetTextFromFileAsync(IStorageFile file)
         {
             return await FileIO.ReadTextAsync(file);
         }
        }
    }
    

A WindowsFileService implementação fornece operações de arquivo concretas usando o Tempo de Execução do Windows (WinRT) e APIs de armazenamento .NET:

  • Injeção do construtor: O serviço aceita um IStorageFolder em seu construtor. Essa abordagem permite configurar o local de armazenamento quando você instancia o serviço. Essa abordagem torna o serviço flexível e testável.
  • CreateOrUpdateFileAsync(): Este método é usado TryGetItemAsync() para verificar se um arquivo já existe. Se isso acontecer, o método atualiza o arquivo existente. Caso contrário, ele criará um novo arquivo usando CreateFileAsync(). Essa abordagem lida com cenários de criação e atualização em um único método.
  • DeleteFileAsync(): Antes de excluir um arquivo, esse método verifica se o arquivo existe usando TryGetItemAsync(). Essa verificação impede que exceções sejam lançadas ao tentar excluir arquivos inexistentes.
  • FileExists(): Este método síncrono verifica a existência do arquivo chamando o async TryGetItemAsync() e bloqueando com .Result. Embora essa abordagem geralmente não seja recomendada, ela é usada aqui para dar suporte CanDelete() ao método de validação no ViewModel, que deve ser síncrono.
  • Métodos de item de armazenamento: Os GetStorageItemsAsync() métodos e GetTextFromFileAsync() fornecem acesso a arquivos e seu conteúdo usando APIs de armazenamento do WinRT. Esses métodos permitem que os modelos carreguem e enumerem notas.

Ao implementar a interface, você pode facilmente substituir essa classe por uma implementação simulada IFileService para teste ou um provedor de armazenamento diferente, se necessário.

Saiba mais nos documentos:

Configurar a injeção de dependência no App.xaml.cs

Antes de atualizar os modelos e ViewModels para usar o serviço de arquivo, configure a injeção de dependência para que o serviço possa ser resolvido e injetado nos construtores.

Atualize o App.xaml.cs arquivo para configurar o contêiner DI:

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using WinUINotes.ViewModels;

namespace WinUINotes;

public partial class App : Application
{
    private readonly IServiceProvider _serviceProvider;

    public App()
    {
        Services = ConfigureServices();
        this.InitializeComponent();
    }

    private static IServiceProvider ConfigureServices()
    {
        var services = new ServiceCollection();

        // Services
        services.AddSingleton<Services.IFileService>(x =>
            ActivatorUtilities.CreateInstance<Services.WindowsFileService>(x,
                            Windows.Storage.ApplicationData.Current.LocalFolder)
        );

        // ViewModels
        services.AddTransient<AllNotesViewModel>();
        services.AddTransient<NoteViewModel>();

        return services.BuildServiceProvider();
    }

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        m_window.Activate();
    }

    public IServiceProvider Services { get; }

    private Window? m_window;

    public new static App Current => (App)Application.Current;
}

Essa configuração configura o contêiner de injeção de dependência com todos os serviços necessários:

  • ConfigureServices() método: um método estático que cria e configura a coleção de serviços. Separar esse método torna a configuração mais fácil de manter e testar.
  • Services property: uma propriedade de instância que mantém o IServiceProvider. O construtor define essa propriedade chamando ConfigureServices().
  • App.Current propriedade estática: fornece acesso conveniente à instância atual App , o que é útil quando modelos ou outras classes precisam acessar o provedor de serviços.
  • IFileService registo: Usa ActivatorUtilities.CreateInstance para criar uma instância de WindowsFileService com ApplicationData.Current.LocalFolder como parâmetro. Essa abordagem permite que o parâmetro do construtor seja injetado no momento do registro. Registe o serviço como um singleton, pois as operações de ficheiro são sem estado e uma única instância pode ser partilhada em toda a aplicação.
  • Registro de ViewModels: registre ambos os ViewModels como transitórios, o que significa que uma nova instância é criada sempre que uma é solicitada. Essa abordagem garante que cada página obtenha sua própria instância ViewModel com estado limpo.

Modelos e outras classes podem aceder ao provedor de serviços através de App.Current.Services.GetService() para recuperar serviços registrados quando necessário.

Saiba mais nos documentos:

Atualizar modelos para usar o serviço de arquivo

Agora que o serviço de arquivo está disponível por meio de injeção de dependência, atualize as classes de modelo para usá-lo. Os modelos recebem o serviço de ficheiros e utilizam-no em todas as operações com ficheiros.

Atualizar o modelo de notas

Atualize a Note classe para aceitar o serviço de arquivo e usá-lo para salvar, excluir e operações de existência de arquivo:

using System;
using System.Threading.Tasks;
using WinUINotes.Services;

namespace WinUINotes.Models;

public class Note
{
    private IFileService fileService;
    public string Filename { get; set; } = string.Empty;
    public string Text { get; set; } = string.Empty;
    public DateTime Date { get; set; } = DateTime.Now;

    public Note(IFileService fileService)
    {
        Filename = "notes" + DateTime.Now.ToBinary().ToString() + ".txt";
        this.fileService = fileService;
    }

    public async Task SaveAsync()
    {
        await fileService.CreateOrUpdateFileAsync(Filename, Text);
    }

    public async Task DeleteAsync()
    {
        await fileService.DeleteFileAsync(Filename);
    }

    public bool NoteFileExists()
    {
        return fileService.FileExists(Filename);
    }
}

O Note modelo agora recebe o serviço de arquivo por meio da injeção do construtor:

  • Construtor: Aceita um IFileService parâmetro, tornando a dependência explícita e necessária. Esse design promove a capacidade de teste e garante que o modelo sempre tenha acesso ao serviço de arquivos de que precisa.
  • Geração de nome de arquivo: O construtor gera automaticamente um nome de arquivo exclusivo usando o carimbo de data/hora atual, garantindo que cada nota tenha um nome de arquivo distinto.
  • Operações de arquivo: Os SaveAsync(), DeleteAsync(), e NoteFileExists() todos os métodos delegam ao serviço de arquivo injetado, mantendo o modelo focado na coordenação de operações em vez de implementar detalhes de E/S de arquivo.

Essa abordagem elimina a necessidade de o modelo usar o padrão do localizador de serviços (acessando App.Services diretamente), o que melhora a capacidade de teste e torna as dependências claras.

Atualizar o modelo AllNotes

Atualize a AllNotes classe para carregar notas do armazenamento usando o serviço de arquivo:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.Storage;
using WinUINotes.Services;

namespace WinUINotes.Models;

public class AllNotes
{
        private IFileService fileService;
        public ObservableCollection<Note> Notes { get; set; } = [];

        public AllNotes(IFileService fileService)
        {
            this.fileService = fileService;
        }

        public async Task LoadNotes()
        {
            Notes.Clear();
            await GetFilesInFolderAsync(fileService.GetLocalFolder());
        }

        private async Task GetFilesInFolderAsync(IStorageFolder folder)
        {
            // Each StorageItem can be either a folder or a file.
            IReadOnlyList<IStorageItem> storageItems =
                                        await fileService.GetStorageItemsAsync(folder);
            foreach (IStorageItem item in storageItems)
            {
                if (item.IsOfType(StorageItemTypes.Folder))
                {
                    // Recursively get items from subfolders.
                    await GetFilesInFolderAsync((IStorageFolder)item);
                }
                else if (item.IsOfType(StorageItemTypes.File))
                {
                    IStorageFile file = (IStorageFile)item;
                    Note note = new(fileService)
                    {
                        Filename = file.Name,
                        Text = await fileService.GetTextFromFileAsync(file),
                        Date = file.DateCreated.DateTime
                    };
                    Notes.Add(note);
                }
            }
        }
}

O AllNotes modelo recebe o serviço de arquivo por meio da injeção do construtor, assim como o Note modelo. Como essa classe está no WinUINotes.Bus projeto, ela não pode acessar App.Current.Services a partir do WinUINotes projeto (devido a restrições de referência do projeto).

O LoadNotes() método chama o método privado GetFilesInFolderAsync() para enumerar recursivamente todos os arquivos na pasta de armazenamento local e suas subpastas. Para cada item de armazenamento:

  1. Se for uma pasta, o método se chama recursivamente para processar o conteúdo da pasta
  2. Se for um arquivo, ele cria uma nova Note instância com o serviço de arquivo injetado
  3. A nota Filename é definida como o nome do ficheiro
  4. A nota é Text preenchida lendo o conteúdo do ficheiro usando GetTextFromFileAsync()
  5. A nota Date está definida para a data de criação do ficheiro
  6. A nota é adicionada à coleção observável Notes

Essa abordagem garante que todas as anotações carregadas do armazenamento tenham acesso ao serviço de arquivos de que precisam para futuras operações de salvamento e exclusão.

Atualizar ViewModels para usar o serviço de arquivo

Com os modelos agora usando o serviço de arquivo, você precisa atualizar o ViewModels. No entanto, como os modelos lidam diretamente com as operações de arquivo, os ViewModels se concentram principalmente em orquestrar os modelos e gerenciar propriedades observáveis.

Atualizar AllNotesViewModel

Atualize o AllNotesViewModel para trabalhar com o modelo atualizado AllNotes :

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using WinUINotes.Models;
using WinUINotes.Services;

namespace WinUINotes.ViewModels
{
    public partial class AllNotesViewModel : ObservableObject
    {
        private readonly AllNotes allNotes;

        [ObservableProperty]
        private ObservableCollection<Note> notes;

        public AllNotesViewModel(IFileService fileService)
        {
            allNotes = new AllNotes(fileService);
            notes = new ObservableCollection<Note>();
        }

        [RelayCommand]
        public async Task LoadAsync()
        {
            await allNotes.LoadNotes();
            Notes.Clear();
            foreach (var note in allNotes.Notes)
            {
                Notes.Add(note);
            }
        }
    }
}

O que mudou desde o Passo 2?

A principal alteração é a adição do IFileService parâmetro ao construtor. Na etapa 2, o ViewModel instanciou AllNotes com um construtor sem parâmetros (allNotes = new AllNotes()). Agora que o modelo AllNotes requer o serviço de ficheiros para executar as suas operações, o ViewModel recebe a IFileService através da injeção no construtor e passa-a para o modelo.

Esta alteração mantém o fluxo de dependência adequado - o serviço de arquivo é injetado no nível mais alto (modelo de visualização) e flui para baixo até o modelo. O ViewModel continua a se concentrar em coordenar o processo de carregamento e manter a coleção observável Notes sincronizada com os dados do modelo, sem a necessidade de saber os detalhes da implementação de como os arquivos são carregados.

Atualizar NoteViewModel

Atualize o NoteViewModel para injetar o serviço de arquivo e use o sistema de mensagens do MVVM Toolkit:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using System;
using System.Threading.Tasks;
using WinUINotes.Models;
using WinUINotes.Services;

namespace WinUINotes.ViewModels
{
    public partial class NoteViewModel : ObservableObject
    {
        private Note note;
        private IFileService fileService;

        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
        [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
        private string filename = string.Empty;

        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
        private string text = string.Empty;

        [ObservableProperty]
        private DateTime date = DateTime.Now;

        public NoteViewModel(IFileService fileService)
        {
            this.fileService = fileService;
            this.note = new Note(fileService);
            this.Filename = note.Filename;
        }

        public void InitializeForExistingNote(Note note)
        {
            this.note = note;
            this.Filename = note.Filename;
            this.Text = note.Text;
            this.Date = note.Date;
        }

        [RelayCommand(CanExecute = nameof(CanSave))]
        private async Task Save()
        {
            note.Filename = this.Filename;
            note.Text = this.Text;
            note.Date = this.Date;
            await note.SaveAsync();

            // Check if the DeleteCommand can now execute
            // (it can if the file now exists)
            DeleteCommand.NotifyCanExecuteChanged();
        }

        private bool CanSave()
        {
            return note is not null
                && !string.IsNullOrWhiteSpace(this.Text)
                && !string.IsNullOrWhiteSpace(this.Filename);
        }

        [RelayCommand(CanExecute = nameof(CanDelete))]
        private async Task Delete()
        {
            await note.DeleteAsync();
            note = new Note(fileService);
            // Send a message from some other module
            WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note));
        }

        private bool CanDelete()
        {
            // Note: This is to illustrate how commands can be
            // enabled or disabled.
            // In a real application, you shouldn't perform
            // file operations in your CanExecute logic.
            return note is not null
                && !string.IsNullOrWhiteSpace(this.Filename)
                && this.note.NoteFileExists();
        }
    }
}

O que mudou desde o Passo 2?

Várias alterações importantes suportam a injeção de dependência e a comunicação entre ViewModel:

  1. Injeção de serviço de arquivo: O construtor agora aceita IFileService como parâmetro e o armazena em um campo. Esse serviço é passado para o Note modelo ao criar novas instâncias, garantindo que todas as notas possam executar operações de arquivo.

  2. WeakReferenceMessenger: O Delete() método agora usa o MVVM Toolkit WeakReferenceMessenger.Default.Send() para transmitir uma NoteDeletedMessage após a exclusão de uma nota. Essa abordagem permite o acoplamento flexível entre ViewModels - outras partes do aplicativo (como NotePage) podem ouvir essa mensagem e responder adequadamente (por exemplo, navegando de volta para a lista de notas, que foi atualizada) sem a NoteViewModel necessidade de uma referência direta a elas.

O WeakReferenceMessenger é um recurso chave do MVVM Toolkit que evita vazamentos de memória usando referências fracas. Os componentes podem assinar mensagens sem criar referências fortes que impediriam a coleta de lixo.

Saiba mais nos documentos:

Criar a classe NoteDeletedMessage

O WeakReferenceMessenger precisa de uma classe de mensagem para enviar entre componentes. Crie uma nova classe para representar o evento de exclusão de nota:

  1. No projeto WinUINotes.Bus , adicione um novo arquivo NoteDeletedMessage.csde classe:

    using CommunityToolkit.Mvvm.Messaging.Messages;
    using WinUINotes.Models;
    
    namespace WinUINotes
    {
        public class NoteDeletedMessage : ValueChangedMessage<Note>
        {
            public NoteDeletedMessage(Note note) : base(note)
            {
            }
        }
    }
    

Esta classe de mensagem herda de ValueChangedMessage<Note>, que é um tipo de mensagem especializado fornecido pelo MVVM Toolkit para carregar notificações de alteração de valor. O construtor aceita um Note e o passa para a classe base, tornando-o disponível para destinatários de mensagens através da Value propriedade. Quando NoteViewModel envia esta mensagem, qualquer componente que se inscreva NoteDeletedMessage a recebe e pode acessar a nota excluída através da Value propriedade.

Como o sistema de mensagens funciona no MVVM Toolkit:

  1. Remetente: O NoteViewModel.Delete() método envia a mensagem usando WeakReferenceMessenger.Default.Send(new NoteDeletedMessage(note)).
  2. Destinatário: Páginas (como NotePage) podem se registrar para receber mensagens implementando IRecipient<NoteDeletedMessage> e registrando-se com o mensageiro. Quando a mensagem é recebida, a página pode navegar de volta para a lista de todas as notas.
  3. Acoplamento solto: O remetente não precisa saber quem (se alguém) está ouvindo. O recetor não precisa de uma referência direta ao remetente. Esta configuração mantém os componentes independentes e testáveis.

A abordagem de referência fraca significa que, se um componente for coletado pelo coletor de lixo, a sua assinatura de mensagem será limpa automaticamente, sem causar vazamentos de memória.

Atualizar páginas para usar a injeção de dependência

Atualize seus construtores de página para receber os ViewModels por meio de DI.

Atualizar AllNotesPage.xaml.cs

using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using WinUINotes.ViewModels;

namespace WinUINotes.Views
{
    public sealed partial class AllNotesPage : Page
    {
        private AllNotesViewModel? viewModel;

        public AllNotesPage()
        {
            this.InitializeComponent();
            viewModel = App.Current.Services.GetService<AllNotesViewModel>();
        }

        private void NewNoteButton_Click(object sender, RoutedEventArgs e)
        {
            Frame.Navigate(typeof(NotePage));
        }

        private void ItemsView_ItemInvoked(ItemsView sender, ItemsViewItemInvokedEventArgs args)
        {
            Frame.Navigate(typeof(NotePage), args.InvokedItem);
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if (viewModel is not null)
            {
                await viewModel.LoadAsync();
            }
        }
    }
}

O que mudou desde o Passo 2?

O aplicativo agora obtém o AllNotesViewModel do contêiner de injeção de dependência usando App.Current.Services.GetService<AllNotesViewModel>() em vez de criar diretamente com new AllNotesViewModel(). Esta abordagem tem várias vantagens:

  1. Resolução automática de dependência: O contêiner DI fornece automaticamente a IFileService dependência que AllNotesViewModel requer em seu construtor.
  2. Gerenciamento do ciclo de vida: O contêiner DI gerencia o ciclo de vida do ViewModel de acordo com a forma como ele foi registrado (como transitório, neste caso, fornecendo uma nova instância).
  3. Testabilidade: Este padrão facilita a troca de implementações ou dependências mockadas em testes.
  4. Capacidade de manutenção: Se as dependências do ViewModel mudarem no futuro, você só precisará atualizar a configuração de DI, não todos os locais onde o ViewModel é criado.

O resto do código permanece o mesmo. O OnNavigatedTo() método ainda chama LoadAsync() para atualizar a lista de anotações quando o usuário navega para esta página.

Atualizar NotePage.xaml.cs

using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using WinUINotes.Models;
using WinUINotes.ViewModels;

namespace WinUINotes.Views
{
    public sealed partial class NotePage : Page
    {
        private NoteViewModel? noteVm;

        public NotePage()
        {
            this.InitializeComponent();
        }

        public void RegisterForDeleteMessages()
        {
            WeakReferenceMessenger.Default.Register<NoteDeletedMessage>(this, (r, m) =>
            {
                if (Frame.CanGoBack)
                {
                    Frame.GoBack();
                }
            });
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            noteVm = App.Current.Services.GetService<NoteViewModel>();
            RegisterForDeleteMessages();

            if (e.Parameter is Note note && noteVm is not null)
            {
                noteVm.InitializeForExistingNote(note);
            }
        }
    }
}

O que mudou desde o Passo 2?

Várias alterações importantes integram recursos de injeção de dependência e mensagens:

  1. ViewModel do container DI: O NoteViewModel é agora obtido do container de injeção de dependência usando App.Current.Services.GetService<NoteViewModel>() no método OnNavigatedTo() em vez de ser instanciado diretamente. Essa abordagem garante que o ViewModel receba automaticamente sua dependência necessária IFileService .
  2. Registo de mensagens: o novo NoteDeletedMessage método inscreve-se usando o WeakReferenceMessenger. Quando uma nota é excluída (do NoteViewModel.Delete() método), esta página recebe a mensagem e navega de volta para a lista de todas as anotações usando Frame.GoBack().
  3. Padrão de mensagens: esse padrão demonstra o acoplamento solto habilitado pelo sistema de mensagens do MVVM Toolkit. O NoteViewModel não precisa saber sobre a navegação ou a estrutura da página - ele simplesmente envia uma mensagem quando uma nota é excluída e a página lida com a resposta de navegação de forma independente.
  4. Tempo de ciclo de vida: O ViewModel é instanciado e o registro de mensagens ocorre no OnNavigatedTo(), garantindo que tudo seja inicializado corretamente quando a página se tornar ativa.

Esse padrão separa as preocupações de forma eficaz: o ViewModel se concentra na lógica de negócios e nas operações de dados, enquanto a página lida com preocupações específicas da interface do usuário, como navegação.