1

I've got a very basic view.

<ContentPage x:Class="ThetaRex.InvestmentManager.Merlin.Views.ScenarioSelectionPage"
             Title="{Binding Title}"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns="http://xamarin.com/schemas/2014/forms">
    <StackLayout>
        <ListView ItemsSource="{Binding Items}"/>
    </StackLayout>
<ContentPage/>

The code behind is also very simple:

namespace ThetaRex.InvestmentManager.Merlin.Views
{
    using System.ComponentModel;
    using ThetaRex.InvestmentManager.Merlin.ViewModels;
    using Xamarin.Forms;

    public partial class ScenarioSelectionPage : ContentPage
    {
        public ScenarioSelectionPage()
        {
            InitializeComponent();
            this.BindingContext = this.ViewModel = new ScenarioSelectionViewModel();
        }

        public ScenarioSelectionViewModel ViewModel { get; set; }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            ViewModel.LoadItemsCommand.Execute(null);
        }
    }
}

Coming from a pure MVVM environment in WPF and UWP, I want to bind the view to the viewmodel in XAML, not using the this.Binding = ViewModel in the code behind. I've tried:

<ContentPage x:Class="ThetaRex.InvestmentManager.Merlin.Views.ScenarioSelectionPage"
             xmlns:controls="clr-namespace:ThetaRex.InvestmentManager.Merlin.Controls"
             BindingContext="{Binding ViewModel}"
             Title="{Binding Title}"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns="http://xamarin.com/schemas/2014/forms">

But it didn't work. How do I bind to the ViewModel from XAML?

Note: I know that I can create a view model from scratch in XAML, but it doesn't use the same instance that the code behind in the view uses, so that's not an option.

Quark Soup
  • 4,272
  • 3
  • 42
  • 74
  • Possible duplicate of [Set BindingContext to ViewModel in XAML on Xamarin.Forms](https://stackoverflow.com/questions/38552035/set-bindingcontext-to-viewmodel-in-xaml-on-xamarin-forms) – sermet Sep 30 '19 at 13:59
  • @sermet - It's not a duplicate. See the note. That solution creates a brand new ViewModel that isn't accessible to the View. The view has a requirement that it must be able to invoke methods on it's view model. For example, executing the **LoadItemsCommand** on initialization in the sample above. – Quark Soup Sep 30 '19 at 14:54
  • 1
    var vm = BindingContext as ScenarioSelectionViewModel; vm.LoadItemsCommand.Execute(null); – sermet Sep 30 '19 at 15:00

1 Answers1

0

If I understood what you want, the solution is build a ViewModelLocator like this:

  1. ViewModelLocalizator Class
public static class ViewModelLocalizator
{
    public static readonly BindableProperty AutoWireViewModelProperty =
           BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocalizator), default(bool), propertyChanged: OnAutoWireViewModelChanged);

    public static bool GetAutoWireViewModel(BindableObject bindable)
    {
        return (bool)bindable.GetValue(AutoWireViewModelProperty);
    }

    public static void SetAutoWireViewModel(BindableObject bindable, bool value)
    {
        bindable.SetValue(AutoWireViewModelProperty, value);
    }

    /// <summary>
    /// VERIFY THE VIEW NAME AND ASSOCIATE IT WITH THE VIEW MODEL OF THE SAME NAME. REPLACING THE 'View' suffix WITH THE 'ViewModel'
    /// </summary>
    private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (!(bindable is Element view))
        {
            return;
        }

        var viewType = view.GetType();

        var viewModelName = viewType.FullName.Replace(".Views.", ".ViewModels.").Replace("Page", "ViewModel");
        var viewModelType = Type.GetType(viewModelName);

        if (viewModelType == null) { return; }

        var vmInstance = Activator.CreateInstance(viewModelType);

        if (vmInstance != null)
        {
            view.BindingContext = vmInstance;
        }
    }
}
  1. Using It on your View
<ContentPage x:Class="YourProject.Views.YourTestPage"
             Title="{Binding Title}"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:viewModelBase="clr-namespace:YourProject.ViewModels;assembly=YouProject"
             viewModelBase:ViewModelLocalizator.AutoWireViewModel="true"
>
    <StackLayout>
        <ListView ItemsSource="{Binding Items}"/>
    </StackLayout>
<ContentPage/>
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459