Bind controls with Model object in ViewModel class in Xamarin forms - xamarin

I am learning MVVM in Xamarin forms using Prism. I have implemented a login functionality which uses a User model class. But the bindings are not working. Please review the code and suggest corrections.
I am not sure how to bind the control's text property to the Model class object's properties.
LoginPage.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:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="LeaveTracker.Views.LoginPage"
Title="{Binding Title}">
<StackLayout Orientation="Vertical" BindingContext="{Binding UserObj}">
<Entry Placeholder="User ID" Text="{Binding UserID}"/>
<Entry Placeholder="Password" Text="{Binding Password}" IsPassword="True"/>
</StackLayout>
</ContentPage>
LoginPageViewModel.cs
public class LoginPageViewModel : ViewModelBase
{
private User _user;
private IFirebaseService _firebaseService;
public User UserObj
{
get { return _user; }
set { SetProperty(ref _user, value); }
}
public DelegateCommand LoginCommand { get; set; }
public LoginPageViewModel(IFirebaseService firebaseService, INavigationService navigationService) : base(navigationService)
{
Title = "Log In";
_firebaseService = firebaseService;
LoginCommand = new DelegateCommand(Login, CanLogin);
}
private void Login()
{
var x = _firebaseService.LoginAsync(_user);
}
private bool CanLogin()
{
if (string.IsNullOrEmpty(_user.UserID) && string.IsNullOrEmpty(_user.Password))
{
return true;
}
return false;
}
User.cs
public class User
{
private string _userID;
public string UserID
{
get { return _userID; }
set { _userID = value; }
}
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
}

your BindingContext is LoginPageViewModel, and UserObj is a property of the VM, so your binding path needs to include UserObj
<Entry Placeholder="User ID" Text="{Binding UserObj.UserID}"/>
<Entry Placeholder="Password" Text="{Binding UserObj.Password}" IsPassword="True"/>

Related

Xamarin CollectionView Observable Collection not updating with Searchbar

My Accounts CollectionView is not updating with the Searchbar. Xaml below.
<?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:viewmodels="clr-namespace:Pricing051721.ViewModels"
x:Class="Pricing051721.MainPage" Title="KR Pricing"
x:Name="This">
<ContentPage.BindingContext>
<viewmodels:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button Text="Logout" Command="{Binding LogoutCommand}" Margin="0,5,0,5"/>
<SearchBar x:Name="searchBar"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference searchBar}}"/>
<CollectionView ItemsSource="{Binding Accounts}" Margin="5">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5" >
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.AccountSelected, Source={x:Reference This}}" CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<StackLayout >
<Label FontSize="Medium" Text="{Binding Name}" ></Label>
<Label Text="{Binding Address}"></Label>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
I am trying to search through the Accounts already queried in the view model so I don't have to hit the database again. The search works, but Accounts is not updated.
namespace Pricing051721.ViewModels
{
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<Account> Accounts { get; set; }
public INavigation Navigation { get; set; }
public ICommand LogoutCommand { get; set; }
AdAuthenticationService authService;
public ObservableCollection<Account> baseAccountList;
public MainPageViewModel()
{
Accounts = new ObservableCollection<Account> { new Account { AllowUpdate = true, Address = "Wait", Name = "Loading" } };
authService = new AdAuthenticationService();
Task.Run(async () =>
{
if (!authService.IsAuthenticated)
{
var response = authService.Authenticate();
await Update(response.AccessToken, "");
}
else await Update(authService.AccessToken, "");
});
AccountSelected = new Command<Account>(async (a) =>
{
if (!a.AllowUpdate)
return;
await Navigation.PushAsync(new UpdateAccountView(a));
return;
var result = await UserDialogs.Instance.PromptAsync(new PromptConfig
{
InputType = InputType.Name,
OkText = "Change",
Title = "Enter New Column Break",
Text = a.ColumnBreak
});
if (result.Ok && result.Text != null && !result.Text.Trim().Equals(""))
{
a.ColumnBreak = result.Text;
isUpdating = true;
var ok = await crm.Update(a);
var message = ok ? "Account Updated!" : "Unable to update!";
await UserDialogs.Instance.AlertAsync(new AlertConfig
{
Title = "Message",
Message = message,
OkText = "Ok"
});
isUpdating = false;
}
}, _ => !isUpdating);
LogoutCommand = new Command(new Action(() => {
authService.Logout();
Environment.Exit(Environment.ExitCode);
}));
}
//search
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand PerformSearch => new Command<string>((string query) =>
{
Accounts = SearchAccounts(query);
});
private bool isUpdating = false;
private Crm crm;
public ObservableCollection<Account> accounts;
public async Task Update(string accessToken, string query)
{
Crm.Setup(accessToken);
crm = Crm.AuthenticatedCrmService;
var accounts = await crm.GetAccounts();
Accounts.RemoveAt(0);
accounts.ForEach(a => Accounts.Add(a));
}
public ObservableCollection<Account> SearchAccounts(string query)
{
Task.Run(async () =>
{
if (!authService.IsAuthenticated)
{
var response = authService.Authenticate();
await Update(response.AccessToken, "");
}
else await Update(authService.AccessToken, "");
});
baseAccountList = Accounts;
if (!(query == ""))
{
var normalizedQuery = query?.ToLower() ?? "";
List<Account> accountsList = (List<Account>)Accounts.Where(f => f.Name.ToLowerInvariant().Contains(normalizedQuery)).ToList();
ObservableCollection<Account> accounts = new ObservableCollection<Account>(accountsList);
Accounts.Clear();
return accounts;
}
else
{
accounts = Accounts;
return accounts;
}
}
public ICommand AccountSelected { get; set; }
}
}
I don't need a neat solution (as you can tell by my code so far), just something that will work Thanks in advance!
My Accounts CollectionView is not updating with the Searchbar
From your code, you don't post some code about PerformSearch command, I don't know how do you search data by searchbar. I do one sample about search some data by searchbar, display in collectionview, you can modify your code according to the following code.
<SearchBar
x:Name="searchBar"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference searchBar}}" />
<CollectionView Margin="5" ItemsSource="{Binding Accounts}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<!--<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.AccountSelected, Source={x:Reference This}}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>-->
<StackLayout>
<Label FontSize="Medium" Text="{Binding Name}" />
<Label Text="{Binding Address}" />
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public partial class Page15 : ContentPage
{
public Page15()
{
InitializeComponent();
this.BindingContext = new AccountViewModel();
}
}
public class AccountViewModel
{
public ObservableCollection<Account> AccountList { get; set; }
public ObservableCollection<Account> Accounts { get; set; }
public ICommand PerformSearch { get; set; }
public AccountViewModel()
{
AccountList = new ObservableCollection<Account>();
Accounts = new ObservableCollection<Account>();
for(int i=0;i<30;i++)
{
Account a = new Account();
a.Name = "account" + i;
a.Address = "address " + i;
AccountList.Add(a);
Accounts.Add(a);
}
PerformSearch = new Command(search => {
if(search!=null)
{
string searchtext = (string)search;
if (!string.IsNullOrEmpty(searchtext))
{
Accounts.Clear();
List<Account> list= AccountList.Where((account) => account.Name.ToLower().Contains(searchtext) || account.Address.ToLower().Contains(searchtext)).ToList();
foreach(Account a in list)
{
Accounts.Add(a);
}
}
Accounts = AccountList;
}
else
{
Accounts = AccountList;
}
});
}
}
public class Account
{
public string Name { get; set; }
public string Address { get; set; }
}

