Set BindingContext to ViewModel in XAML on Xamarin.Forms - c#

I want to develop a simple project with Xamarin.Form and MVVM. In my solution (named XamarinPOC) i have (in addition to standard Xamarin.Forms projects) one separate project for the model (XamarinPOC.Model) and one separate project for the ViewModel (XamarinPOC.ViewModel).
I defined in a XamarinPOC.ViewModel project an abstract class for a BaseViewModel class (that implements the INotifyPropertyChanged Interface) and after I've created a SummaryViewModel class that extend BaseViewModel class with a simple property:
namespace XamarinPOC.ViewModel
{
public class SummaryViewModel : BaseViewModel
{
private string _test = "The binding is OK!";
public String test
{
get
{
return _test;
}
set
{
_test = value;
OnPropertyChanged("test");
}
}
public SummaryViewModel(){}
}
}
Next I created a simple ContentPage (SummatyView) in a XamarinPOC project that contain only a label that i want show the text defined in ViewModel. I want to use a XAML for defining the View and the binding but when I run the app nothing is displayed, I no errors on compile-time and runtime but the text are not displayed. My XAML is this
<?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:local="clr-namespace:XamarinPOC.ViewModel,assembly=XamarinPOC.ViewModel"
x:Class="XamarinPOC.Summary"
Title="Summary List"
BindingContext="XamarinPOC.ViewModel.SummaryViewModel">
<StackLayout>
<Label Text="{Binding test}"/>
</StackLayout>
</ContentPage>
and finally my app.cs is:
namespace XamarinPOC
{
public class App : Application
{
public App()
{
MainPage = new Summary();
}
}
}
In the XamarinPOC project I've added a reference to XamarinPOC.ViewModel and XamarinPOC.Model assemblies.
I think the problem is in the XAML definition of binding, but i don't find the error. Where am I wrong?

To bind the view to the viewmodel from Xaml in your case do it like this
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:XamarinPOC.ViewModel; assembly=XamarinPOC.ViewModel"
x:Class="XamarinPOC.Summary"
Title="Summary List">
<ContentPage.BindingContext>
<viewModels:SummaryViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="{Binding test}"/>
</StackLayout>
</ContentPage>
One side note I noticed is with naming conventions, it is better to put all your ViewModels, even if it is only one viewModel, inside a folder named "ViewModels" So the namespace in your case would be XamarinPOC.ViewModels

Related

Getting "Can not find the object referenced by" error during simplest Xamarin data binding

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}
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.
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

How to bind Listview to Observablecollection in a static class of another namespace

