.NET Maui - Calculadora IMC


  Hoje veremos como criar uma calculadora IMC simples usando MVVM no .NET MAUI.

Se você esta chegando agora e não sabe o que é o .NET MAUI nem como usá-lo acompanhe o meu artigo :  Criando seu primeiro projeto  onde eu mostro como verificar e configurar o ambiente para criar sua primeira aplicação .NET MAUI.

Model-View-ViewModel

O padrão Model-View-ViewModel (MVVM) impõe uma separação entre três camadas de software — a interface do usuário XAML, chamada de exibição, os dados subjacentes, chamados de modelo, e um intermediário entre a exibição e o modelo, chamado de viewmodel.

A view e a viewmodel geralmente são conectadas por meio de associações de dados(data binding) definidas no código XAML. O BindingContext para a view geralmente é uma instância da viewmodel.

Model

A camada de Model é responsável pelos dados e pela lógica de negócios do aplicativo. Ela contém as classes que definem as estruturas de dados e as regras de negócios.

View

A camada de View é responsável pela interface do usuário e pela interação do usuário com o aplicativo. Ela contém as classes que definem a interface gráfica do usuário, como os controles, botões, menus, etc.

ViewModel

A camada ViewModel é responsável por conectar a View e o Model. Ela contém as classes que implementam a lógica de apresentação, incluindo a validação dos dados, formatação, conversão de tipos e ações do usuário. O ViewModel também implementa a interface INotifyPropertyChanged para notificar a View sobre as alterações nos dados do Model e contém a lógica que traduz os dados do Modelo para a View e vice-versa.

Para mostrar um exemplo prático de aplicação do padrão MVVM vamos criar uma calculadora de IMC (Índice de Massa Corpórea) no .NET MAUI.

Criando o projeto .NET MAUI

A seguir vou mostrar alguns exemplos usando FlexLayout. Para isso vamos criar um novo projeto no VS 2022 usando o template .NET MAUI App chamado CalculadoraIMC :

No projeto criado vamos incluir uma classe chamada MainPageViewModel que representa a nossa viewmodel. Aqui vamos definir as propriedades Peso e Altura e a propriedade Classificacao que permite o obter o valor do IMC calculado.

using System.ComponentModel;
namespace
CalculadoraIMC;

internal class MainPageViewModel : INotifyPropertyChanged
{
 
private double peso = 75;
 
private double altura = 165;
 
private const double Fase = 1.0;

  public double Altura
  {
   
get => altura;
   
set
    {
       altura = ProximaFase(value);
       AtualizaIMC();
    }
  }

  public double Peso
  {
   
get => peso;
   
set
    {
      peso = ProximaFase(value);
      AtualizaIMC();
    }
   }

  public double Imc => Math.Round(Peso / Math.Pow(Altura / 100, 2), 2);

  public string Classificacao
  {
  
 get
    {
     
if (Imc < 18.5)
       
return "Você está abaixo do peso";
     
else if (Imc < 25)
       
return "Seu peso está normal";
     
else if (Imc < 30)
       
return "Você está com sobrepeso";
     
else if (Imc < 40)
       
return "Você está obeso";
     
else
        return
"Você está MUITO obeso";
    }
   }

   private void AtualizaIMC()
   {
     RaisePropertyChanged(nameof(Imc));
     RaisePropertyChanged(nameof(Classificacao));
   }

   public event PropertyChangedEventHandler PropertyChanged;
  
private void RaisePropertyChanged(string v)
   {
     PropertyChanged?.Invoke(
this, new PropertyChangedEventArgs(v));
   }

   private double ProximaFase(double valor) => Math.Round(valor / Fase) * Fase;
}


Destaques deste código:

A interface INotifyPropertyChanged é fundamental na implantação do padrão MVVM no .NET MAUI, pois ela permite que a ViewModel notifique a view quando ocorrem alterações nos dados do Modelo. Sem essa interface, a visualização não seria atualizada quando as propriedades do Modelo fossem modificadas.

A declaração de evento PropertyChanged do tipo PropertyChangedEventHandler cria um evento que é disparado sempre que uma propriedade do ViewModel é alterada. Esse evento é responsável por notificar a visualização sobre a alteração para que ela possa atualizar sua interface de usuário correspondente.

O método RaisePropertyChanged é chamado sempre que uma propriedade do ViewModel é modificada e é responsável por disparar o evento PropertyChanged, notificando assim a visualização sobre a alteração. O método recebe como parâmetro o nome da propriedade que foi modificada (v), que será incluído no argumento PropertyChangedEventArgs passado para o evento. Isso permite que a view saiba qual propriedade foi modificada e possa atualizar sua interface de usuário correspondente de acordo.

O operador "?." é usado para verificar se o evento PropertyChanged não é nulo antes de invocá-lo. Isso é uma precaução para garantir que o evento só seja invocado se houver pelo menos um manipulador de eventos registrado para ele. Caso contrário, uma exceção NullReferenceException seria lançada.