Create a xamarin view with a complex object binding

I use XamarinForms and the plugin ValidationRules.
I have an account model, a login page which is linked to the accountviewmodel. And in my loginpage I add a xamarin view FormEntry which has a bindable property Validation of type Validatable<string>.
The bindable property has some properties I want to use: Value, Error and hasError property. I put the binding in my LoginPage and then the binding is null in my FormEntry content view. If I get the value of the validation object it's null in my formentry textchanged event.
Account Model
public ValidatableObject<string> Password { get; set; }
public ValidatableObject<string> Email { get; set; }
public Account()
{
Password = new ValidatableObject<string>();
Email = new ValidatableObject<string>();
}
private void AddValidations()
{
// Email validations
Email.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = C.T("Email is required.") });
Email.Validations.Add(new EmailRule<string> { ValidationMessage = C.T("Email is not valid.") });
//Password validations
Password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = C.T("A password is required.") });
Password.Validations.Add(new LengthMinRule<string> { ValidationMessage = C.T("A password is required."), MinLength = 6 });
}
LoginPage
<local:FormEntry Validation="{Binding AccountInfo.Email}" Grid.Row="2" />
FormEntry Xaml
<ContentView.Resources>
<converter:ErrorValidationColorConverter x:Key="errorValidationColorConverter" />
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
</ContentView.Resources>
<ContentView.Content>
<!--<border:SfBorder Grid.Row="2"
Style="{DynamicResource SfBorderStyle}"
BorderColor="{Binding Source={x:Reference emailEntry}, Path=IsFocused, Converter={StaticResource errorValidationColorConverter}, ConverterParameter={x:Reference emailEntry}}">-->
<inputLayout:SfTextInputLayout x:Name="Input" LeadingViewPosition="Outside" TrailingViewPosition="Outside" ContainerType="None">
<Entry TextChanged="Entry_TextChanged" x:Name="entry"
Placeholder="Email">
</Entry>
<inputLayout:SfTextInputLayout.LeadingView>
<Label
Text="" FontFamily="{StaticResource FontAwesomeSolid}">
</Label>
</inputLayout:SfTextInputLayout.LeadingView>
<inputLayout:SfTextInputLayout.TrailingView>
<Label
x:Name="LabelError"
Text=""
FontFamily="{StaticResource FontAwesomeSolid}"
TextColor="{DynamicResource ErrorColor}"
FontSize="22">
</Label>
</inputLayout:SfTextInputLayout.TrailingView>
</inputLayout:SfTextInputLayout>
<!--</border:SfBorder>-->
</ContentView.Content>
My FormEntry cs
public FormEntry()
{
InitializeComponent();
}
public ICommand ValidateUserNameCommand => new Command(() =>
ValidateField());
private bool ValidateField()
{
return Validation.Validate();
}
public static readonly BindableProperty ValidationProperty =
BindableProperty.Create(
propertyName: "Validation",
returnType: typeof(ValidatableObject<string>),
declaringType: typeof(FormEntry),
defaultValue: default(ValidatableObject<string>));
public ValidatableObject Validation
{
get
{
return (ValidatableObject)GetValue(ValidationProperty);
}
set
{
SetValue(ValidationProperty, value);
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == ValidationProperty.PropertyName)
{
Input.ErrorText = Validation.Error;
Input.HasError = Validation.Validate();
entry.Text = Validation.Value;
LabelError.IsVisible = Input.HasError;
}
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
ValidateField();
}
}

