bwoogie
bwoogie

Reputation: 4427

Shell Routing With Parameters And TabBar

I'm trying to route parameters to a page (which contains a WebView) that can be reused by multiple tabs. I'd like to pass a Url and Title but so far in the BrowserPage I'm getting empty strings

AppShell.xaml

<TabBar>
    <ShellContent Title="Page 1" Icon="icon_about.png" Route="Page1Route" ContentTemplate="{DataTemplate local:BrowserPage}"/>
    <ShellContent Title="Page 2" Icon="icon_about.png" Route="Page2Route" ContentTemplate="{DataTemplate local:BrowserPage}"/>
</TabBar>

AppShell.xaml.cs

public partial class AppShell : Xamarin.Forms.Shell {
   public AppShell() {
      InitializeComponent();
      Routing.RegisterRoute("Page1Route?Url=page1url&Title=Page1", typeof(BrowserPage));
      Routing.RegisterRoute("Page2Route?Url=page2url&Title=Page2", typeof(BrowserPage));
            
   }
}

BrowserPage.xaml.cs

[XamlCompilation(XamlCompilationOptions.Compile)]
[QueryProperty(nameof(Url), nameof(Url))]
[QueryProperty(nameof(Title), nameof(Title))]

public partial class BrowserPage : ContentPage {

    public BrowserPage() {
        InitializeComponent();

        ((BrowserViewModel)BindingContext).Title = Title;
        ((BrowserViewModel)BindingContext).Url = Url;
    }

    private string url = "";
    public string Url {
        get {
            return url;
        }
        set {
            url = value;
            OnPropertyChanged();
        }
    }
}

In the BrowserPage code-behind, Title and Url are empty. How do I properly pass and receive data this way?

Upvotes: 1

Views: 1638

Answers (3)

bwoogie
bwoogie

Reputation: 4427

I was able to solve this by completely removing routing and adding a Url property and a Title property to the BrowserPage's codebehind:

private string url;
public string Url {
    get => url;
    set {
        url = value;
        ((BrowserViewModel)BindingContext).SetSource(url);
    }
}

private string pageTitle;
public string PageTitle {
    get => pageTitle;
    set {
        pageTitle = value;
        ((BrowserViewModel)BindingContext).SetPageTitle(pageTitle);
    }
}

In the AppShell.xaml file I removed the Route and ContentTemplate, and set the ShellContent.Content as described here. That allows me to set the url and title:

<ShellContent Title="Page 1" Icon="icon_about.png" >
    <local:BrowserPage Url="http://www.yahoo.com" PageTitle="Page 1"/>
</ShellContent>

<ShellContent Title="Page 2" Icon="icon_about.png">
    <local:BrowserPage Url="http://www.google.com" PageTitle="Page 2"/>
</ShellContent>

When the app starts and Url and PageTitle are set it passes it on to the ViewModel and it loads the page.

Full code for anyone who needs to achieve this.

BrowserPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:vm="clr-namespace:MyApp.ViewModels"
             x:Class="MyApp.Views.BrowserPage"
             Title="{Binding Title}">

    <ContentPage.BindingContext>
        <vm:BrowserViewModel/>
    </ContentPage.BindingContext>
    
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="Accent">#96d1ff</Color>
        </ResourceDictionary>
    </ContentPage.Resources>

    <Grid>
        <WebView 
            x:Name="mywebview" 
            Source="{Binding Source}" 
            Cookies="{Binding Cookies}" 
            Navigating="mywebview_Navigating" 
            Navigated="mywebview_Navigated" 
            WidthRequest="1000" 
            HeightRequest="1000"/>

        <ActivityIndicator 
            x:Name="spinner" 
            Color="Black"
            IsRunning="True"
            HorizontalOptions="Center" 
            VerticalOptions="Center"/>
    </Grid>
</ContentPage>

BrowserPage.xaml.cs

using MyApp.ViewModels;
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace MyApp.Views {
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BrowserPage : ContentPage {

        private string url;
        public string Url {
            get => url;
            set {
                url = value;
                ((BrowserViewModel)BindingContext).SetSource(url);
            }
        }

        private string pageTitle;
        public string PageTitle {
            get => pageTitle;
            set {
                pageTitle = value;
                ((BrowserViewModel)BindingContext).SetPageTitle(pageTitle);
            }
        }

        public BrowserPage() {
            InitializeComponent();
        }

        private void mywebview_Navigated(object sender, WebNavigatedEventArgs e) {
            mywebview.IsVisible = true;
            spinner.IsVisible = false;
        }

        private void mywebview_Navigating(object sender, WebNavigatingEventArgs e) {
            mywebview.IsVisible = false;
            spinner.IsVisible = true;
        }
    }
}

BrowserViewModel.cs

using System;
using System.Net;
using Xamarin.Forms;

namespace MyApp.ViewModels {
    public class BrowserViewModel : BaseViewModel {

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

        private UrlWebViewSource source;
        public UrlWebViewSource Source {
            get {
                return source;
            }
            set {
                SetProperty(ref source, value);
            }
        }

        private CookieContainer cookies;
        public CookieContainer Cookies {
            get {
                return cookies;
            }
            set {
                SetProperty(ref cookies, value);
            }
        }