A seguir vamos definir no arquivo MainPage.xaml o código que faz a vinculação com as propriedades definidas na viewmodel alterando o seu código conforme abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CalculadoraIMC.MainPage">   

    <ContentPage.Resources>
        <Color x:Key="SliderColor">#ff8000</Color>
        <Color x:Key="TitleColor">#0080ff</Color>
        <Color x:Key="LabelColor">#00161f</Color>

        <Style x:Key="StyleLabel" TargetType="Label">
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="FontSize" Value="Title" />
            <Setter Property="FontAttributes" Value="Bold" />
            <Setter Property="TextColor" Value="{StaticResource LabelColor}" />
        </Style>

        <Style
            x:Key="TitleStyle"
            BasedOn="{StaticResource StyleLabel}"
            TargetType="Label">
            <Setter Property="FontSize" Value="Large" />
            <Setter Property="Padding" Value="40" />
        </Style>

        <Style
            x:Key="ValueStyle"
            BasedOn="{StaticResource StyleLabel}"
            TargetType="Label">
            <Setter Property="FontSize" Value="Title" />
            <Setter Property="TextColor" Value="{StaticResource LabelColor}" />
        </Style>

        <Style TargetType="Slider">
            <Setter Property="ThumbColor" Value="{StaticResource SliderColor}" />
            <Setter Property="MinimumTrackColor" Value="{StaticResource SliderColor}" />
            <Setter Property="MaximumTrackColor" Value="{StaticResource SliderColor}" />
        </Style>

    </ContentPage.Resources>

    <FlexLayout
        Padding="40"
        Direction="Column"
        JustifyContent="SpaceEvenly">

        <StackLayout>
            <Label Style="{StaticResource TitleStyle}" Text="Qual a sua altura ?" />
            <Label Style="{StaticResource ValueStyle}" Text="{Binding Source={x:Reference SliderPeso},
 Path=Value, StringFormat='{0:F0} cm'}" />
            <Slider
                x:Name="SliderAltura"
                Maximum="240"
                Minimum="35"
                Value="{Binding Altura}" />
        </StackLayout>

        <StackLayout>
            <Label Style="{StaticResource TitleStyle}" Text="Qual o seu peso ?" />
            <Label Style="{StaticResource ValueStyle}" Text="{Binding Source={x:Reference SliderPeso},
Path=Value, StringFormat='{0:F0} kg'}" />
            <Slider
                x:Name="SliderPeso"
                Maximum="320"
                Minimum="35"
                Value="{Binding Peso}" />
        </StackLayout>

        <StackLayout>
            <Label Style="{StaticResource StyleLabel}" Text="Seu IMC é : " />
            <Label Style="{StaticResource StyleLabel}" Text="{Binding Imc}" TextColor="Blue" />
            <Label Style="{StaticResource StyleLabel}" Text="{Binding Classificacao}" TextColor="Blue" />
        </StackLayout>
    </FlexLayout>

</ContentPage>


Este código XAML está definindo recursos (Resources) em um ContentPage, que é uma página de conteúdo no .NET MAUI. Os recursos definidos incluem três cores (SliderColor, TitleColor e LabelColor) e três estilos (StyleLabel, TitleStyle e ValueStyle) que podem ser usados em elementos de interface do usuário na página.

A cor SliderColor é usada como a cor de destaque de um Slider, um controle que permite que o usuário selecione um valor de um intervalo contínuo. A cor TitleColor é usada como a cor do título da página. A cor LabelColor é usada como a cor de texto para rótulos (Labels) em diferentes estilos.

Os estilos definidos incluem um estilo base (StyleLabel) para rótulos com algumas propriedades comuns, como HorizontalOptions, FontSize, FontAttributes e TextColor. O estilo TitleStyle é baseado no estilo StyleLabel e adiciona algumas propriedades extras, como um tamanho de fonte maior e um preenchimento (Padding) para o rótulo do título. O estilo ValueStyle também é baseado no estilo StyleLabel, mas define uma fonte de tamanho menor e uma cor de texto diferente.

Por fim, um estilo é definido para o controle Slider, que define a cor do polegar (ThumbColor) e das trilhas mínima e máxima (MinimumTrackColor e MaximumTrackColor) para a cor SliderColor definida anteriormente.

Ao definir esses recursos e estilos em um ContentPage, é possível usar esses recursos e estilos em vários elementos de interface do usuário na página, tornando a página mais consistente e fácil de manter.

A seguir usamos um layout FlexLayout onde estamos empilhando 3 StackLayout ara definir a interface com o usuário aplicando os recursos criados.

Executando o projeto iremos obter o seguinte resultado:

Pegue o projeto aqui :  CalculadoraIMC.zip (sem as referências)...

"Pois estou convencido de que nem morte nem vida, nem anjos nem demônios, nem o presente nem o futuro, nem quaisquer poderes,nem altura nem profundidade, nem qualquer outra coisa na criação será capaz de nos separar do amor de Deus que está em Cristo Jesus, nosso Senhor."
Romanos 8:38,39

Referências:


José Carlos Macoratti