How can I pass a command to a template and have it execute in my back end code and pass the parameter?

I have this template:
<?xml version="1.0" encoding="utf-8"?>
<Grid Padding="20,0" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.DataGridTemplate"
x:Name="this" HeightRequest="49" Margin="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand, Source={x:Reference this}}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="{Binding Test" />
</Grid>
Behind this I have:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
"Command",
typeof(ICommand),
typeof(DataGridTemplate),
null);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
}
and I am trying to call the template like this in file: Settings.xaml.cs
<template:DataGridTemplate TapCommand="openCFSPage" />
hoping that it will call my method here in file: Settings.cs
void openCFSPage(object sender, EventArgs e)
{
Navigation.PushAsync(new CFSPage());
}
The code compiles but when I click on the grid it doesn't call the openCFSPage method.
1) Does anyone have an idea what might be wrong?
2) Also is there a way that I can add a parameter to the template and then have that parameter passed to my method in the CS back end code?
Note that I would like to avoid adding a view model if possible. The application is small and I'd like to just have the code I need in the CS code of the page that calls the template.
Please note that the simplest way to implement this would be through MVVM (i.e. a view-model), but if you want to side-step this option (as you mentioned in the question) then you can use one of the following options
Option1 : Wrap delegate into command object
If you look at it from the perspective of a XAML parser, you are technically trying to assign a delegate to a property of type ICommand. One way to avoid the type mismatch would be to wrap the delegate inside a command-property in the page's code-behind.
Code-behind [Settings.xaml.cs]
ICommand _openCFSPageCmd;
public ICommand OpenCFSPageCommand {
get {
return _openCFSPageCmd ?? (_openCFSPageCmd = new Command(OpenCFSPage));
}
}
void OpenCFSPage(object param)
{
Console.WriteLine($"Control was tapped with parameter: {param}");
}
XAML [Settings.xaml]
<!-- assuming that you have added x:Name="_parent" in root tag -->
<local:DataGridView TapCommand="{Binding OpenCFSPageCommand, Source={x:Reference _parent}}" />
Option2 : Custom markup-extension
Another option (a bit less mainstream) is to create a markup-extension that wraps the delegate into a command object.
[ContentProperty("Handler")]
public class ToCommandExtension : IMarkupExtension
{
public string Handler { get; set; }
public object Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
var lineInfo = (serviceProvider?.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo ?? new XmlLineInfo();
object rootObj = Source;
if (rootObj == null)
{
var rootProvider = serviceProvider.GetService<IRootObjectProvider>();
if (rootProvider != null)
rootObj = rootProvider.RootObject;
}
if(rootObj == null)
{
var valueProvider = serviceProvider.GetService<IProvideValueTarget>();
if (valueProvider == null)
throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
//we assume valueProvider also implements IProvideParentValues
var propInfo = valueProvider.GetType()
.GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(propInfo == null)
throw new ArgumentException("valueProvider does not provide an ParentObjects");
var parentObjects = propInfo.GetValue(valueProvider) as IEnumerable<object>;
rootObj = parentObjects?.LastOrDefault();
}
if(rootObj != null)
{
var delegateInfo = rootObj.GetType().GetMethod(Handler,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(delegateInfo != null)
{
var handler = Delegate.CreateDelegate(typeof(Action<object>), rootObj, delegateInfo) as Action<object>;
return new Command((param) => handler(param));
}
}
throw new XamlParseException($"Can not find the delegate referenced by `{Handler}` on `{Source?.GetType()}`", lineInfo);
}
}
Sample usage
<local:DataGridView TapCommand="{local:ToCommand OpenCFSPage}" />
You have 2 options depending on the the use case :
FYI, there's no way to call another method directly from the view (its a bad design pattern to do so)
Using Event Aggregator :
Create interface
public interface IEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}
All you have to do is call it from you TapCommand
_eventAggregator.GetEvent<ItemSelectedEvent>().Publish(_selectedItem);
Then in your Settings.cs you can Create a method that can receive the data
this.DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
Inheritance and Polymorphism / Making openCFSPage a service :
Creating a interface / service that links both models
public interface IOpenCFSPage
{
Task OpenPage();
}
and a method :
public class OpenCFSPage : IOpenCFSPage
{
private INavigationService _navigationService;
public OpenCFSPage(INavigationService navigationService){
_navigationService = navigationService;
}
public async Task OpenPage()
{
await _navigationService.NavigateAsync(new CFSPage());
}
}
Settings.xaml:
<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" />
<!-- Uncomment below and corresponding parameter property code in DataGridTemplate.xaml.cs to pass parameter from Settings.xaml -->
<!--<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" CommandParameter="A" />-->
Settings.xaml.cs:
public Settings()
{
InitializeComponent();
OpenCFSPage = new Command(p => OpenCFSPageExecute(p));
BindingContext = this;
}
public ICommand OpenCFSPage { get; private set; }
void OpenCFSPageExecute(object p)
{
var s = p as string;
Debug.WriteLine($"OpenCFSPage:{s}:");
}
DataGridTemplate.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
Padding="0,20"
HeightRequest="49" Margin="0"
x:Class="Japanese.DataGridTemplate">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Test" />
</Grid>
DataGridTemplate.xaml.cs:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
nameof(TapCommand), typeof(ICommand), typeof(DataGridTemplate), null,
propertyChanged: OnCommandPropertyChanged);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
//public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
// nameof(CommandParameter), typeof(string), typeof(DataGridTemplate), null);
//public string CommandParameter
//{
// get { return (string)GetValue(CommandParameterProperty); }
// set { SetValue(CommandParameterProperty, value); }
//}
static TapGestureRecognizer GetTapGestureRecognizer(DataGridTemplate view)
{
var enumerator = view.GestureRecognizers.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
if (item is TapGestureRecognizer) return item as TapGestureRecognizer;
}
return null;
}
static void OnCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is DataGridTemplate view)
{
var tapGestureRecognizer = GetTapGestureRecognizer(view);
if (tapGestureRecognizer != null)
{
tapGestureRecognizer.Command = (ICommand)view.GetValue(TapCommandProperty);
//tapGestureRecognizer.CommandParameter = (string)view.GetValue(CommandParameterProperty);
}
}
}
}
Check this code you help you. Here you have to pass a reference of list view and also you need to bind a command with BindingContext.
<ListView ItemsSource="{Binding Sites}" x:Name="lstSale">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Label Text="{Binding FriendlyName}" />
<Button Text="{Binding Name}"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding
Path=BindingContext.RoomClickCommand,
Source={x:Reference lstSale}}"
CommandParameter="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

