XAML 等待Shell.Current.GoToAsync()后保持按下MAUI按钮

6ioyuze2  于 2023-01-15  发布在  Shell
关注(0)|答案(3)|浏览(253)

在我的MAUI应用程序中,我在两个页面之间导航,主页面和添加页面。
我正在按钮调用的ViewModel上的命令中使用await Shell.Current.GoToAsync()。但是,当我单击按钮时,它保持灰色。它导航到正确的页面,但当我返回到页面时,它仍然是灰色的。如果我右键单击按钮,它会修复此问题,不再是灰色的。如果我取消await或使其不同步,也会修复此问题。
第一节第一节第一节第一节第一节第二节第一节

<?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:viewmodel="clr-namespace:ButtonTest"
             x:DataType="viewmodel:MainPageViewModel"
             x:Class="ButtonTest.MainPage">

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

            <Image
                Source="dotnet_bot.png"
                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                HeightRequest="200"
                HorizontalOptions="Center" />

            <Label
                Text="Hello, World!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />

            <Label
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                FontSize="18"
                HorizontalOptions="Center" />

            <Button
                x:Name="CounterBtn"
                Text="AddPage"
                Command="{Binding GoAddPageCommand}"
                HorizontalOptions="Center" />

        </VerticalStackLayout>
    </ScrollView>

</ContentPage>
namespace ButtonTest;

public partial class MainPage : ContentPage
{
    int count = 0;

    public MainPage(MainPageViewModel vm)
    {
        InitializeComponent();
        BindingContext= vm;
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        count++;

        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);
    }
}
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ButtonTest
{
    public partial class MainPageViewModel : ObservableObject
    {
        [RelayCommand]
        public async Task GoAddPage()
        {
            await Shell.Current.GoToAsync("//AddPage");
        }
    } 
}
<?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:viewmodel="clr-namespace:ButtonTest"
             x:DataType="viewmodel:AddPageViewModel"
             x:Class="ButtonTest.AddPage"
             Title="AddPage">
    <VerticalStackLayout>
        <Button
                x:Name="CounterBtn"
                Text="MainPage"
                Command="{Binding GoMainPageCommand}"
                HorizontalOptions="Center" />
    </VerticalStackLayout>
</ContentPage>
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ButtonTest
{
    public partial class AddPageViewModel : ObservableObject
    {
        [RelayCommand]
        public async Task GoMainPage()
        {
            await Shell.Current.GoToAsync("//MainPage");
        }
    }
}

Appshell.xaml

<Shell
    x:Class="ButtonTest.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:ButtonTest"
    Shell.FlyoutBehavior="Disabled">

    <ShellContent
        Title="MainPage"
        ContentTemplate="{DataTemplate local:MainPage}"
        Route="MainPage" />

    

</Shell>

AppShell.xaml.cs

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute($"{nameof(MainPage)}", typeof(MainPage));
        Routing.RegisterRoute($"{nameof(AddPage)}", typeof(AddPage));

    }
}

我认为按下按钮后它会返回正常颜色。我尝试删除async或不返回Task,效果如预期。如何处理异步命令和按钮?

yqlxgs2m

yqlxgs2m1#

当您使用[RelayCommand]属性为方法自动生成Command时,将生成何种类型的Command取决于方法的返回类型和签名。

1.中继命令

当你的方法有一个void返回类型时,那么一个RelayCommand会在幕后为你生成:

[RelayCommand]
private void DoSomething()
{
    //e.g.
    Shell.Current.GoToAsync("//AddPage");
}

这里会生成一个RelayCommand,由于方法是void,所以不能等待它的执行,它会同步运行,所以命令会立即返回,按钮也会立即重新启用。

2.异步中继命令

另一方面,当你的方法返回类型是async Task时,它会在幕后创建一个AsyncRelayCommand

[RelayCommand]
private async Task DoSomethingAsync()
{
    //e.g.
    await Shell.Current.GoToAsync("//MainPage");
}

这里有几个关键的区别:

  1. AsyncRelayCommand等待执行任务
    1.在等待执行时,AsyncRelayCommandIsRunning标志将设置为true
    1.当IsRunning标志为true时,MAUI中的按钮识别AsyncRelayCommands并被禁用,而同步RelayCommand的情况并非如此
    在这种情况下,您的Button将保持禁用状态,直到Shell.Current.GoToAsync();调用完成执行。这可能需要很长时间,具体取决于您的应用中正在执行的操作。

快速修复

除非您需要特别等待导航调用,否则您只需更改方法签名以返回void,而不是上面第1节中的Task。* 请注意,这意味着您的方法在直接调用时不可等待。*

一般备注

我建议不要直接在ViewModel中执行导航。我个人认为,最好将导航隐藏在接口后面,或者将其委托给视图的代码。这样,ViewModel就可以保持整洁和可测试性。
至于为什么Button永远不会返回到它的活动颜色,可能有一个bug,尽管我还没有遇到过。对我来说,它在我的MAUI应用程序中工作得很好。但是,我总是把导航委托给我的视图或我的ViewModels之外的一个helper类。

ttygqcqt

ttygqcqt2#

默认情况下,async RelayCommand将禁用该按钮,直到命令的Task完成。您在此处描述的行为表明await Shell.Current.GoToAsync()未完成,这可能是一个bug。您可以通过在GoToAsync()之后放置断点并检查在AddPage显示后是否命中断点来验证这一点。

flmtquvp

flmtquvp3#

您可以将方法更改为下面的代码。

public partial class MainPageViewModel : ObservableObject
    {
        [RelayCommand]
        public static void GoAddPage()
        {
            Shell.Current.GoToAsync("add");
        }
    }

使用async方法使按钮必须等待,所以它不能完成任务,直到你把它转回主线程。所以这就是为什么颜色不能改回来。你可以使用静态方法。

相关问题