3

I'm somewhat new to Xamarin development. Trying to accomplish a fairly simple task: in XAML, data-bind the menu page to a ViewModel that is injected into my menu-page.

Here's what my XAML looks like. Intellisense recognizes _viewModel and shows its properties further down in it the ListView

<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.openxlformats.org/markup-compatibility/2006"
            mc:Ignorable="d"
            BindingContext="{x:Reference_viewModel}"
            x:Class="SFC.Cliente.Mobile.Views.MenuPage"
            x:Name="Menu"
            Title="Menu">
  <StackLayout VerticalOptions="FillAndExpand">
     <ListView x:Name="ListViewMenu" HasUnevenRows="True" ItemsSource="{Binding MenuItems}">
        <ListView.ItemTemplate>
           <DataTemplate>
              <ViewCell>
                 <Grid Padding="10">
                    <Label Text="{Binding Title}" FontSize="20"/>
                 </Grid>
              </ViewCell>
           </DataTemplate>
        </ListView.ItemTemplate>
     </ListView>
  </StackLayout>
</ContentPage>

Here's what my code behind looks like. ViewModel is injected into page's code-behind without issues and is not null

namespace SFC.Client.Mobile.Views
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(visible:false)]
    public partial class MenuPage : ContentPage
    {
        // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
        private readonly MenuItemsViewModel _viewModel;

        public MenuPage(MenuItemsViewModel viewModel)
        {
            _viewModel = viewModel;

            InitializeComponent();

            ListViewMenu.ItemSelected += async (sender, e) =>
            {
                if (e.SelectedItem == null)
                    return;

                var id = ((HomeMenuItem) e.SelectedItem).Id;
                var rootPage = (MainPage) Application.Current.MainPage;
                await rootPage.NavigateFromMenu(id);
            };
        }
    }
}

Here's the exception that I am getting during InitializeComponent(). I would like to wire data-binding thru XAML, not thru code. I've tried making _viewModel to be public or private, property or member: no change. What am I doing wrong?

{System.Collections.ListDictionaryInternal}
-2146233088
(null)
(null)
"Position 7:14. Can not find the object referenced by _viewModel"
"Xamarin.Forms.Xaml"
" at Xamarin.Forms.Xaml.Reference.ProvideValue(System.IServiceProvider serviceProvider) [0..."
{System.Reflection.MonoMethod}

Igorek
  • 15,716
  • 3
  • 54
  • 92
  • 2
    Why images instead of code? – krobelusmeetsyndra Jul 18 '19 at 18:42
  • cuz SO wouldn't allow me to past XAML even when using Ctrl-K, so I gave up trying to understand why and pasted screenshots :) I've been on this issue for the last 2 days and read every data-binding article and watched every video already.. frustrated – Igorek Jul 18 '19 at 18:44
  • I think you'll need to have a public field, so instead of using the `_viewModel`, create a public property `ViewModel` – Odrai Jul 18 '19 at 18:44
  • @Odrai tried public – Igorek Jul 18 '19 at 18:44
  • 1
    @Igorek Maybe the [Set BindingContext to ViewModel](https://stackoverflow.com/questions/38552035/set-bindingcontext-to-viewmodel-in-xaml-on-xamarin-forms) post is useful? – Odrai Jul 18 '19 at 18:45
  • @Odrai Yes, their approach worked, however, I cannot access the XAML-defined ViewModel object from code-behind...? – Igorek Jul 18 '19 at 18:54
  • 1
    I've edited the question to remove the images and put them in text. Everyone's data will be pleased. – krobelusmeetsyndra Jul 18 '19 at 19:04
  • 1
    you should be able to cast the page's BindingContext to your VM – Jason Jul 18 '19 at 19:10
  • Yeah.. however, now it appears that AutoFac is not being used to resolve ViewModel's dependencies... off to research that part now – Igorek Jul 18 '19 at 19:16
  • Is the issue solved now? – Leo Zhu Jul 19 '19 at 01:10

2 Answers2

2

I have to agree with Ivan Ičin above.

I am not sure why it is so important to set the binding context on the XAML when you can easily just do this:

public partial class MenuPage : ContentPage
{
    // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
    // private readonly MenuItemsViewModel _viewModel;

    public MenuPage(MenuItemsViewModel viewModel)
    {
        BindingContext = viewModel; // <---------- changed line

        ...
    }
}

UPDATE: found possible solution.

Set a static property on your page, e.g.:

public static MenuItemsViewModel BindingContextInstance { get; set; } = null;

Assign your view model to the above static member BEFORE you call InitializeComponent():

public MenuPage(MenuItemsViewModel viewModel)
{
    BindingContextInstance = viewModel;
    InitializeComponent();
      ...
} 

Then in the XAML, add a new xmlns entry and set BindingContext:

xmlns:views="clr-namespace:SFC.Client.Mobile.Views"
BindingContext="{x:Static views:MenuPage.BindingContextInstance}"

Note: This does not work for auto complete in the current stable version of Visual Studio for Mac (8.1.5.9), but does work in the current Preview version (8.2) and in the current version of Visual Studio 2019 (16.1.6) on Windows. Set your update channel to Preview in Visual Studio for Mac in the Visual Studio Update dialog and when the updates are downloaded, Restart and Install updates.

jgoldberger - MSFT
  • 5,978
  • 2
  • 20
  • 44
0

I don't think that you can bind like that. Here is an example of how view models are bound in XAML: Set BindingContext to ViewModel in XAML on Xamarin.Forms

Ivan Ičin
  • 9,672
  • 5
  • 36
  • 57