I reed a lot about namespace but still facing a problem / misunderstanding.
I would like to bind a Listview to an Observablecollection which resides in a static class in another namespace. It works but I can't succeed without code. I'm sure there is a better way to do this entirely in the XAML file.
So, this is my XAML file (...a part of - I delete a lot to be clear ...) :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ToolBox"
x:Class="ToolBox.MainPage">
<StackLayout>
<ListView x:Name="listToolBox">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"
Detail="{Binding CreateDate}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
I set up the binding context of the "listToolBox" in the code behind:
...
using ToolBox.Model;
namespace ToolBox
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
listToolBox.ItemsSource = ToolBox.Model.EBis.ToolBoxList;
}
...
It refers to a member of a static class:
namespace ToolBox.Model
{
public static class EBis
{
public static ObservableCollection<cDocuments> ToolBoxList = new ObservableCollection<cDocuments>
{
new cDocuments{ Id = -1, Name = "Pas (encore) de connection aux serveurs ..."}
};
...
MY QUESTION:
What are the binding instructions I have to write in the XAML elements in order to delete this line of code behind
listToolBox.ItemsSource = ToolBox.Model.EBis.ToolBoxList;
To be complete, the structure of my "Toolbox" APP is here:
Thanks a lot for your advice
Use the x:Static markup extension:
<ListView ItemsSource=“{x:Static local:Model.EBis.ToolBoxList}”>

How to set binding context to another assembly in XAML? Type not found

I am new to Xamarin/XAML and am trying to incorporate it into my project, but I'm getting "Type not found" errors when I try to put the BindingContext into the XAML, but it works if I set it in the C# constructor. Any ideas?
ProjectX.Forms.MainPage.cs
namespace ProjectX.Forms
{
public partial class MainPage
{
public MainPage( )
{
InitializeComponent( );
//this.BindingContext = new ProjectX.ViewModel.MainPage( );
}
}
}
ProjectX.Forms.Mainpage.xaml
<?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:viewModel="clr-namespace:ProjectX.ViewModel;assembly=projectx.viewmodel"
x:Class="ProjectX.Forms.MainPage"
Title="Project">
<ContentPage.BindingContext>
<viewModel:MainPage/>
</ContentPage.BindingContext>
<StackLayout>
<Entry Text="{Binding Input, Mode=TwoWay}"/>
<Label Text="{Binding Output}"
Font="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center">
</Label>
</StackLayout>
</ContentPage>
ProjectX.ViewModel.MainPage.cs
namespace ProjectX.ViewModel
{
public class MainPage : INotifyPropertyChanged
{
...
}
}
As is, I get the following error message:
Xamarin.Forms.Xaml.XamlParseException: Position 8:6. Type viewModel:MainPage not found in xmlns clr-namespace:ProjectX.ViewModel;assembly=projectx.viewmodel
If I uncomment the comment in ProjectX.Forms.MainPage and remove the BindingContext stuff from the xaml file, the error goes away.
What's the best way to do this using XAML without having to put the bindingcontext in the constructor?
Is the Name of the Assembly where the MainViewModel resides projectx.viewmodel? You can check that in the Project Properties.
It's confusing that the Page has the same name as the ViewModel. And it is impractical as you see, since you always have to reference it with the namespace (it's practice to name the ViewModel something like MainViewModel)

Why are UI components always private in Xamarin.Forms?

I'm building Xamarin.Forms application.
My View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestApp.Mobile.Views.MainView">
<Grid>
<Frame x:Name="TheFrame" />
</Grid>
</ContentPage>
Then In App.cs I want to access TheFrame. Like this:
MainView mainWindow = new MainView ();
someFunction(mainWindow.TheFrame);
But! This is how auto-generated MainView.cs looks like:
public partial class MainView : ContentPage {
private Frame MainFrame;
private void InitializeComponent() {
this.LoadFromXaml(typeof(MainView));
MainFrame = this.FindByName<Frame>("MainFrame");
}
}
WHY?! How can I access TheFrame in App.cs?
You would only be able to access the Frame from the code-behind using the x:Name property. Why do you need to access it from App.cs?
You could declare the Frame in App.cs and then add it to your page, which would allow it to be accessed from anywhere.

Xamarin.Forms accessing controls written in markup from Code

Im trying to add some items to a Listview which i added using Xamarin.Forms markup in an xaml file.
The button can be accessed by hooking with the click event.But since the listview is empty i need the event like ondraw like in winforms, so that i can hook to it when it is drawn.
In the XAML file I have :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonXaml.ButtonXamlPage">
<StackLayout>
<Button Text="Tap for click count!"
BorderWidth="10"
TextColor="Red"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />
<ListView
HorizontalOptions="Center"
/>
</StackLayout>
</ContentPage>
In the .cs file i have
using System;
using Xamarin.Forms;
namespace ButtonXaml
{
public partial class ButtonXamlPage
{
int count = 0;
public ButtonXamlPage()
{
InitializeComponent();
}
public void OnButtonClicked(object sender, EventArgs args)
{
((Button)sender).Text = "You clicked me";
}
}
}
So should i hook to events in Listview or can i do something like Resource.getElementbyID like we do in android
To access a Forms control in the code-behind, you need to assign it a name, using the x:Name attribute
in XAML:
<ListView HorizontalOptions="Center" x:Name="MyList" />
in code:
MyList.ItemsSource = myData;
There is a bug in Xamarin where VS doesn't see the defined x:Name
http://forums.xamarin.com/discussion/25409/problem-with-xaml-x-name-and-access-from-code-behind
Say you've defined an image in XAML:
<Image x:Name="myImageXName" />
Then this should work in code behind:
this.FindByName<Image>("myImageXName");
In my case the problem was lack of line XamlCompilation(XamlCompilationOptions.Compile)] in .xaml.cs file.
Example:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
...
}

Categories