What is the recommended way of passing parameter info between tabs in a Shell app using MVVM?

TerryH 55 Reputation points
2023-01-21T23:37:31.07+00:00

I've got a Shell app (I'm not targeting mobile if it makes a difference) that has a main page where the user picks an item from a collectionview, and some strings are passed to the first tab in a TabBar. So, the user is either seeing the MainPage or a Page with a number of tabs on it. I'm using mvvm via the Community Toolkit so each page has a viewmodel injected into the class in the code behind, along with some other services being used.

When picking the item from the collection view on the MainPage, I'm calling Shell.Current.GoToAsync() in a RelayCommand and passing the two strings to the first tab by adding them to the URI. The viewmodel from Tab1 receives those, and assigns the ObservableObject properties and the page is populated. Ok so far.

Here is the problem: If the user clicks on Tab2, there does not seem to be a way to access the data from the current viewmodel in order to pass it to Tab2. There is a NavigatedTo event that gets fired, the event args do not have any public data - it's literally an empty event args. So, there is no way to know which tab was last selected, or get access to the properties on the viewmodel.

It seems like the correct way would be to call GoToAsync() but there is no way to get the strings from any viewmodel to pass. Trying to handle things in a NavigatedTo event got me nowhere. The action of just clicking a different tab ONLY fires the NavigatedTo event.

Is there something I'm missing? Is there a way to call a command in the viewmodel when switching tabs so I can just call GoToAsync() and pass the info along? Or bind to a command in the VM when declaring the tabs and routes in AppShell.xaml?

<?xml version="1.0" encoding="UTF-8" ?>
 <Shell
     x:Class="MyApp.AppShell"
     xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
     xmlns:pages="using:MyApp.Pages"
     FlyoutBehavior="Disabled">
     <ShellContent
             Title="Welcome"
             ContentTemplate="{DataTemplate pages:MainPage}"
             Route="MainPage" />
     
     <TabBar>
         <ShellContent Title="Tab1" Icon="tab1.png"
                       ContentTemplate="{DataTemplate pages:Tab1Page}"
                       Route="Tab1Page"/>
         <ShellContent Title="Tab2" Icon="tab2.png"
                       ContentTemplate="{DataTemplate pages:Tab2Page}"
                       Route="Tab2Page"/>
         <ShellContent
                       Title="Tab3" Icon="tab3.png"
                       ContentTemplate="{DataTemplate pages:Tab3Page}"
                       Route="Tab3Page"/>
     </TabBar>
 </Shell>

Should I be using TabbedPage or NavigationPage instead, and not use Shell at all? It feels like this really shouldn't be a difficult thing to pass some string data when clicking on tabs, but I'm either missing something or using the wrong architecture for what I want it to do.

I get it that calling GoToAsync() is the way to do this, but getting access to the data I need, is the problem.

Here's the basic view model. The viewmodels for tab2 and tab3 are similar.


[QueryProperty("Name", "Name")]
[QueryProperty("Path", "Path")]
public partial class Tab1ViewModel : ObservableObject
{
    [ObservableProperty]
    string name;

    [ObservableProperty]
    string path;
}   

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,834 questions
0 comments No comments
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 67,781 Reputation points Microsoft Vendor
    2023-01-23T06:41:29.1533333+00:00

    Hello,

    Shell do not have APIs which tab was last selected.

    If viewmodels for tabs are similar. You can create a view model that single instance.

    public static class VMInstance
    {
        private static SampleViewModel _sampleViewModel = new SampleViewModel();
        public static SampleViewModel SingleVM
        {
            get
            {
                return _sampleViewModel;
            }
        }
    }
    
    public class SampleViewModel: INotifyPropertyChanged
    {
           public string _name="test";
        public string Name
        {
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                             }
            }
            get
            {
                return _name;
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
            public SampleViewModel()
        {        }
    }
    

    Please bind the same viewmodel in the layout background code.

    public Tab1Page()
        {
            InitializeComponent();
            BindingContext = VMInstance.SingleVM;
        }
    public Tab2Page()
        {
            InitializeComponent();
            BindingContext = VMInstance.SingleVM;
        }
    

    Then binding the same property for controls in the content pages of tab. If this value of property changed, all of pages will be changed.

    For example, I binding Name property's Text in tabs pages. when name changed in tab1, tab2 will be changed as well.

    <Label             
    Text="{Binding Name}"
               />
    

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful