Xamarin button not being enabled - xamarin

A User register page has a Save toolbar button. It is always disabled. The CanExecute method is not being called when a parameter is changed on screen. The Save button should be enabled by the CanExecute method when the user name and password are non blank and the PW and confirmpassword fields match. The CanExecute method is not being called when a change is made to any of the three fields.
Why isn't CanExecute method being called for the registerButton when something is entered into any of the three fields (username, PW, and ConfirmPassword ) The OnPropertyChanged method is being called for any change in these three fields, but apparently the external code is not detecting that a change has been made. Is this some kind of scope issue?
The entire project is available if necessary.
Jim Durbin
Here is the xaml code
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class=*"ButtonTest.RegisterPage"*>
<ContentPage.ToolbarItems>
**<ToolbarItem x:Name="registerButton"
Text="Save"
Command="{Binding RegisterCommand}"
CommandParameter="{Binding Password}"/>**
</ContentPage.ToolbarItems>
<ContentPage.Content>
<ScrollView Orientation="Vertical">
<StackLayout x:Name="containerStackLayout"
VerticalOptions="FillAndExpand"
Margin="20">
<Label Text="Registration"
FontSize="Large"
HorizontalOptions="Center"
FontAttributes="Bold"></Label>
<Entry x:Name="UserName"
Placeholder="User Name"
Text="{Binding UserName, Mode=TwoWay}"
Keyboard="Text"/>
<Entry x:Name="PasswordEntry"
Placeholder="Password"
Text="{Binding PW, Mode=TwoWay}"
IsPassword="True"/>
<Entry x:Name="confirmPasswwordEntry"
Placeholder="Confirm Password"
Text="{Binding ConfirmPassword, Mode=TwoWay}"
IsPassword="True"/>
<Entry x:Name="Email"
Placeholder="Email"
Text="{Binding Email, Mode=TwoWay}"
Keyboard="Email"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
viewModel code
using ButtonTest.Model;
using System.ComponentModel;
using ButtonTest.ViewModel.Commands;
namespace ButtonTest.ViewModel
{
public class RegisterPageVM : INotifyPropertyChanged
{
public RegisterCommand RegisterCommand { get; set; }
private Password user;
public Password User
{
get { return user; }
set
{
user = value;
OnPropertyChanged("User");
}
}
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
User = new Password()
{
UserName = this.UserName,
PW = this.PW,
ConfirmPassword = this.ConfirmPassword
};
OnPropertyChanged("UserName");
}
}
private string pw;
public string PW
{
get { return pw; }
set
{
pw = value;
User = new Password()
{
UserName = this.UserName,
PW = this.PW,
ConfirmPassword = this.ConfirmPassword
};
OnPropertyChanged("PW");
}
}
private string confirmpassword;
public string ConfirmPassword
{
get { return confirmpassword; }
set
{
confirmpassword = value;
User = new Password()
{
UserName = this.UserName,
PW = this.PW,
ConfirmPassword = this.ConfirmPassword
};
OnPropertyChanged("ConfirmPassword");
}
}
public RegisterPageVM()
{
RegisterCommand = new RegisterCommand(this);
//User = new Password();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public void Register(Password user)
{
Password.Register(user);
}
}
}
Here is the command code
using System;
using System.Windows.Input;
using ButtonTest.Model;
namespace ButtonTest.ViewModel.Commands
{
public class RegisterCommand : ICommand
{
private RegisterPageVM viewModel;
public RegisterCommand(RegisterPageVM viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
Password user = (Password)parameter;
if (user != null)
{
if (user.PW == user.ConfirmPassword)
{
if (string.IsNullOrEmpty(user.UserName) || string.IsNullOrEmpty(user.PW))
return false;
return true;
}
return false;
}
return false;
}
public void Execute(object parameter)
{
Password user = (Password)parameter;
viewModel.Register(user);
}
}
}
here is the Password.cs code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Numerics;
using System.Linq;
using System.Threading.Tasks;
namespace ButtonTest.Model
{
public class Password : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string username;
public string UserName
{
get { return username; }
set
{
username = value;
OnPropertyChanged("UserName");
}
}
private string pw;
public string PW
{
get { return pw; }
set
{
pw = value;
OnPropertyChanged("PW");
}
}
private string confirmPassword;
public string ConfirmPassword
{
get { return confirmPassword; }
set
{
confirmPassword = value;
OnPropertyChanged("ConfirmPassword");
}
}
private string email;
public string Email
{
get { return email; }
set { email = value; }
}
public static async void Insert(Password password)
{
await App.client.GetTable<Password>().InsertAsync(password);
//await App.client.SyncContext.PushAsync();
}
public static async Task<bool> Delete(Password password)
{
try
{
await App.passwordsTable.DeleteAsync(password);
//await App.client.SyncContext.PushAsync();
return true;
}
catch (Exception)
{
return false;
}
}
public static async void Register(Password user)
{
await App.client.GetTable<Password>().InsertAsync(user);
}
public static async Task<bool> Login(string username, string password)
{
bool isEmailEmpty = string.IsNullOrEmpty(username);
bool isPasswordEmpty = string.IsNullOrEmpty(password);
if (isEmailEmpty || isPasswordEmpty)
{
return false;
}
else
{
var user = (await App.client.GetTable<Password>().Where(u => u.UserName == username).ToListAsync()).FirstOrDefault();
if (user != null)
{
App.user = user;
if (user.pw == password)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
}
}

This problem is solved. The CommandParameter binding for the RegisterCommand needed to be set to the User property in the VM. The CanExecute method is now being called for each change in the UserName, PW, and ConfirmPassword fields.
It was solved by E.Z. Hart from the github support group. Many thanks for the solution.

Related

How to get 2 specific data from firebase using Xamarin

I've been trying to work on this code example I found online. I want to display the name and email welcome page after I log in but it doesn't show the name only the E-mail
This is the firebasehelper
public static async Task<List<Citizen>> GetAllCitizen()
{
try
{
var citizenlist = (await firebase
.Child("Citizen").OnceAsync<Citizen>()).Select(item =>
new Citizen
{
Email = item.Object.Email,
Password = item.Object.Password,
FirstName= item.Object.FirstName
}).ToList();
return citizenlist;
}
catch (Exception e)
{
Debug.WriteLine($"Error:{e}");
return null;
}
}
//Read
public static async Task<Citizen> GetCitizen(string email, string firstName)
{
try
{
var allCitizen = await GetAllCitizen();
await firebase.Child("Citizen")
.OnceAsync<Citizen>();
return allCitizen.Where(a => a.Email == email).FirstOrDefault();
}
catch (Exception e)
{
Debug.WriteLine($"Error:{e}");
return null;
}
}
This is the WelcomeVM
public class WelcomeVM : INotifyPropertyChanged
{
public WelcomeVM(string email2, string firstName2)
{
FirstName = firstName2;
Email = email2;
}
private string firstName;
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
private string email;
public string Email
{
get { return email; }
set { email = value; }
}
private string password;
public event PropertyChangedEventHandler PropertyChanged;
The welcome xaml
<ContentPage.Content>
<StackLayout>
<Label Text="{Binding FirstName, StringFormat='Welcome {0}'}" Margin="10" FontSize="25"/>
<Label Text="{Binding Email, StringFormat='Welcome {0}'}" Margin="10" FontSize="20"/>
<Label Text="{Binding FirstName, StringFormat='Welcome {0}'}" Margin="10" FontSize="20"/>
I copied the code that displays the E-mail but it doesn't show the name only the E-mail

How to Binding Result from ViewModel in Xamarin

I have 1 question here: How to get data from ViewModel in Xamarin, however I still haven't solved the problem. I created a new post with some changes.
I have:
PageOne.xaml
<StackLayout>
<RefreshView x:DataType="locals:ViewCustomerViewModel" Command="{Binding LoadUserinfoCommand}" IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<Label Text="{Binding Customer.Address}" />
</RefreshView>
</StackLayout>
PageOne.xaml.cs
ViewCustomerViewModel viewCustomerViewModel;
public Customer CustomerGet { get; set; }
public PageOne()
{
InitializeComponent();
BindingContext = viewCustomerViewModel = new ViewCustomerViewModel();
viewCustomerViewModel.OnAppearing();
}
Class Customer
public class Customer
{
public string Address{ get; set; }
........
}
ViewCustomerViewModel
public class ViewCustomerViewModel:BaseCustomerViewModel
{
ApiServiceUserinfo apiServiceUserinfo = new ApiServiceUserinfo();
public Command LoadUserinfoCommand { get; }
public ObservableCollection<Customer> CustomerInfos { get; set; }
public ViewCustomerViewModel()
{
LoadUserinfoCommand = new Command(async () => await ExecuteLoadUserinfoCommand());
CustomerInfos = new ObservableCollection<Customer>();
}
public void OnAppearing()
{
IsBusy = true;
}
async Task ExecuteLoadUserinfoCommand()
{
string userget = "1";
IsBusy = true;
try
{
CustomerInfos.Clear();
var customerList = await apiServiceUserinfo.GetCustomersInfo(userget);
CustomerInfos.Add(customerList);
}
catch (Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
}
And I got the result CustomerInfos.Add(customerList);
However <Label Text="{Binding Customer.Address}" /> does not get results
Please help me again clearly in the answer. Thank you.
Update
ViewCustomerViewModel
public class ViewCustomerViewModel:BaseCustomerViewModel
{
ApiServiceUserinfo apiServiceUserinfo = new ApiServiceUserinfo();
public Command LoadUserinfoCommand { get; set;}
public Customer CustomerGets { get; set;}--> update
public ViewCustomerViewModel()
{
LoadUserinfoCommand = new Command(async () => await ExecuteLoadUserinfoCommand());
//CustomerGets = new Customer();
}
public void OnAppearing()
{
IsBusy = true;
}
async Task ExecuteLoadUserinfoCommand()
{
string userget = "1";
IsBusy = true;
try
{
var customerList = await apiServiceUserinfo.GetCustomersInfo(userget);
CustomerGets = customerList;
}
catch (Exception)
{
throw;
}
finally
{
IsBusy = false;
}
}
}
PageOne.xaml
<StackLayout>
<RefreshView x:DataType="locals:ViewCustomerViewModel" Command="{Binding LoadUserinfoCommand}" IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<Label Text="{Binding CustomerGets.Address}" />
</RefreshView>
</StackLayout>
We need to call the OnPropertyChanged method to notify the change in setter method of the property .
private Customer customerGets;
public Customer CustomerGets {
get { return customerGets; }
set {
customerGets = value;
NotifyPropertyChanged(); //the method is declared in BaseCustomerViewModel
}
}
Ensure that BaseCustomerViewModel has implemented INotifyPropertyChanged , something like that
public class BaseCustomerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

How can I ensure my ListView is refreshed in Xamarin?

I received some help yesterday. I am still new to c# and using Xamarin Forms with Prism as the MvvM Framework.
I pull data from an API and want to display it in a ListView, the api works and returns data correctly. If I return the data as a List and bind the list, I have to manually update the binding in the XAML (while running the application) in order to view the data in the rows.
I thought I may need to change to an ObservableCollection, but I didn't have much luck with it.
How can I correct this?
I will try to updated the OP with a gif showing my problem.
Here is my model:
public class OpenWorkOrder
{
public int ID { get; set; }
public string WOID { get; set; }
public string AssetNumber { get; set; }
public string WorkRequested { get; set; }
public Nullable<bool> Active { get; set; }
}
Here is the ViewModel:
private List<OpenWorkOrder> _openWO;
public List<OpenWorkOrder> OpenOrders
{
get { return _openWO; }
set { SetProperty(ref _openWO, value); }
}
private string selectedWO;
public string SelectedWO { get => selectedWO; set => SetProperty(ref selectedWO, value); }
private string wOID;
public string WOID { get => wOID; set => SetProperty(ref wOID, value); }
private string iD;
public string ID { get => iD; set => SetProperty(ref iD, value); }
private string workRequested;
public string WorkRequested { get => workRequested; set => SetProperty(ref workRequested, value); }
public async Task<List<OpenWorkOrder>> OpenWO()
{
var client = new RestClient("apiurl/");
var request = new RestRequest("Maint/GetOpenWorkOrders", Method.GET);
IRestResponse<List<OpenWorkOrder>> response = await client.ExecuteAsync<List<OpenWorkOrder>>(request);
if (response.StatusCode.ToString() == "OK")
{
return response.Data;
}
else
{
return null;
}
public async void OnNavigatedTo(INavigationParameters parameters)
{
try { OpenOrders = await OpenWO();
Console.WriteLine("Await Called!");
}
catch (Exception ex)
{
await _dialogService.DisplayAlertAsync("Exception Handled", ex.ToString(), "OK");
}
}
private List<OpenWorkOrder> _openWO;
public List<OpenWorkOrder> OpenOrders
{
get { return _openWO; }
set { SetProperty(ref _openWO, value); }
}
public async Task<List<OpenWorkOrder>> OpenWO()
{
var client = new RestClient("apiurl/");
var request = new RestRequest("Maint/GetOpenWorkOrders", Method.GET);
IRestResponse<List<OpenWorkOrder>> response = await client.ExecuteAsync<List<OpenWorkOrder>>(request);
if (response.StatusCode.ToString() == "OK")
{
return response.Data;
}
else
{
return null;
}
public async void OnNavigatedTo(INavigationParameters parameters)
{
try { OpenOrders = await OpenWO();
Console.WriteLine("Await Called!");
}
catch (Exception ex)
{
await _dialogService.DisplayAlertAsync("Exception Handled", ex.ToString(), "OK");
}
}
Here is the XAML:
<StackLayout Orientation="Horizontal">
<ListView x:Name="Data" ItemsSource="{Binding OpenOrders}"
GroupDisplayBinding="{Binding WOID}"
SelectionMode="Single">
<ListView.Behaviors>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding WorkRequested}" HorizontalOptions="Center"/>
<Label Text="{Binding WOID}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
EDIT: Attached gif here

Login MVVM / Labels

i have a question regarding to Binding. I have a Login that I now decided to transfer to MVVM until now i had labels with content "wrong password" and such in code behind and i was setting their visibility. Btw I do have RESX file and I am binding my labels, just not sure how to approach this.
bool isPreparedToBeSubmitted = true;
if (string.IsNullOrEmpty(UserName))
{
isPreparedToBeSubmitted = false;
emailValLabel.IsVisible = true;
}
if (string.IsNullOrEmpty(Password))
{
isPreparedToBeSubmitted = false;
passwordEntryValLabel.IsVisible = true;
}
But I think this can be done much nicer, but I am not sure how. Do i get the visibility bool in Model and then set binding to the label in View?
Since you want to use MVVM. It would be better to bind the text of entry to ViewModel . Don't forget to set the binding mode as OneWayToSource .
You could check the following demo . The page contains two entries . The submit button will appear when user both input the user name and password .
in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<Entry Placeholder="input username" Text="{Binding UserName,Mode=OneWayToSource}" HeightRequest="40" WidthRequest="200" />
<Entry Placeholder="input password" Text="{Binding Password,Mode=OneWayToSource}" HeightRequest="40" WidthRequest="200" />
<Button Text="Submit" IsVisible="{Binding IsVisible}" Command="{Binding SubmitCommand}" />
</StackLayout>
in ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand SubmitCommand { get; set; }
//IsVisible
bool isVisible;
public bool IsVisible
{
get
{
return isVisible;
}
set
{
if (isVisible != value)
{
isVisible = value;
NotifyPropertyChanged("IsVisible");
}
}
}
string userName;
public string UserName
{
get
{
return userName;
}
set
{
if (userName != value)
{
userName = value;
IsSubmitVisible();
NotifyPropertyChanged("UserName");
}
}
}
string password;
public string Password
{
get
{
return password;
}
set
{
if (password != value)
{
password = value;
IsSubmitVisible();
NotifyPropertyChanged("Password");
}
}
}
void IsSubmitVisible()
{
if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
{
IsVisible = true;
}
else
{
IsVisible = false;
}
}
public MyViewModel()
{
SubmitCommand = new Command(()=> {
//...
});
}
}

Binding in Xamarin.Forms does not work after web API request

I am trying to make simple app which will provide features to read/write data to database trough an Web API.
I have view model which is bind to view, but it is not working properly after web api get request, even that call was successfully done. I've tried to check value with display alert, value is correct, but it is not presented in view part, exactly in one label. Here is my code:
<?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="SOSEUApp.Pages.NotePage"
Title="Dnevnik">
<ContentPage.ToolbarItems>
<ToolbarItem Text="GET" Clicked="ToolbarItem_Clicked"></ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Date,StringFormat='Date: {0}'}"></Label>
</StackLayout>
<StackLayout>
</StackLayout>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NotePage : ContentPage
{
NoteViewModel nvm = new NoteViewModel();
public NotePage()
{
InitializeComponent();
BindingContext = nvm;
}
private async void ToolbarItem_Clicked(object sender, EventArgs e)
{
nvm.IsBusy = true;
nvm.Notes = await App.NotesWebApiService.GetAll(nvm.CurrentActiveNote.Route);
nvm.GetLastNote();
nvm.IsBusy = false;
await DisplayAlert("Info", nvm.Date.ToString(), "Close");
}
}
public class NoteViewModel : BaseViewModel
{
IList<Note> notes = new List<Note>();
public IList<Note> Notes
{
get { return notes; }
set { SetProperty(ref notes, value); }
}
private Note currentActiveNote = new Note();
public Note CurrentActiveNote { get { return currentActiveNote; } }
public string Date { get { return currentActiveNote.Date.ToString("dd.MM.yyyy"); } }
public string OrderedNumber
{
get { return currentActiveNote.OrderNumber.ToString(); }
set
{
string v = currentActiveNote.OrderNumber.ToString();
SetProperty(ref v, value);
currentActiveNote.OrderNumber = Convert.ToInt16(v);
}
}
public string Description
{
get { return currentActiveNote.Description; }
set
{
string v = currentActiveNote.Description;
SetProperty(ref v, value);
currentActiveNote.Description = v;
}
}
public void GetLastNote()
{
notes.OrderBy(a => a.Date);
currentActiveNote = notes.Last();
}
}
public class BaseViewModel : DataModel, INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Thank you in advance!
just called SetProperty method and passed desired values there. It works
Said as Jason, you need to fire a PropertyChanged event when Date changes.Here is official document for reference.
Generally, usually writed in Set methods.As follow:
private string propertyname;
public string PropertyName
{
set { SetProperty(ref propertyname, value); }
get { return propertyname; }
}
Or write as follow:
public string PropertyName
{
set
{
if (propertyname!= value)
{
propertyname= value;
OnPropertyChanged("PropertyName");
}
}
get
{
return propertyname;
}
}
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));
}
When data of model change , this will be invoked automatically.

Resources