Navigate to a page from a tab within a TabbedPage - xamarin.forms

I have a simple Xamarin.Forms app with a main page as TabbedPage which looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage
x:Class="PrismSample.Views.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
xmlns:views="clr-namespace:PrismSample.Views"
Title="Main Page"
prism:ViewModelLocator.AutowireViewModel="True">
<TabbedPage.Children>
<NavigationPage Title="Page1">
<x:Arguments>
<views:OnePage Title="Page 1" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Page2">
<x:Arguments>
<views:TwoPage Title="Page 2" />
</x:Arguments>
</NavigationPage>
</TabbedPage.Children>
</TabbedPage>
On the page "OnePage" I have a button. When I tap on that button I want to navigate to a page named "ThreePage".
OnPage:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="PrismSample.Views.OnePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True">
<ContentPage.Content>
<StackLayout Margin="20" Spacing="20">
<Label HorizontalOptions="CenterAndExpand" Text="Page 1" />
<Button
x:Name="Button1"
Command="{Binding ClickMeCommand}"
HorizontalOptions="FillAndExpand"
Text="Open Page 3" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
OnePageViewModel:
namespace PrismSample.ViewModels
{
public class OnePageViewModel : BindableBase
{
private readonly INavigationService navigationService;
public DelegateCommand ClickMeCommand { private set; get; }
public OnePageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
ClickMeCommand = new DelegateCommand(async () => await ClickedAsync(), () => true);
Debug.WriteLine($"NavigationStack: {this.navigationService.GetNavigationUriPath()}", nameof(OnePageViewModel));
}
public Task ClickedAsync()
{
Debug.WriteLine($"You clicked me!", nameof(OnePageViewModel));
return navigationService.NavigateAsync("ThreePage");
}
}
}
App.xaml.cs
namespace PrismSample
{
public partial class App
{
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
var result = await NavigationService.NavigateAsync("MainPage");
if(!result.Success)
{
System.Diagnostics.Debugger.Break();
}
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
containerRegistry.RegisterForNavigation<OnePage, OnePageViewModel>();
containerRegistry.RegisterForNavigation<TwoPage, TwoPageViewModel>();
containerRegistry.RegisterForNavigation<ThreePage, ThreePageViewModel>();
}
}
}
I expected the following:
When tapping on the button a navigation to the page "ThreePage" is done within the first tab. I also expected to have a "Back" button in the navigation bar.
What happens:
Nothing in the first tab. But if I switch to the second tab it shows the page "ThreePage" but also without a "Back" button.
What is going wrong here???
I have attached my project here:
https://www.dropbox.com/s/fuu2wo5zqhp52gk/08-TabbedNavigation.zip?dl=0