        public BrowserViewModel() { }
        public BrowserViewModel(string title) {
            Title = title;
        }

        public void SetPageTitle(string title) {
            Title = title;
        }

        public void SetSource(string Url) {
            CookieContainer cookieJar = new CookieContainer();
            Uri uri = new Uri(Url, UriKind.RelativeOrAbsolute);

            Cookie cookie = new Cookie {
                ...
            };
            cookieJar.Add(uri, cookie);

            Cookies = cookieJar;
            Source = new UrlWebViewSource { Url = uri.ToString() };
        }
    }
}

AppShell.xaml

<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms" 
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:local="clr-namespace:MyApp.Views"
       Title="MyApp"
       x:Class="MyApp.AppShell">
    <Shell.Resources>
        <ResourceDictionary>
            <Style x:Key="BaseStyle" TargetType="Element">
                <Setter Property="Shell.BackgroundColor" Value="{StaticResource Primary}" />
                <Setter Property="Shell.ForegroundColor" Value="White" />
                <Setter Property="Shell.TitleColor" Value="White" />
                <Setter Property="Shell.DisabledColor" Value="#B4FFFFFF" />
                <Setter Property="Shell.UnselectedColor" Value="#95FFFFFF" />
                <Setter Property="Shell.TabBarBackgroundColor" Value="{StaticResource Primary}" />
                <Setter Property="Shell.TabBarForegroundColor" Value="White"/>
                <Setter Property="Shell.TabBarUnselectedColor" Value="#95FFFFFF"/>
                <Setter Property="Shell.TabBarTitleColor" Value="White"/>
            </Style>
            <Style TargetType="TabBar" BasedOn="{StaticResource BaseStyle}" />
            <Style TargetType="FlyoutItem" BasedOn="{StaticResource BaseStyle}" />
        </ResourceDictionary>
    </Shell.Resources>

    <TabBar>
        <ShellContent Title="Page 1" Icon="icon_about.png" >
            <local:BrowserPage Url="http://www.yahoo.com" PageTitle="Page 1"/>
        </ShellContent>

        <ShellContent Title="Page 2" Icon="icon_about.png">
            <local:BrowserPage Url="http://www.google.com" PageTitle="Page 2"/>
        </ShellContent>
    </TabBar>
</Shell>

Upvotes: 1

Cfun
Cfun

Reputation: 9721

Route registration is the same with or without parameters, parameters are sent (specified) on GoToAsync() call and not in RegisterRoute() call, so you need to provide parameters when navigating to BrowserPage.

public partial class AppShell : Xamarin.Forms.Shell {
   public AppShell() {
      InitializeComponent();
      Routing.RegisterRoute(nameof(BrowserPage), typeof(BrowserPage));            
   }
}

Page1.xaml.cs

async void Button_Clicked(object sender, System.EventArgs e)
{
   await Shell.Current.GoToAsync($"{nameof(BrowserPage)}?Title=Page1Title&Url=Page1Url");
}

Page2.xaml.cs

async void Button_Clicked(object sender, System.EventArgs e)
{
   await Shell.Current.GoToAsync($"{nameof(BrowserPage)}?Title=Page2Title&Url=Page2Url");
}

Also I believe you don''t require OnPropertyChanged on your code behind auto-property is enough, and Title property is not defined:

[QueryProperty(nameof(Url), nameof(Url))]
[QueryProperty(nameof(Title), nameof(Title))]

public partial class BrowserPage : ContentPage {

    public BrowserPage() {
        InitializeComponent();

        ((BrowserViewModel)BindingContext).Title = Title;
        ((BrowserViewModel)BindingContext).Url = Url;
    }

    public string Url { get; set; }
    public string Title { get; set; }
}

Edit

I suggest to see if you can redesign your app structure in that case

Upvotes: 1

Leon Lu
Leon Lu

Reputation: 9274

First, we do not add transfer data in the Routing.RegisterRoute.

If you register page, just use following code format, do not add transfer data.

 Routing.RegisterRoute(nameof(Page1), typeof(Page1));

When you execute the navigate, then you can use query property attributes(QueryProperty) to transfer data.

For example, I want to navigate to the page1, then send Title and Url.

  private async void Button_Clicked(object sender, EventArgs e)
        {

            string Title = "myTitle";
            string Url = "www.baidu.com";
             await Shell.Current.GoToAsync($"Page1?title={Title}&url={Url}");
        }

Then we can get the value from Page1.

 [XamlCompilation(XamlCompilationOptions.Compile)]
    [QueryProperty(nameof(Title), "title")]
    [QueryProperty(nameof(Url), "url")]
    public partial class Page1 : ContentPage
    {
        private string url = "";
        public string Url
        {
            get
            {
                return url;
            }
            set
            {
                url = value;
                OnPropertyChanged("Url");
            }
        }

        private string title = "";
        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                title = value;
                OnPropertyChanged("Title");
            }
        }
        public Page1()
        {
            InitializeComponent();

             BindingContext = this;
        }
    }
}

Here is running screenshot. Click the navi button, then navigate to the page1 enter image description here

Then we can get the value.

enter image description here

Upvotes: 0

Related Questions