You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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; }
    }
}

问题排查与修复方案

核心问题分析

  1. 绑定上下文不匹配:XAML中嵌套的StackLayout指定了x:DataType="models:PlayerModal",但父级上下文是MultiplicationPageViewModel,且ViewModel未将playerModal暴露为可绑定的公共属性,导致XAML无法找到绑定源。
  2. 缺少属性变更通知:PlayerModal未实现INotifyPropertyChanged接口,属性值变化时UI无法自动更新。
  3. 业务逻辑错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 01:53:09