Navigating via TabbedPages is broken in the latest Prism released version (8.0.0.1909).
[Bug] Navigation when in Tabbed Pages doesn't change currently selected tab #2218
(It seems they finally have fixed this issue but they haven't released a new version until now)
So downgrade Prism.DryIoc to 7.2.0.1422 or wait for the next Prism release...

Related

.NET Maui Binding a contentview to parent ViewModel MVVM

I have a xaml page that contains two instances of the same content view. The content view have a datepicker which should update a value in the parent view model ( each content view should update a different variable in the view model). I tried to do the bindiable property but it's not working. I set the BindingMode to TwoWay but that's not working.
The issue is that the binding is not working from the contentview to the parent viewmodel through the bindiable property. Any input is much appreciated.
Below is my code:
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
BackgroundColor="{DynamicResource PageBackgroundColor}"
xmlns:picker="clr-namespace:TestSync.View"
xmlns:viewmodel="clr-namespace:TestSync.ViewModel"
x:DataType="viewmodel:TimeTrackerViewModel"
x:Class="TestSync.MainPage">
<VerticalStackLayout>
<Label Text="{Binding SelectedDate}"/>
<Label Text="{Binding SelectedDate1}"/>
<picker:DateTimePickerContentView CardTitle="First DatePicker" CardDate="{Binding SelectedDate,Mode=TwoWay}" />
<picker:DateTimePickerContentView CardTitle="Second DatePicker" CardDate="{Binding SelectedDate1,Mode=TwoWay}" />
</VerticalStackLayout>
</ContentPage>
TimeTrackerViewModel.cs
namespace TestSync.ViewModel
{
public partial class TimeTrackerViewModel :ObservableObject
{
[ObservableProperty]
public DateTime selectedDate;
[ObservableProperty]
public DateTime selectedDate1;
}
}
DateTimePickerContentView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodel="clr-namespace:TestSync.View"
x:DataType="viewmodel:DateTimePickerContentView"
x:Class="TestSync.View.DateTimePickerContentView"
>
<VerticalStackLayout>
<Label Text="{Binding CardTitle}"/>
<DatePicker x:Name="myDate" Date="{Binding CardDate}" />
</VerticalStackLayout>
</ContentView>
and DateTimePickerContetntView.xaml.cs
namespace TestSync.View;
public partial class DateTimePickerContentView : ContentView
{
public static readonly BindableProperty CardTitleProperty = BindableProperty.Create(nameof(CardTitle), typeof(string), typeof(DateTimePickerContentView), string.Empty);
public string CardTitle
{
get => (string)GetValue(DateTimePickerContentView.CardTitleProperty);
set => SetValue(DateTimePickerContentView.CardTitleProperty, value);
}
public static readonly BindableProperty CardDateProperty = BindableProperty.Create(nameof(CardDate), typeof(DateTime), typeof(DateTimePickerContentView), defaultValue:DateTime.Parse("12/15/1992"),defaultBindingMode:BindingMode.TwoWay,propertyChanged:test);
private static void test(BindableObject bindable, object oldValue, object newValue)
{
var mytest= bindable as DateTimePickerContentView;
mytest.myDate.Date = (DateTime)newValue;
}
public DateTime CardDate
{
get => (DateTime)GetValue(DateTimePickerContentView.CardDateProperty);
set => SetValue(DateTimePickerContentView.CardDateProperty, value);
}
public DateTimePickerContentView()
{
InitializeComponent();
BindingContext = this;
}
}
I give you a workaround here.
For DateTimePickerContentView.xaml, define the BindingContext
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
...
x:Name="this">
<VerticalStackLayout BindingContext="{x:Reference this}">
<Label Text="{Binding CardTitle}"/>
<DatePicker x:Name="myDate" Date="{Binding CardDate}" />
</VerticalStackLayout>
</ContentView>
So for DateTimePickerContentView.cs, just delete this line
...
public DateTimePickerContentView()
{
InitializeComponent();
//BindingContext = this;
}
For data binding in a ContentView, you could refer to this official doc: Define the UI.
And if you want to set a default value, you should set it in TimeTrackerViewModel, because TimeTrackerViewModel's constructor execute after custom control set the default value. Then it will be replaced such as 1/1/1900 .
public TimeTrackerViewModel()
{
SelectedDate = DateTime.Parse("12/15/1992");
SelectedDate1 = DateTime.Parse("12/15/1992");
}
Hope it works for you.

xamarin.forms not showing ToolbarItem

I just created empty xamarin.forms project and nothing in it.
I searched on how to add Toolbar, in Microsoft documentation say add these line to xaml file.
<ContentPage.ToolbarItems>
<ToolbarItem Text="Example Item"
IconImageSource="example_icon.png"
Order="Primary"
Priority="0" />
</ContentPage.ToolbarItems>
But not toolbar is showing on my android device, this is really sad because this is my first time using xamarin and this is just empty project but it is not working.
this is my main xaml file
<?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:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="test3.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="sssssssss Item"
Order="Primary"
Priority="0" />
</ContentPage.ToolbarItems>
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Your App.xaml.cs should be similar to this:-
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new test3.MainPage());
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}

