MAUI MVVM 架构,在消耗 API 的同时创建登录系统

huangapple go评论70阅读模式
英文:

MAUI MVVM Architecture, doing a login system while consuming a API

问题

我正在尝试学习MAUI以创建一个项目,但似乎遇到了困难。我无法理解MVVM架构,因为以前从未有过类似的经验。我现在将展示我的代码,并希望得到能够解释为什么它不起作用以及可能解决问题的答案。

我有三个文件夹:Views,用于存储设计。Models,用于存储类。ViewModels,用于获取数据。这是XAML内容页,包括登录页面。

然后我有一个持有API请求数据的Model。

然后是LoginViewModel。

请忽略API链接!但大致就是这样,我没有更多的代码。看起来似乎没有任何动作。我认为问题可能是设计文件背后缺少代码。我愿意听取建议,感激任何有生产力的答案!

英文:

I am trying to learn MAUI in order to create a project, but it seems like i got stuck. I cant understand the MVVM architecture, as i never had any simillar experience before. I will now present my code, and would love to get answers that could explain why it is not working, and a possible solution for the problem.

I have three folders: Views, where i store the design. Models, where i store the classes. And ViewModels, that get the data. This is the xaml content page, which consists of a login page.

<?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="thebridgeproject.Views.login" 
             xmlns:ViewModels="clr-namespace:thebridgeproject.ViewModels"
             Shell.NavBarIsVisible="False"
             Title="LoginPage" >
    <ContentPage.BindingContext>
        <ViewModels:LoginViewModel />
    </ContentPage.BindingContext>

    <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">

            <Image Source="loginicon.png" HeightRequest="150" WidthRequest="150" />

            <VerticalStackLayout Spacing="5">
            <Label Text="Welcome!" FontSize="28" TextColor="#3B7A5E" HorizontalTextAlignment="Center" />
            <Label Text="Login to your account" FontSize="18" TextColor="Gray" HorizontalTextAlignment="Center" />
            </VerticalStackLayout>

            <StackLayout Orientation="Horizontal">
                <Frame ZIndex="1" HasShadow="True" BorderColor="White" HeightRequest="56" WidthRequest="56" CornerRadius="28">
                    <Image Source="user.png" HeightRequest="20" WidthRequest="20" />
                </Frame>
            <Frame HeightRequest="45" Margin="-20,0,0,0" Padding="0" HasShadow="True" BorderColor="White"  HorizontalOptions="FillAndExpand">
                <Entry Text="{Binding Username}" Margin="20,0,0,0" VerticalOptions="Center" Placeholder="Username"/>
            </Frame>
            </StackLayout>

            <StackLayout Orientation="Horizontal">
                <Frame ZIndex="1" HasShadow="True" BorderColor="White" HeightRequest="56" WidthRequest="56" CornerRadius="28">
                    <Image Source="lock.png" HeightRequest="20" WidthRequest="20" />
                </Frame>
            <Frame HeightRequest="45" Margin="-20,0,0,0" Padding="0" HasShadow="True" BorderColor="White"  HorizontalOptions="FillAndExpand">
                <Entry Text="{Binding Password}" Margin="20,0,0,0" VerticalOptions="Center" Placeholder="Password" IsPassword="True" />
            </Frame>
            </StackLayout>

        <Button Text="Sign in" WidthRequest="100" CornerRadius="20" HorizontalOptions="Center" BackgroundColor="#3B7A5E" Command="{Binding LoginCommand}" />

            <StackLayout Orientation="Horizontal" Spacing="5" HorizontalOptions="Center">
            <Label Text="Dont have an account?" TextColor="Gray" />
            <Label Text="Sign up here" TextColor="#50b3f2" />
            </StackLayout>
        </VerticalStackLayout>
    </ContentPage>

Then i have the Model that holds the data for the API request.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace thebridgeproject.Models
{
    class users
    {

        public class Result
        {
            public int NumUtente { get; set; }
            public string Nome { get; set; }
            public string Password { get; set; }
            public string Morada { get; set; }
            public string Cidade { get; set; }
            public string DataNascimento { get; set; }
            public string NumTlf { get; set; }
        }

        public class Root
        {
            public bool success { get; set; }
            public string message { get; set; }
            public List<Result> result { get; set; }
        }

    }
}

After that, we have the LoginViewModel:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using thebridgeproject.Models;

namespace thebridgeproject.ViewModels
{
    public class LoginViewModel : INotifyPropertyChanged
    {
        private string _username;
        public string Username
        {
            get { return _username; }
            set
            {
                _username = value;
                OnPropertyChanged(nameof(Username));
            }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                OnPropertyChanged(nameof(Password));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private async Task Login()
        {
            using (var httpClient = new HttpClient())
            {

                httpClient.DefaultRequestHeaders.Add("Authorization", "RaV9N");
                var response = await httpClient.GetAsync("http:///bp/utentes");

                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    var users = JsonConvert.DeserializeObject<users.Root>(content);

                    if (users.success)
                    {
                        var user = users.result.FirstOrDefault(x => x.Nome == Username);
                        if (user != null && VerifyPassword(user.Password, Password))
                        {
                            // Login successful
                            // ...
                        }
                        else
                        {
                            // Login failed
                            // ...
                        }
                    }
                    else
                    {
                        // API request failed
                        // ...
                    }
                }
                else
                {
                    // API request failed
                    // ...
                }
            }
        }

