I have a simple Converter which converts an array of string values to single string object i.e. joins all the strings using a separator. When the app is run, it hits the breakpoint in the value converter but not thereafter. It's not showing the concatenated string in a label as expected.
View:
<ContentPage
x:Class="mvvm.Views.LoginPage"
xmlns:converters="clr-namespace:XFApp.Core.Converters;assembly=XFApp.Core">
<ContentPage.Resources>
<ResourceDictionary>
<converters:ValidationErrorConverter x:Key="validationErrorConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Padding="10,100,10,10">
<StackLayout>
<Label
Text="{Binding Errors, Converter={StaticResource validationErrorConverter}}" />
<Label Text="User name:" />
<Entry x:Name="entryUsername" Keyboard="Email" Text="{Binding Username, Mode=TwoWay}" Style="{StaticResource EntryStyle}" />
<Label Text="Password:" />
<Entry x:Name="entryPassword" IsPassword="true" Text="{Binding Password, Mode=TwoWay}" Style="{StaticResource EntryStyle}" />
</StackLayout>
<Button VerticalOptions="EndAndExpand" Text="LOGIN" Command="{Binding AuthenticateUserCommand}" />
</StackLayout>
</ContentPage>
View Code Behind:
using mvvm.ViewModels;
using Xamarin.Forms;
namespace mvvm.Views
{
public partial class LoginPage : ContentPage
{
LoginViewModel viewModel = new LoginViewModel();
public LoginPage()
{
InitializeComponent();
this.BindingContext = viewModel;
}
}
}
ViewModel:
using System.Windows.Input;
using Xamarin.Forms;
using mvvm.Models;
using System.Collections.Generic;
using XFApp.Core.ViewModels;
namespace mvvm.ViewModels
{
public class LoginViewModel : ViewModelBase
{
private string _username;
private string _password;
private bool _isValid;
private IList<string> _errors;
public LoginViewModel()
{
_username = "";
_password = "";
_errors = new List<string>();
}
public IList<string> Errors
{
set { SetProperty(ref _errors, value); }
get { return _errors; }
}
public string Username {
set {
SetProperty(ref _username, value);
((Command)AuthenticateUserCommand).ChangeCanExecute();
}
get { return _username; }
}
public string Password
{
set {
SetProperty(ref _password, value);
((Command)AuthenticateUserCommand).ChangeCanExecute();
}
get { return _password; }
}
public bool IsValid
{
set { SetProperty(ref _isValid, value); }
get { return _isValid; }
}
public ICommand AuthenticateUserCommand => new Command(
execute: () => {
AuthenticateUserAsync();
}
//,
//canExecute: () => {
// return (Username.Length > 0);
//}
);
private bool Validate()
{
if (_username == "")
{
_errors.Add("An e-mail address is required.");
Errors = _errors;
}
if (_password == "")
{
_errors.Add("A password is required.");
Errors = _errors;
}
return _errors.Count == 0;
}
private async void AuthenticateUserAsync()
{
Errors.Clear();
IsValid = true;
bool isValid = Validate();
bool isAuthenticated = false;
Token token = null;
if (!isValid)
{
IsValid = false;
return;
}
token = await Services.UserService.IsValidUser(_username, _password);
if (token != null)
isAuthenticated = true;
if (isAuthenticated)
{
//new Messenger().Subscribe<LoginFailedMessage>(this, loginPage.LoginFailed);
//MainPage = new NavigationPage(new mvvmPage());
}
else
{
//Errors.Clear();
//Errors.Add("Invalid credentials.");
}
}
}
}
Converter:
using System;
using System.Collections.Generic;
using System.Globalization;
using Xamarin.Forms;
namespace XFApp.Core.Converters
{
public class ValidationErrorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<string> errors = value as List<string>;
return errors != null && errors.Count > 0 ? string.Join("\r\n", errors.ToArray()) : "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
ViewModelBase:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace XFApp.Core.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}