Nested views in tabs binding to separate ViewModel

Is there a way how to provide a nested view it's own viewmodel?
Example:
Master view of type TabbedView has multiple tabs.
<mvx:MvxTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Foo.Core.Pages.Access.MainPage"
xmlns:res="clr-namespace:Foo.Core.Resources;assembly=Foo.Core"
xmlns:mvx="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
xmlns:views="clr-namespace:Foo.Core.Pages.Access">
<TabbedPage.Children>
<views:LoginPage></views:LoginPage>
<views:RegisterPage></views:RegisterPage>
</TabbedPage.Children>
</mvx:MvxTabbedPage>
The LoginPage and RegisterPage are in separate views. But all the binding must be in the MainViewModel and I want the bindings to be separately in the LoginViewModel and RegisterViewModel.
Is there a way how to setup the binding to the properties to appropriate viewmodel? Preferably in XAML.
In order that to work you need to let the NavigationService (and therefore the Presenter) to load the children pages:
Xamarin.Forms View Presenter -> MvxTabbedPagePresentationAttribute
In your case it should be something like:
ViewModels
public class MyTabsContainerViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public MyTabsContainerViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService ?? throw new ArgumentNullException(nameof(navigationService));
}
public override async void ViewAppearing()
{
await ShowInitialViewModels();
base.ViewAppearing();
}
private async Task ShowInitialViewModels()
{
var tasks = new List<Task>();
tasks.Add(_navigationService.Navigate<LoginViewModel>());
tasks.Add(_navigationService.Navigate<RegisterViewModel>());
await Task.WhenAll(tasks);
}
}
public class LoginViewModel : MvxViewModel
{
}
public class RegisterViewModel : MvxViewModel
{
}
Views
MyTabsContainerPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<views:MvxTabbedPage x:TypeArguments="viewModels:MyTabsContainerViewModel" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:Foo.Core.ViewModels;assembly=Foo.Core"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="Foo.Core.Pages.Access.MyTabsContainerPage">
</views:MvxTabbedPage>
MyTabsContainerPage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
[MvxMasterDetailPagePresentation(Position = MasterDetailPosition.Detail, NoHistory = true)]
public partial class MyTabsContainerPage : MvxTabbedPage<MyTabsContainerViewModel>
{
public MyTabsContainerPage()
{
InitializeComponent();
}
}
LoginPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<views:MvxContentPage x:TypeArguments="viewModels:LoginViewModel" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:Foo.Core.ViewModels;assembly=Foo.Core"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="Foo.Core.Pages.Access.MixedNavTab1Page">
<StackLayout>
<Label Text="This is Tab 1" />
</StackLayout>
</views:MvxContentPage>
LoginPage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
[MvxTabbedPagePresentation(WrapInNavigationPage = false, Title = "LoginTab1")]
public partial class LoginPage : MvxContentPage<LoginViewModel>
{
public LoginPage()
{
InitializeComponent();
}
}
RegisterPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<views:MvxContentPage x:TypeArguments="viewModels:RegisterViewModel" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:Foo.Core.ViewModels;assembly=Foo.Core"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="Foo.Core.Pages.Access.RegisterPage">
<StackLayout>
<Label Text="This is Tab 2" />
</StackLayout>
</views:MvxContentPage>
RegisterPage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
[MvxTabbedPagePresentation(WrapInNavigationPage = false, Title = "RegisterTab2")]
public partial class RegisterPage : MvxContentPage<RegisterViewModel>
{
public RegisterPage()
{
InitializeComponent();
}
}
Full sample in the Playground project
HIH

How clicking barItem in Navigation Bar will bring new content page