        private bool VerifyPassword(string hashedPassword, string enteredPassword)
        {
            // Use the BCrypt.Net library to verify the entered password
            return BCrypt.Net.BCrypt.Verify(enteredPassword, hashedPassword);
        }
    }
}

Ignore the API link! But that is mostly it, i have no more code. It seems like it does nothing. I think the issue might be the lack of code in the file behind the design. Im open to suggestions, and i am thankfull for any productive answer!

答案1

得分: 0

view model 实现属性和命令,供视图数据绑定,并通过更改通知事件通知view任何状态更改。view model 还负责协调视图与所需的模型类的交互。视图模型和模型类之间通常是一对多的关系。您可以参考模型-视图-视图模型模式获取更多详情。

您可以参考下面的示例代码,了解如何在LoginCommand中使用它。请注意,我将它放在Label TapGestureRecognizer中,但它在Button中的使用方式与您在Button中使用它的方式相同。

XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:ViewModel="clr-namespace:MyApp.ViewModels"
             xmlns:local="clr-namespace:MyApp"
             x:Class="MyApp.Views.LoginPage"
             BackgroundColor="#112B47">
    <ContentPage.BindingContext>
        <ViewModel:LoginViewModel/>
    </ContentPage.BindingContext>

    <StackLayout  Padding="15" VerticalOptions="Center" HorizontalOptions="FillAndExpand">

        <Label HorizontalOptions="Center">
            <Label.FormattedText>
                <FormattedString>
                    <Span Text="Don't have an account?" TextColor="Gray"></Span>
                    <Span Text="Register" TextColor="Gray" FontAttributes="Bold" TextDecorations="Underline"></Span>
                </FormattedString>
            </Label.FormattedText>
            <Label.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding LoginCommand}"></TapGestureRecognizer>
            </Label.GestureRecognizers>
        </Label>
    </StackLayout>
</ContentPage>

LoginViewModel:

public class LoginViewModel : INotifyPropertyChanged 
{
    public ICommand LoginCommand { get; private set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public LoginViewModel()
    {
        LoginCommand = new Command(async () => await Login());
    }

    public async Task Login()
    {
        // 添加您的逻辑在这里
    }
}
英文:

The view model implements properties and commands to which the view can data bind to, and notifies the view of any state changes through change notification events. The view model is also responsible for coordinating the view's interactions with any model classes that are required. There's typically a one-to-many relationship between the view model and the model classes. You can refer to The Model-View-ViewModel Pattern for more details.

You can refer to my sample code below on how to use LoginCommand in your LoginCommand. Notice that I put it in a Label TapGestureRecognizer, however its usage is the same as that you use it in a Button.

XAML:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt; 
&lt;ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot; xmlns:ViewModel=&quot;clr-namespace:MyApp.ViewModels&quot;
             
             xmlns:local=&quot;clr-namespace:MyApp&quot;
             x:Class=&quot;MyApp.Views.LoginPage&quot;
             BackgroundColor=&quot;#112B47&quot;
             &gt;
    &lt;ContentPage.BindingContext&gt;
        &lt;ViewModel:LoginViewModel/&gt;
    &lt;/ContentPage.BindingContext&gt;

    &lt;StackLayout  Padding=&quot;15&quot; VerticalOptions=&quot;Center&quot; HorizontalOptions=&quot;FillAndExpand&quot;&gt;

        &lt;Label HorizontalOptions=&quot;Center&quot;&gt;
            &lt;Label.FormattedText&gt;
                &lt;FormattedString&gt;
                     &lt;Span Text=&quot;Don&#39;t have an account?&quot; TextColor=&quot;Gray&quot;&gt;&lt;/Span&gt;
                     &lt;Span Text=&quot;Register&quot; TextColor=&quot;Gray&quot; FontAttributes=&quot;Bold&quot; TextDecorations=&quot;Underline&quot;&gt;&lt;/Span&gt;
                &lt;/FormattedString&gt;
            &lt;/Label.FormattedText&gt;
            &lt;Label.GestureRecognizers&gt;
                &lt;TapGestureRecognizer  Command=&quot;{Binding LoginCommand}&quot;&gt;&lt;/TapGestureRecognizer&gt;
            &lt;/Label.GestureRecognizers&gt;
        &lt;/Label&gt;
        
    &lt;/StackLayout&gt;
   
&lt;/ContentPage&gt;

LoginViewModel:

  public class LoginViewModel : INotifyPropertyChanged 
  {

    
        public ICommand LoginCommand { get; private set; }
   

        public event PropertyChangedEventHandler PropertyChanged;



        public LoginViewModel()
        {
              LoginCommand = new Command(async () =&gt; await Login());
        }
   
     

        public async Task Login()
        {
            // add your logic here
        }

     
   }

huangapple
  • 本文由 发表于 2023年2月14日 04:31:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75440878.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定