从我的添加页面导航回来时,如何在我的 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 来实现您想要实现的目标事件 OnNavigated
或 OnNavigating
.
- 首先,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();
}
第三种方法
从 AddEditFlightPage
的 OnDisappearing()
调用 ExecuteLoadFlightsCommand()
,而不是 AppShell.OnNavigated()
。
AddEditFlightPage.xaml.cs
protected override void OnDisappearing()
{
(DependencyService.Resolve<IFlyoutContentTemplateViewModel>())?.ExecuteLoadFlightsCommand();
base.OnDisappearing();
}
我正在使用 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 来实现您想要实现的目标事件 OnNavigated
或 OnNavigating
.
- 首先,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();
}
第三种方法
从 AddEditFlightPage
的 OnDisappearing()
调用 ExecuteLoadFlightsCommand()
,而不是 AppShell.OnNavigated()
。
AddEditFlightPage.xaml.cs
protected override void OnDisappearing()
{
(DependencyService.Resolve<IFlyoutContentTemplateViewModel>())?.ExecuteLoadFlightsCommand();
base.OnDisappearing();
}