How do you get View Page from its associated ViewModel programmatically?

dg2k 1,391 Reputation points
2023-05-06T17:02:50.2533333+00:00

There are rare occasions where I want to get a View Page object from its own ViewModel.
Don't worry about the oft-said warning of: "don't break the MVVM pattern". I just want to get the View Page from its ViewModel programmatically.

I can do this in a round-about way (such as passing View Page's this object to ViewModel with with PageAppearing() or navigation event, but there must be a more concise programmatic way.

How do you get View Page from its associated ViewModel programmatically?

Example:

  • View Page is MainPage mainPag ViewModel is MainPageViewModel mainPageViewModel
  • mainPage.BindingContext = mainPageViewModel
  • Then, how to get the mainPage object from mainPageViewModel object?
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,066 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,497 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 70,811 Reputation points Microsoft Vendor
    2023-05-07T02:32:44.71+00:00

    Hello,

    You can do this by Ioc (Inversion of control)

    Firstly, install the CommunityToolkit.Mvvm nugget package.

    Then, create an interface for pag.

    public interface IPage
    {
        public Page GetCurrentPage();
    }
    

    Next, Configure and resolve services in the App.xaml.cs.

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            Services = ConfigureServices();
            MainPage = new AppShell();
        }
    
       /// <summary>
        /// Gets the current <see cref="App"/> instance in use
        /// </summary>
        public new static App Current => (App)Application.Current;
        public IServiceProvider Services { get; }
    
       /// <summary>
        /// Configures the services for the application.
        /// </summary>
        private static IServiceProvider ConfigureServices()
        {
            var services = new ServiceCollection();
           services.AddSingleton<IPage, MainPage>();
           return services.BuildServiceProvider();
        }
    }
    

    Open your MainPage's background. You can implement the IPage and return this Mainpage in GetCurrentPage method.

    public partial class MainPage : ContentPage, IPage
    {
       public MainPage()
        {
            InitializeComponent();
          
        }
    
    protected override void OnAppearing()
        {
            base.OnAppearing();
            BindingContext = new MyViewModel();
        }
    
       public Page GetCurrentPage()
        {
            return this;
        }
    

    In the end, you can get the MainPage in the Viewmodel.

    internal class MyViewModel
        {
            public ICommand MyCommand { get; set; }
            public MyViewModel()
            {
               MyCommand = new Command((e) =>
                {
                   IPage page = App.Current.Services.GetService<IPage>();
                    var mainPage= page.GetCurrentPage();
                });
              
            }
        }
    

    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.


  2. dg2k 1,391 Reputation points
    2023-05-08T05:10:27.54+00:00

    @Leon Lu (Shanghai Wicresoft Co,.Ltd.) @L

    The solution was under my nose, but didn't see it.

    • In the base class of ViewModel, usually public partial class BaseViewModel : ObservableObject
      define a property public Page ViewPage { get; set; }
    • In view page constructor while setting the BindingContext set the ViewPage to the instantiated view page object.
    private readonly MainPageViewModel viewModel;
    
    public MainPage(MainPageViewModel vm)
    {
    	InitializeComponent();
    	BindingContext = this.viewModel = vm;
    	vm.ViewPage = this;
    }
    

    Now, every ViewModel has the object of its view page set to its ViewPage property. In hindsight, it's that easy!