0

我有一个 Xamarin shell 应用程序,在 AppShell.xaml 中定义了以下内容:

   <TabBar Route="tabs" x:Name="tbTabs">
        <Tab Title="Home"
             Route="home"             
             Style="{StaticResource BaseStyle}"
             Icon="home.png" >
            <ShellContent>
                <views:LandingPage />
            </ShellContent>
        </Tab>
        <Tab Title="Services"
             Route="service"             
             Style="{StaticResource BaseStyle}"
             Icon="service.png">
            <ShellContent>
                <views:ServicesPage/>
            </ShellContent>
        </Tab>
   </TabBar>

我的目标是通过单击主页上的图标转到列出应用程序收到的通知历史记录的页面,从主页导航到在 shell 结构之外定义的页面。单击列表中的通知,将转到该通知的详细信息页面。

主页 <-> 通知历史 <-> 通知详情

在 AppShell.xaml 中未定义为 shell 一部分的页面的路由在 AppShell.xaml.cs 中定义为:

Routing.RegisterRoute("notificationhistorypage", typeof(NotificationHistoryPage));
Routing.RegisterRoute("notificationdetailspage", typeof(NotificationDetailsPage));

问题是,当我在通知历史记录页面上单击通知时,导航动画会在导航到详细信息页面之前显示导航回主页。当我从详细信息页面导航回来时,而不是返回通知列表,它直接进入主页。

我已经尝试了很多方法来解决这个问题。所有似乎都表现出相同的行为,或导致异常。

从我在https://github.com/xamarin/Xamarin.Forms/issues/5790阅读的内容来看,应该将路由定义为:

Routing.RegisterRoute("//tabs/home/notificationhistorypage", typeof(NotificationHistoryPage));
Routing.RegisterRoute("//tabs/home/notificationhistorypage/notificationdetailspage", typeof(NotificationDetailsPage));

不幸的是,这会在运行时产生异常:

“无法找出路线:/notificationhistorypage\n参数名称:uri”

为了从主页导航到通知历史记录页面,我尝试了这些:

await Shell.Current.GoToAsync("/notificationhistorypage");

await Shell.Current.GoToAsync("//tabs/home/notificationhistorypage");

为了从通知历史页面导航到通知详细信息页面,我尝试了这些:

await Shell.Current.GoToAsync($"/notificationdetailspage?itemid={notification.ID}", true);

await Shell.Current.GoToAsync($"///tabs/home/notificationhistorypage/notificationdetailspage?itemid={notification.ID}", true);

await Shell.Current.Navigation.PushAsync(new NotificationDetailsPage(notification.ID.ToString()));

结果总是一样的。出于某种原因,导航历史页面总是在推送通知详细信息页面之前从导航堆栈中弹出。我正在尝试做的事情是否可能,或者我应该摆脱外壳,并使用旧导航来完成这一切?

4

2 回答 2

1

问题是,当我单击通知历史记录页面上的通知时,导航动画会在导航到详细信息页面之前显示导航回主页。当我从详细信息页面导航回来时,而不是返回通知列表,它直接进入主页。

AppShell.xaml 中的弹出项:

<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
    <ShellContent
        Title="About"
        ContentTemplate="{DataTemplate local:AboutPage}"
        Icon="tab_about.png"
        Route="AboutPage" />
    <ShellContent
        Title="Browse"
        ContentTemplate="{DataTemplate local:ItemsPage}"
        Icon="tab_feed.png" />
    <ShellContent
        Title="Main"
        ContentTemplate="{DataTemplate local:MainPage}"
        Icon="tab_feed.png" />
</FlyoutItem>

在 Shell 子类构造函数中的 Routing.RegisterRoute 方法:

 public AppShell()
    {
        InitializeComponent();
        
        Routing.RegisterRoute("NotificationHistory", typeof(NotificationHistory));
        Routing.RegisterRoute("NotificationDetails", typeof(NotificationDetails));
    }

从 MainPage 导航到 NotificationHistory 页面。

private async void btn1_Clicked(object sender, EventArgs e)
    {
        await Shell.Current.GoToAsync("NotificationHistory");
    }

从 NotificationHistory 导航到 NotificationDetailspage。

 async void OnItemSelected(Item item)
    {
        if (item == null)
            return;

        // This will push the ItemDetailPage onto the navigation stack
        await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
    }

通知历史页面:

  <StackLayout>
       
            <CollectionView
                x:Name="ItemsListView"
                ItemsSource="{Binding Items}"
                SelectionMode="None">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout Padding="10" x:DataType="model:Item">
                            <Label
                                FontSize="16"
                                LineBreakMode="NoWrap"
                                Text="{Binding Text}" />
                            <Label
                                FontSize="13"
                                LineBreakMode="NoWrap"
                                Text="{Binding Description}" />
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type local:HistoryViewModel}}, Path=ItemTapped}"
                                    CommandParameter="{Binding .}"
                                    NumberOfTapsRequired="1" />
                            </StackLayout.GestureRecognizers>
                        </StackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
       
    </StackLayout>

 public partial class NotificationHistory : ContentPage
{
    HistoryViewModel _viewModel;
    public NotificationHistory()
    {
        InitializeComponent();
        BindingContext = _viewModel = new HistoryViewModel();
    }

}
public class HistoryViewModel : BaseViewModel
{  
    public   ObservableCollection<Item> Items { get; set; }    
    public Command<Item> ItemTapped { get; }
    public HistoryViewModel()
    {
        Title = "history";
        Items = new ObservableCollection<Item>()
        {
            new Item { Id ="1" , Text = "First item", Description="This is an item description." },
            new Item { Id = "2", Text = "Second item", Description="This is an item description." },
            new Item { Id = "3", Text = "Third item", Description="This is an item description." },
            new Item { Id = "4", Text = "Fourth item", Description="This is an item description." },
            new Item { Id = "5", Text = "Fifth item", Description="This is an item description." },
            new Item { Id = "6", Text = "Sixth item", Description="This is an item description." }
        };

        ItemTapped = new Command<Item>(OnItemSelected);
    }
    async void OnItemSelected(Item item)
    {
        if (item == null)
            return;

        // This will push the ItemDetailPage onto the navigation stack
        await Shell.Current.GoToAsync($"{nameof(NotificationDetails)}?{nameof(DetailViewModel.ItemId)}={item.Id}");
    }
}

通知详情页面:

  <StackLayout Padding="15" Spacing="20">
            <Label FontSize="Medium" Text="Text:" />
            <Label FontSize="Small" Text="{Binding Text}" />
            <Label FontSize="Medium" Text="Description:" />
            <Label FontSize="Small" Text="{Binding Description}" />
        </StackLayout>

 public partial class NotificationDetails : ContentPage
{
    public NotificationDetails()
    {
        InitializeComponent();
        this.BindingContext =new DetailViewModel();
    }
}
[QueryProperty(nameof(ItemId), nameof(ItemId))]
public class DetailViewModel : BaseViewModel
{
    private string itemId;
    private string text;
    private string description;
    public string Id { get; set; }

    public string Text
    {
        get => text;
        set => SetProperty(ref text, value);
    }

    public string Description
    {
        get => description;
        set => SetProperty(ref description, value);
    }

    public string ItemId
    {
        get
        {
            return itemId;
        }
        set
        {
            itemId = value;
            LoadItemId(value);
        }
    }

    public async void LoadItemId(string itemId)
    {
        HistoryViewModel items = new HistoryViewModel();
        try
        {
            var item = items.Items.Where(x => x.Id == itemId).FirstOrDefault();
            if(item!=null)
            {
                Id = item.Id;
                Text = item.Text;
                Description = item.Description;
            }
          
        }
        catch (Exception)
        {
            Debug.WriteLine("Failed to Load Item");
        }
    }
}

BaseViewModel 是实现 INotifyPropertyChanged 的​​类

 public class BaseViewModel : INotifyPropertyChanged
{
   
    string title = string.Empty;
    public string Title
    {
        get { return title; }
        set { SetProperty(ref title, value); }
    }

    protected bool SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;
        if (changed == null)
            return;

        changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}
于 2021-04-07T03:42:27.393 回答
0

不幸的是,这个问题是由我在 AppShell.xaml.cs 的 OnNavigating() 中的一些代码引起的,当按下某些选项卡时,它会弹出导航堆栈的所有内容。当 args.Source == ShellNavigationSource.Push 时,我没有意识到我需要阻止该代码运行。我还应该提到,为了使我的代码修复工作,我必须从 4.8 更新到 Xamarin.Forms 5.0。

于 2021-04-08T15:11:40.170 回答