freshmvvm command parameter

I have a Command with CommandParameter in my Page but when is trigger for PageModel value is null and I not what´s up.
I have a Model "UserLogin":
[AddINotifyPropertyChangedInterface]
public class UserLogin
{
public string Username { get; set; }
public string Password { get; set; }
public UserLogin(string username, string password)
{
Username = username;
Password = Password;
}
public UserLogin()
{
}
}
and my respective PageModel is "LoginPageModel":
[AddINotifyPropertyChangedInterface]
public class LoginPageModel : FreshBasePageModel
{
IDatabaseService _databaseService;
IUserDialogs _userDialogs;
IRestService _restService;
public LoginPageModel(IDatabaseService databaseService, IRestService restService, IUserDialogs userDialogs) //injected from IOC
{
_databaseService = databaseService;
_restService = restService;
_userDialogs = userDialogs;
}
private UserLogin _userLogin;
public UserLogin UserLogin
{
get
{
return _userLogin;
}
set
{
_userLogin = value;
if (value != null)
RaisePropertyChanged();
}
}
public Command LoginCommand
{
get
{
return new Command<UserLogin>(async (log) =>
{
....
}
and my respective Page is "LoginPage.xaml":
<ContentPage.Content>
<StackLayout Padding="20" Spacing="5">
<Entry x:Name="Username" Text="{Binding Username}" Keyboard="Email" Placeholder="Username" />
<Entry x:Name="Password" Text="{Binding Password}" Placeholder="Password" IsPassword="true" />
<Button x:Name="btn_Login" Command="{Binding LoginCommand}" CommandParameter="{Binding UserLogin}" Text="Login" />
</StackLayout>
</ContentPage.Content>
What´s wrong in my code for parameter is null?
Thanks People

Xamarin Forms + Mvvmcross binding command not work

I am an new Xamarin Form. I created a simple xamarin forms project with mvvmcross (Hello World very simple for begin), but when i implemented binding command, and not effect change text of label. My Xaml code and ViewModel below.
<?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:vm="clr-namespace:MvvmCross.ViewModels;assembly=MvvmCross"
x:Class="MvvmCross.Views.HelloView">
<StackLayout>
<StackLayout.BindingContext>
<vm:HelloViewModel />
</StackLayout.BindingContext>
<Entry HorizontalOptions="Fill" VerticalOptions="Center" Text="{Binding Name, Mode=TwoWay }"/>
<Button Text="Hello" HorizontalOptions="Center" VerticalOptions="Center" Command="{Binding HelloCommand}" />
<Label HorizontalOptions="Fill" VerticalOptions="Center" FontSize="15" Text="{Binding Hello, Mode=TwoWay}" />
</StackLayout>
using MvvmCross.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MvvmCross.ViewModels
{
public class HelloViewModel: Core.ViewModels.MvxViewModel
{
private string _name;
public HelloViewModel()
{
Hello = "Your name";
}
public string Name
{
get { return _name; }
set { _name = value; RaisePropertyChanged(() => Name); }
}
private string _hello;
public string Hello
{
get { return _hello; }
set { _hello = value; RaisePropertyChanged(() => Hello); }
}
private ICommand _helloCommand;
public ICommand HelloCommand
{
get { _helloCommand = _helloCommand ?? new MvxCommand(ShowHello); return _helloCommand; }
}
private void ShowHello()
{
// not change label text so sadly
Hello = Name.ToString();
Debug.WriteLine(Hello);
}
}
}
Thank for all helping
Even if late, it could help someone else.
if you have set up correctly MvvmCross on your Xamarin Forms project (review [Getting Started with MvvmCross][1]) you don't need to specifically set the BindigContext, neither in the view nor in the view model.
About the question, simple example of the use of the button's command binding:
view
<views:MvxContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr- namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="TestProject.Pages.TestPage">
<ContentView>
<StackLayout>
<Button Text="Test first command!" Command="{Binding TestFirstCommand}"/>
<Button Text="Test second command!" Command="{Binding TestSecondCommand}"/>
<Label Text="{Binding AnyText}"/>
</StackLayout>
</ContentView>
view model
namespace TestProject.ViewModels
{
public class TestViewModel : MvxNavigationViewModel
{
private string _AnyTest;
public TestViewModel()
{
AnyText = "";
}
public string AnyText { get => _AnyTest; set => SetProperty(ref _AnyTest, value); }
public Command TestFirstCommand => new Command(TestFirstCommandMethod);
public Command TestSecondCommand => new Command(TestSecondCommandMethod);
private void TestFirstCommandMethod()
{
AnyText = "Hello!";
}
private void TestSecondCommandMethod()
{
AnyText = "How are you?";
}
}
}
Has u set the BindingContext?
In your HelloView.xaml.cs:
public HelloView() {
BindingContext = new HelloViewModel();
}
I'm on mobile, really hard to type..

Resources