从我的添加页面导航回来时,如何在我的 FlyoutContentTemplate 视图模型上触发数据刷新?

How do I trigger a data refresh on my FlyoutContentTemplate view model when navigating back from my add page?

我正在使用 Shell 构建 Xamarin Forms 应用程序,其中我没有构建典型的弹出窗口。我为页眉、页脚和内容分别创建了一个模板。内容包含从数据库中获取的数据。

我在我的 app.xaml 文件中定义了这些。

<Style TargetType="Shell" ApplyToDerivedTypes="True">
    <Setter Property="FlyoutFooterTemplate" Value="{DataTemplate common:FlyoutFooterTemplate}"/>
    <Setter Property="FlyoutHeaderTemplate" Value="{DataTemplate common:FlyoutHeaderTemplate}"/>
    <Setter Property="FlyoutContentTemplate" Value="{DataTemplate common:FlyoutContentTemplate}"/>
</Style>

我已将 FlyoutContentTemplate 创建为 RefreshView。

<?xml version="1.0" encoding="utf-8" ?>
<RefreshView 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:InFlight.ViewModels.Common"
    xmlns:model="clr-namespace:InFlight.Core.Models;assembly=InFlight.Core"  
    x:Class="InFlight.Views.Common.FlyoutContentTemplate"
    x:DataType="local:FlyoutContentTemplateViewModel"
    Command="{Binding LoadFlightsCommand}"
    IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
    <CollectionView    
        ItemsSource="{Binding Flights}"
        SelectionMode="None">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Padding="10" x:DataType="model:Flight">
                    <Label Text="{Binding CallSign}"
                            LineBreakMode="NoWrap"
                            Style="{StaticResource LabelMedium}" />
                    <Label Text="{Binding FlightNotes}" 
                            LineBreakMode="NoWrap"
                            Style="{StaticResource LabelSmall}" />
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
        <CollectionView.Footer>
            <Button Command="{Binding AddFlightCommand}" Text="Add Flight" />
        </CollectionView.Footer>
    </CollectionView>
</RefreshView>

后面的代码只是设置 BindingContext。

using InFlight.ViewModels.Common;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace InFlight.Views.Common
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class FlyoutContentTemplate : RefreshView
    {
        readonly FlyoutContentTemplateViewModel viewModel;

        public FlyoutContentTemplate()
        {
            InitializeComponent();
            this.BindingContext = viewModel = new FlyoutContentTemplateViewModel();
        }
    }
}

视图模型相当简单,它处理由 RefreshView 触发的 LoadFlightsCommand 和到 AddEditFlightPage 的导航。

using InFlight.Core.Models;
using InFlight.Core.Respositories;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace InFlight.ViewModels.Common
{
    public class FlyoutContentTemplateViewModel : BaseViewModel
    {
        public ObservableCollection<Flight> Flights { get; set; }
        public Command LoadFlightsCommand { get; }
        public Command AddFlightCommand { get; }
        readonly IFlightRepository flightRepository;

        public FlyoutContentTemplateViewModel()
        {
            flightRepository = DependencyService.Get<IFlightRepository>();
            
            Flights = new ObservableCollection<Flight>();
            LoadFlightsCommand = new Command(ExecuteLoadFlightsCommand);
            AddFlightCommand = new Command(async () => await OnAddFlightCommand());

            ExecuteLoadFlightsCommand();
        }

        private async Task OnAddFlightCommand()
        {
            await Shell.Current.GoToAsync("AddEditFlightPage");
            Shell.Current.FlyoutIsPresented = false;
        }

        private void ExecuteLoadFlightsCommand()
        {
            IsBusy = true;

            try
            {
                Flights.Clear();
                var flts = flightRepository.GetFlights();

                foreach (var flight in flts)
                {
                    Flights.Add(flight);
                }                
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }
    }
}

这一切似乎都很好,但目前我需要拉动刷新才能触发 LoadFlightsCommand

问题是我想在从添加页面返回时触发数据刷新。我看到过人们点击 OnAppearing 事件以触发刷新的帖子,但由于我使用的是模板和刷新视图,所以我不知道该怎么做。

我是否可能采取了错误的方法并将 Shell 用于不应该用于的目的?

我认为解决方案可能涉及 shell 本身的事件?

如有任何建议,我们将不胜感激。

第一种方法

我不了解您如何定义 Shell 的 routes/navigation,但我相信您可以使用 Shell 来实现您想要实现的目标事件 OnNavigatedOnNavigating.

  • 首先,Shell (AppShell.xaml.cs) 需要访问您在 roder 中的 FlyoutContentTemplateViewModel 的实例,以便从那里调用方法 ExecuteLoadFlightsCommand()
  • ExecuteLoadFlightsCommand() 的可访问性设置为 public。

AppShell.xaml.cs

  public FlyoutContentTemplateViewModel flyoutContentTemplateViewModel;

    public AppShell()
    {
        flyoutContentTemplateViewModel = new();
        InitializeComponent();
    }

  protected override void OnNavigated(ShellNavigatedEventArgs args)
    {
        var previousRouteString = args?.Previous?.Location?.OriginalString;
        var currentRouteString = args?.Current?.Location?.OriginalString;
        
        if (previousRouteString != null && previousRouteString.Contains("[DEPENDS ON YOUR ROUTES NAME]") &&
            currentRouteString.Contains("[DEPENDS ON YOUR ROUTES NAME]"))
        {
           flyoutContentTemplate.ExecuteLoadFlightsCommand();
        }

        base.OnNavigated(args);
    }

在您的 FlyoutContentTemplate() 中,使用我们在您的应用程序中添加的字段中的相同 ViewModel 实例Shell。

public FlyoutContentTemplate()
{
   InitializeComponent();
   BindingContext = viewModel = (Shell.Current as AppShell).flyoutContentTemplateViewModel;
}

第二种方法

如果您不想将 VM 存储在应用程序中Shell,那么您可以使用 DependencyService.

  • 从你的 FlyoutContentTemplateViewModel 中提取一个接口:在 visual studio select class 名称上,右键单击,在菜单中单击“快速操作和重构”,之后单击“提取接口”,VS 将生成一个名为 IFlyoutContentTemplateViewModel:
  • 的接口
 public interface IFlyoutContentTemplateViewModel
    {
        Command AddFlightCommand { get; }
        ObservableCollection<Flight> Flights { get; set; }
        bool IsBusy { get; }
        Command LoadFlightsCommand { get; }

        Task OnAddFlightCommand()
        void ExecuteLoadFlightsCommand();
    }
  • FlyoutContentTemplate.xaml.cs
public FlyoutContentTemplate()
{
    InitializeComponent();
    BindingContext = viewModel = new FlyoutContentTemplateViewModel();
    DependencyService.RegisterSingleton<IFlyoutContentTemplateViewModel>(viewModel);
}
  • AppShell.xaml.cs
...
if (previousRouteString != null && previousRouteString.Contains("[DEPENDS ON YOUR ROUTES NAME]") &&
    currentRouteString.Contains("[DEPENDS ON YOUR ROUTES NAME]"))
 {
        DependencyService.Resolve<IFlyoutContentTemplateViewModel>()?.ExecuteLoadFlightsCommand();
 }

第三种方法

AddEditFlightPageOnDisappearing() 调用 ExecuteLoadFlightsCommand(),而不是 AppShell.OnNavigated()

AddEditFlightPage.xaml.cs

    protected override void OnDisappearing()
    {
        (DependencyService.Resolve<IFlyoutContentTemplateViewModel>())?.ExecuteLoadFlightsCommand();
        base.OnDisappearing();
    }