Я пытаюсь изучить MAUI, чтобы создать проект, но, похоже, я застрял. Я не могу понять архитектуру MVVM, так как у меня никогда не было подобного опыта. Теперь я представлю свой код и хотел бы получить ответы, которые могли бы объяснить, почему он не работает, и возможное решение проблемы.
У меня есть три папки: Views, где я храню дизайн. Модели, где я храню классы. И ViewModels, которые получают данные. Это страница содержимого xaml, состоящая из страницы входа.
<?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>
Затем у меня есть модель, которая содержит данные для запроса API.
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; }
}
}
}
После этого у нас есть 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);
}
}
}
Не обращайте внимания на ссылку API! Но это в основном так, у меня больше нет кода. Вроде ничего не делает. Я думаю, что проблема может заключаться в отсутствии кода в файле дизайна. Я открыт для предложений, и я благодарен за любой продуктивный ответ!
Используя точки останова и/или ведение журнала debug.writeline, точно определите, какая строка не делает то, что вы ожидаете.
Привет Джейсон! Намерение состояло в том, чтобы выполнить код в LoginViewModel. Я уверен, что не понял функции ViewModel и хотел бы получить разъяснения. Он был предназначен для выполнения запроса к API, который предоставляет нам json-объект с логическим значением успеха, которое может быть истинным или ложным, сообщение, текст об успешном выполнении запроса и список всех пользователей. Мы используем этот список пользователей, чтобы проверить, совпадают ли они с именем пользователя и паролем, которые были вставлены во входные данные. Прошу прощения, если в моем вопросе недостаточно информации, обычно я никому не объясняю свой код.
Раньше я добавлял точки останова в свой код, и он ничего не выполнял в модели представления.
Ваша кнопка привязана к LoginCommand, но в вашей виртуальной машине нет ничего с именем LoginCommand. В вашей виртуальной машине не определено ни одного Commands
О... Спасибо, Джейсон! В этом отношении, как я могу определить выполнение команды? Простое изменение входа в задачу на LoginCommand, кажется, не исправляет это, я что-то упустил в своей логике? Я что-то не так понял?
Learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/…
Вместо того, чтобы писать код из 10 000 символов и наблюдать, что «он ничего не делает», вы можете начать с создания одного метода, состоящего из одной строки, и посмотреть, вызовется ли он вообще. «Но это в основном так, у меня больше нет кода», я видел страницы без XAML, но это первая, на которой есть только XAML.
View model реализует свойства и команды, к которым представление может привязываться к данным, и уведомляет view of о любых изменениях состояния посредством событий уведомления об изменении. view model также отвечает за координацию взаимодействия представления с любыми требуемыми классами модели. Обычно между моделью представления и классами модели существует отношение «один ко многим». Вы можете обратиться к Шаблон Model-View-ViewModel для более подробной информации.
Вы можете обратиться к моему примеру кода ниже, чтобы узнать, как использовать LoginCommand в вашем LoginCommand. Обратите внимание, что я поместил его в Label TapGestureRecognizer, однако его использование такое же, как и в Button.
XAML:
<?xml version = "1.0" encoding = "utf-8" ?>
<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>
Логинвиевмодель:
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()
{
// add your logic here
}
}
"кажется, ничего не делает" - понятия не имею, что это значит. Пожалуйста, предоставьте более полезное описание, например "Когда я нажимаю кнопку X, я ожидаю, что метод Y будет выполнен, но это не так (или я получаю сообщение об ошибке, или исключение, и т. д.)"