[![enter image description here][1]][1]This is the MainPage which has a navigation Bar that contains 4 barItems.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="CheckList"
x:Class="mmy.View.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem Name="MenuItem1" Order="Primary" Icon="itemIcon1.png" Priority="0" />
<ToolbarItem Name="MenuItem2" Order="Primary" Icon="itemIcon2.png" Priority="1" />
<ToolbarItem Name="MenuItem1" Order="Primary" Icon="itemIcon3.png" Priority="2" />
<ToolbarItem Name="MenuItem2" Order="Primary" Icon="itemIcon4.png" Priority="3" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<Label Text="MainPage"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The Navigation Bar will look like below:
-------------------------------------------------------
Main barItem1 | barItem2 | barItem3 | barItem4
-------------------------------------------------------
Item1_Content
Say , I have 4 bar items in the Navigation bar
I want to do the following:
When user click barItem2 ,
a) it will bring in a Item2_Content which will replace the Item1_Content. ( I m not sure there is such UI )
b) the barItem (1 to 4 ) still remain in the navigation bar.
Is this doable?
Can you help me how to implement such user requirement?
Thanks
Update:
--(1) - start at P1 :
private async void BtnLogin_Clicked(object sender, EventArgs e)
{
NavigationPage NP = new NavigationPage(new MainPage())
{
BarBackgroundColor = Color.White,
BarTextColor = Color.DarkGray
};
}
--(2) I created a new page call RepairSvc for (3)
--(3) At MainPage : it has a navigation bar from (1)
Click the Icon="itemIcon1.png"
<ToolbarItem Name="MenuItem1" Order="Primary" Icon="itemIcon1.png" Command="Item1Command" Priority="0" />
Code Behind :
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
public ICommand Item1Command { get; private set; }
public MainPage()
{
InitializeComponent();
this.Item1Command = new Command((sender) =>
{
Navigation.PushAsync(new RepairSvc());
});
}
}
assign a command to your toolbar
<ToolbarItem Name="MenuItem1" Order="Primary" Icon="itemIcon1.png" Priority="0" Command="Item1Command" />
in your codebehind
this.Item1Command = new Command((sender) =>
{
Navigation.PushAsync(new MyPage());
});
}
public ICommand Item1Command { get; private set; }

MasterDetail navigation using prism

I recently started building a Xamarin Forms application using Prism.
I'm not able to navigate with the MasterDetail Navigation. The button I use to navigate seems to not make the binding correctly. I never been able to reach the executed command with a breakpoint when clicking on the button.
Everything except the command binding seems to do the binding correctly, so I really have no idea on what is going on.
I already checked out the GitHub sample made available by the Prism team (HamburgerMenu project). I'm convince to use the exact same configuration as the sample but no way to make it works on my project.
Bellow is the code used currently:
MainPage.xaml
<MasterDetailPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MonkeyVault.Views.MainPage">
<MasterDetailPage.Master>
<NavigationPage Title="Required Foo" Icon="ic_menu.png">
<x:Arguments>
<ContentPage Title="Menu">
<StackLayout Padding="40">
<Label Text="{Binding UserName, StringFormat='Hello, {0}'}"/>
<Button Text="Sites" Command="{Binding NavigateCommand}" CommandParameter="Navigation/Sites" />
</StackLayout>
</ContentPage>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Master>
</MasterDetailPage>
MainPageViewModel.cs
public class MainPageViewModel : BaseViewModel
{
#region Fields
private string _userName;
#endregion
#region Properties
public string UserName
{
get => _userName;
set => SetProperty(ref _userName, value);
}
public DelegateCommand<string> NavigateCommand;
public DelegateCommand NCommand;
#endregion
public MainPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "Main Page";
NavigateCommand = new DelegateCommand<string>(OnNavigateCommandExecuted);
}
private async void OnNavigateCommandExecuted(string path)
{
await _navigationService.NavigateAsync(path);
}
}
If someone has already encountered this problem or has any idea I would be greatful.
You need to create your DelegateCommand as a Property.
public DelegateCommand<string> NavigateCommand { get; set; }
Admittedly I am just guessing here, but I have had problems binding to Fields before and needed to change it to a property to get binding.

Resources