.NET MAUI MVVM乘法页面:分数、时长与问题不显示求助
问题描述
我使用.NET MAUI MVVM框架开发一款简易数学运算应用,编写乘法运算页面时,进入页面后仅能看到文本提示和提交按钮,但分数、剩余时长及乘法问题内容无法显示,恳请协助排查解决。
相关代码
MultiplicationPage.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" xmlns:viewModels="clr-namespace:MauiApp2.ViewModels" xmlns:models="clr-namespace:MauiApp2.Models" x:Class="MauiApp2.Views.MultiplicationPage" x:DataType="viewModels:MultiplicationPageViewModel" Title="MultiplicationPage"> <VerticalStackLayout VerticalOptions="Start"> <StackLayout x:DataType="models:PlayerModal"> <Label Text="Score: " FontSize="18" /> <Label Text="{Binding Score}" FontSize="18" /> <Label Text="Time Remaining: " FontSize="18" /> <Label Text="{Binding TimeReaming}" FontSize="18" /> <Label Text="Question: " FontSize="18" /> <Label Text="{Binding Question}" FontSize="18" /> <Entry Placeholder="Enter Your Answer" Text="{Binding Answer}" FontSize="18" /> </StackLayout> <Button Text="Submit" Command="{Binding SubmitAnswerCommand }" FontSize="18" /> </VerticalStackLayout> </ContentPage>
MultiplicationPage.xaml.cs
using MauiApp2.ViewModels; namespace MauiApp2.Views; public partial class MultiplicationPage : ContentPage { public MultiplicationPage(MultiplicationPageViewModel viewModel) { InitializeComponent(); this.BindingContext = viewModel; } }
MultiplicationPageViewModel.cs
using CommunityToolkit.Mvvm.Input; using MauiApp2.Models; using MauiApp2.Services; using MauiApp2.Views; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace MauiApp2.ViewModels { public partial class MultiplicationPageViewModel : BaseViewModel { public ObservableCollection<PlayerModal> PlayerDetails { get; set; } = new ObservableCollection<PlayerModal>(); PlayerModal playerModal = new PlayerModal(); public ICommand SubmitAnswerCommand { get; } public MultiplicationPageViewModel(INavigationService navigationService) : base(navigationService) { playerModal.Score = 0; playerModal.TimeReaming = 60; SubmitAnswerCommand = new Command(SubmitAnswer); // Create a question as an example GenerateQuestion(); // Start the timer StartTimer(); } [RelayCommand] public void GenerateQuestion() { Random random = new Random(); int number1 = random.Next(1, 11); // a random number between 1 and 10 int number2 = random.Next(1, 11); // a random number between 1 and 10 playerModal.Answer = (number1 * number2).ToString(); playerModal.Question = $"{number1} x {number2}"; } public async void StartTimer() { while (true) { await Task.Delay(1000); // wait 1 sec playerModal.TimeReaming--; if (playerModal.TimeReaming == 0) { GameOver(); // Finish the game when time is up } } } public void SubmitAnswer() { if (int.TryParse(playerModal.Answer, out int userAnswer)) { int correctAnswer = int.Parse(playerModal.Answer); if (userAnswer == correctAnswer) { playerModal.Score += 10; // Add 10 points if correct answer is given playerModal.TimeReaming += 5; // Add 5 seconds if correct answer is given } else { playerModal.Score -= 10; // 10 points subtract if the wrong answer is given. playerModal.TimeReaming -= 5; // 5 seconds subtract if wrong answer is given } GenerateQuestion(); // Generate new question } } public async void GameOver() { await NavigationService.NavigateToAsync(nameof(GameOverPage)); } } }
PlayerModel.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MauiApp2.Models { public class PlayerModal { public int Score { get; set; } public int HighScore { get; set; } public int TimeReaming { get; set; } public string Answer { get; set; } public string Question { get; set; } } }
问题排查与修复方案
核心问题分析
- 绑定上下文不匹配:XAML中嵌套的
StackLayout指定了x:DataType="models:PlayerModal",但父级上下文是MultiplicationPageViewModel,且ViewModel未将playerModal暴露为可绑定的公共属性,导致XAML无法找到绑定源。 - 缺少属性变更通知:
PlayerModal未实现INotifyPropertyChanged接口,属性值变化时UI无法自动更新。 - 业务逻辑错误:
SubmitAnswer方法中错误地将用户输入的Answer当作正确答案,导致判断逻辑失效;StartTimer是无限循环,未处理页面销毁时的资源释放,可能引发内存泄漏。
修复步骤
1. 给PlayerModal添加属性变更通知
using System.ComponentModel; using System.Runtime.CompilerServices; namespace MauiApp2.Models { public class PlayerModal : INotifyPropertyChanged { private int _score; private int _highScore; private int _timeReaming; private string _answer; private string _question; private string _correctAnswer; public int Score { get => _score; set => SetProperty(ref _score, value); } public int HighScore { get => _highScore; set => SetProperty(ref _highScore, value); } public int TimeReaming { get => _timeReaming; set => SetProperty(ref _timeReaming, value); } public string Answer { get => _answer; set => SetProperty(ref _answer, value); } public string Question { get => _question; set => SetProperty(ref _question, value); } public string CorrectAnswer { get => _correctAnswer; set => SetProperty(ref _correctAnswer, value); } public event PropertyChangedEventHandler PropertyChanged; protected void SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { if (!EqualityComparer<T>.Default.Equals(backingField, value)) { backingField = value; OnPropertyChanged(propertyName); } } protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
2. 修改ViewModel暴露可绑定属性并修复逻辑
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MauiApp2.Models; using MauiApp2.Services; using MauiApp2.Views; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; namespace MauiApp2.ViewModels { public partial class MultiplicationPageViewModel : BaseViewModel, IDisposable { public ObservableCollection<PlayerModal> PlayerDetails { get; set; } = new ObservableCollection<PlayerModal>(); [ObservableProperty] private PlayerModal _playerModal = new PlayerModal(); public ICommand SubmitAnswerCommand { get; } private CancellationTokenSource _timerCts; public MultiplicationPageViewModel(INavigationService navigationService) : base(navigationService) { PlayerModal.Score = 0; PlayerModal.TimeReaming = 60; SubmitAnswerCommand = new Command(SubmitAnswer); GenerateQuestion(); StartTimer(); } [RelayCommand] public void GenerateQuestion() { Random random = new Random(); int number1 = random.Next(1, 11); int number2 = random.Next(1, 11); PlayerModal.CorrectAnswer = (number1 * number2).ToString(); PlayerModal.Question = $"{number1} x {number2}"; PlayerModal.Answer = string.Empty; } public async void StartTimer() { _timerCts = new CancellationTokenSource(); try { while (!_timerCts.Token.IsCancellationRequested) { await Task.Delay(1000, _timerCts.Token); PlayerModal.TimeReaming--; if (PlayerModal.TimeReaming == 0) { GameOver(); break; } } } catch (TaskCanceledException) { // 定时器正常取消,无需额外处理 } } public void SubmitAnswer() { if (int.TryParse(PlayerModal.Answer, out int userAnswer) && int.TryParse(PlayerModal.CorrectAnswer, out int correctAnswer)) { if (userAnswer == correctAnswer) { PlayerModal.Score += 10; PlayerModal.TimeReaming += 5; } else { PlayerModal.Score -= 10; PlayerModal.TimeReaming -= 5; } GenerateQuestion(); } } public async void GameOver() { _timerCts?.Cancel(); await NavigationService.NavigateToAsync(nameof(GameOverPage)); } public void Dispose() { _timerCts?.Cancel(); _timerCts?.Dispose(); } } }
3. 修正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" xmlns:viewModels="clr-namespace:MauiApp2.ViewModels" xmlns:models="clr-namespace:MauiApp2.Models" x:Class="MauiApp2.Views.MultiplicationPage" x:DataType="viewModels:MultiplicationPageViewModel" Title="MultiplicationPage"> <VerticalStackLayout VerticalOptions="Start"> <StackLayout> <Label Text="Score: " FontSize="18" /> <Label Text="{Binding PlayerModal.Score}" FontSize="18" /> <Label Text="Time Remaining: " FontSize="18" /> <Label Text="{Binding PlayerModal.TimeReaming}" FontSize="18" /> <Label Text="Question: " FontSize="18" /> <Label Text="{Binding PlayerModal.Question}" FontSize="18" /> <Entry Placeholder="Enter Your Answer" Text="{Binding PlayerModal.Answer}" FontSize="18" /> </StackLayout> <Button Text="Submit" Command="{Binding SubmitAnswerCommand }" FontSize="18" /> </VerticalStackLayout> </ContentPage>
4. 页面销毁时释放定时器
using MauiApp2.ViewModels; namespace MauiApp2.Views; public partial class MultiplicationPage : ContentPage { private readonly MultiplicationPageViewModel _viewModel; public MultiplicationPage(MultiplicationPageViewModel viewModel) { InitializeComponent(); _viewModel = viewModel; this.BindingContext = viewModel; } protected override void OnDisappearing() { base.OnDisappearing(); _viewModel.Dispose(); } }
内容的提问来源于stack exchange,提问作者M3rtixius
相关产品推荐
相关产品推荐

