I am trying to create my first Xamarin mobile application, I am a bit confused when it comes to navigating back to the previous screen/tab using the back button (android). I have created a test project and I am using Xamarin form shell to define the hierarchy.
The navigation type I am using is Tab. In my test application I got 2 tabs
This is my current method which I am using to navigate back to the first tab from the second tab if the user presses on the back button.
I override OnBackButtonPressed method and execute a command that executes GoTo method.
public partial class ItemsPage : ContentPage
{
ItemsViewModel _viewModel;
public ICommand goBack;
public ItemsPage()
{
InitializeComponent();
BindingContext = _viewModel = new ItemsViewModel();
goBack = new Command(async (x) => await Shell.Current.GoToAsync("////AboutPage"));
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
protected override bool OnBackButtonPressed()
{
base.OnBackButtonPressed();
goBack.Execute(null);
return true;
}
}
This works, but the problem is, I have hardcoded the page I wanna go back to. In the future, I will be adding 2 more tabs, so in that case say for example if the user is on the 4th tab and the previous tab was Tab 3, how can I retrieve that information so that I can successfully navigate the user back to that previous tab or page?
I tried to use the navigationstack in the Shell but couldn't figure out how to use it with tabs.
Thank you for your time.
Cheers
Edit
-Code in the AppShell which defines the hierarchy
<?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:TestApp03.Views"
Title="TestApp03"
x:Class="TestApp03.AppShell">
<TabBar>
<ShellContent Title="About" Icon="icon_about.png" Route="AboutPage" ContentTemplate="{DataTemplate local:AboutPage}" />
<ShellContent Title="Browse" Icon="icon_feed.png" ContentTemplate="{DataTemplate local:ItemsPage}" />
</TabBar>
</Shell>
You can use await Shell.Current.GoToAsync(".."); to achieve forward jump. For more usage, please refer to this link: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/tabs
Related
I'm trying to create an App where Tabs can be dynamically added and removed during runtime. I have a Tabbar in my AppShell.xaml, as seen in the following code sample:
<Shell
x:Class="foo.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:foo"
xmlns:views="clr-namespace:foo.Views"
Shell.FlyoutBehavior="Disabled"
Shell.BackgroundColor="{DynamicResource ShellBackGroundColor}">
<TabBar x:Name="MainTabBar">
<ShellContent Title="Home"
Icon="alert_icon_2_neg.png"
ContentTemplate="{DataTemplate views:StartPage}">
</ShellContent>
</TabBar>
</Shell>
The code I use to update the Tabs is the following:
public void UpdateTabs(ObservableCollection<UserAction> list)
{
MainTabBar.Items.Clear();
foreach (UserAction action in list)
{
MainTabBar.Items.Add(new ShellContent()
{
Title = action.Title,
Icon = action.ImageSource,
ContentTemplate = new DataTemplate(() => action.ActionPage),
Route = action.Title
});
}
}
After this method is executed the MainTabBar.Items list has the correct shellcontents in it, however the Tabs are not displayed in the UI. How could I achieve this?
Also, I am aware of the similarities between this and a question asked by User Bagroy (How dynamically add tabs in code behind to Maui Shell TabBar?) but the solutions given in response to his question do not work for me.
Based on the code, seems that using MainTabBar.Items.Clear(); then Tabbar not display.
So the workaround here is to add this code after clear the Items,
MainTabBar.Items.Clear();
//add the following two lines
MainTabBar = new TabBar();
this.Items.Add(MainTabBar);
Hope it works for you.
I'm trying to navigate back from the page I have been routed. I have registered all pages as well.
I have Home Page with List view. When clicking ListView Item I'm navigation to DetailsPage from there again I'm navigating to RecordPage. Now when I'm clicking back button on RecordPage, which comes automatically, it throws this exception.
Here is what I have done
AppShell.xaml
<Shell
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp.Mobile.UI;assembly=MyApp.Mobile.UI"
Shell.FlyoutBehavior="Disabled">
<ShellContent
ContentTemplate="{DataTemplate local:HomePage}"
Route="InspectionListPage" />
</Shell>
App.xaml.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
Routing.RegisterRoute(nameof(LoginPage), typeof(LoginPage));
Routing.RegisterRoute(nameof(HomePage), typeof(HomePage));
Routing.RegisterRoute(nameof(DetailsPage), typeof(DetailsPage));
Routing.RegisterRoute(nameof(RecordPage), typeof(RecordPage));
}
}
I'm trying to run a UI Test on a page (About.xaml).
Below are the steps to get to the page when the application loads.
Launch the Login Screen
User enters username
User enters password
User clicks on the Login Button
User lands on the Home Page in an AppShell.
User clicks on the Hamburger menu
User clicks on the About Menu in the Flyout Menu Items.
My question is, how do I set the Automation Id for the Hamburger Menu (Flyout Menu) of the AppShell?
Here's the UI Test Case.
[Test]
public async Task AboutPage_UITest()
{
//Arange
app.EnterText("UsernameEntryId", "user1");
app.EnterText("PasswordEntryId", "Abc#123");
//Act
app.DismissKeyboard();
app.Tap(x => x.Marked("LoginButtonId"));
app.Tap(x => x.Marked("AppShellId"));
//app.Tap(c => c.Class("OverflowMenuButton")); I tried this as well but no luck.
await Task.Delay(30000);
app.Tap(x => x.Marked("AboutId"));
//Assert
var appResult = app.Query("EmailId").First(result => result.Text == "abc#example.com");
Assert.IsTrue(appResult != null, "Label is not displaying the right result!");
app.Screenshot("About Page");
}
In the AppShell.xaml, here's the top section.
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
FlyoutHeaderBehavior="CollapseOnScroll"
Shell.ItemTemplate="{StaticResource FlyoutTemplate}"
Shell.MenuItemTemplate="{StaticResource FlyoutTemplate}"
FlyoutBackgroundColor="WhiteSmoke"
Navigating="OnNavigating"
Navigated="OnNavigated"
AutomationId="AppShellId"
x:Class="DemoApp.AppShell">
Welcome to SO!
After researching that , you can use follow ways to set the AutomationId for the Hamburger Menu.
In AppShell.xaml.cs:
public AppShell()
{
InitializeComponent();
FlyoutIcon.AutomationId = "FlyoutIcon";
//or
FlyoutIcon.SetValue(AutomationProperties.NameProperty, "FlyoutIcon");
}
Note: Also need to set a value for FlyoutIcon(Such as: FlyoutIcon="xxx.png") make it work, otherwise above code will occur error System.NullReferenceException: 'Object reference not set to an instance of an object.'.
In addition, here it the way to go to About Page without tapping the Hamburger menu.
You can add TabBar to AppShell.xaml, and defining the About page and other page inside it.
Such as follow:
<TabBar>
<Tab Title="About"
AutomationId="About"
Icon="tab_about.png">
<ShellContent>
<local:AboutPage />
</ShellContent>
</Tab>
<Tab Title="Browse"
AutomationId="Browse"
Icon="tab_feed.png">
<ShellContent>
<local:ItemsPage />
</ShellContent>
</Tab>
</TabBar>
Then in Test method can navigate to your wanted page as follow:
app.Tap(x => x.Marked("About"));
Or, directly in Xaml, like this:
<Shell.FlyoutIcon>
<FontImageSource AutomationId="THE_ID_YOU_WANT" ... other props />
</Shell.FlyoutIcon>
You can also use FileImageSource or UrlImageSource or whatever kind you need. The important thing is that the AutomationId goes on the ImageSource tag itself.
Annoyingly, you don't seem to be able to set the AutomationId without manually defining an icon. And the Id it gives you is not consistent every run (Sometimes it is "OK" and sometimes "Open navigation drawer" - so this is the best way to get a consistent Id.
I have created a Master Detail Page
I am loading a list of items into the 'detail' frame / window
I want to replace the contents of that page with a template / view which never has any reason to exist as an item in the Menu Items
I have tried replacing MainPage and Navigation which load the page but you lose the Master Detail context - the menu
Please can someone tell me what I call in order to replace the current page with one of my choice while staying within the context of Master Detail?
This does not work, for example - it removes the MasterDetail menu
Navigation.PushAsync(new Arcade.Index());
I have created the MasterDetailPage by pretty much letting Visual Studio generate it. I set it after a successful login, like so:
var welcome = new Pages.Welcome();
Application.Current.MainPage = welcome;
This is an excerpt of the XAML for Welcome
<MasterDetailPage.Master>
<pages:WelcomeMaster x:Name="MasterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<pages:Index />
</x:Arguments>
</NavigationPage>
I've added this to the code behind for Welcome
InitializeComponent();
MasterPage.ListView.ItemSelected += ListView_ItemSelected;
this.Detail = new NavigationPage(new Arcade.Index());
In spite of all that, when I call this later, the MasterDetail menu disappears
((MasterDetailPage)Application.Current.MainPage).Detail = new Arcade.Index();
In you App.xaml.cs add static method then you can use it to navigate in you code
public static void SetDatailPage(Page page)
{
if (App.Current.MainPage is MasterDetailPage)
{
var masterPage = (MasterDetailPage)App.Current.MainPage;
masterPage.Detail = new NavigationPage(page);
}
}
And use it like this
App.SetDatailPage(new YourPageYouWannaNavigateTo());
1 - use App.Current.Mainpage
var md = (MasterDetailPage)App.Current.MainPage;
md.Detail = new MyPage();
2 - use navigation
when you initially create your detail, wrap it in a NavigationPage
this.Detail = new NavigaitionPage(new Page1());
then later, Page1 can navigate to Page2 within the Detail pane
Navigation.PushAsync(new Page2());
3 - explicitly pass a reference to the MasterDetailPage
// when you create Page1, pass a reference to the MasterDetailPage
// you will also need to modify Page1's constructor
this.Detail = new Page1(this);
I am trying to remove navbar from my pages in Xamarin Forms, but I am not able to get it working. I have tried by adding NavigationPage.SetHasNavigationBar(this, false); inside constructor of page eg.
public RegisterUser ()
{
InitializeComponent ();
NavigationPage.SetHasNavigationBar(this, false);
}
And / or by adding NavigationPage.HasNavigationBar="False" inside xaml page
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PetApp.Pages.RegisterUser"
NavigationPage.HasNavigationBar="false">
But none of those helps.
Is there some better best practice to just show clean page with scrollview or should it be possible to remove navbar totally?
It works in Mainpage but not the rest of pages that I am navigating to via
await Navigation.PushAsync(new NavigationPage(new RegisterUser()));
I found a solution, instead of using Navigation.PushAsync I used
Navigation.PushModalAsync(new NavigationPage(new RegisterPet()));
and also OnAppearing of RegisterPet page I added SetHasNavigationBar
protected override void OnAppearing()
{
InitializeSettings();
NavigationPage.SetHasNavigationBar(this, false);
base.OnAppearing();
}