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(()=> {
//...
});
}
}
Related
I'm creating a form where a product status needs to be selected with a dropdown menu.
I've created a picker for this. The data is loaded from a list in my ViewModel, but it doesn't get sent back.
I've tried the same using entry fields and that works fine. I just have no idea how to link the picker with the view model.
Here's my code.
Xaml
</Grid>
<Label Text="Current status" Style="{StaticResource MainLabel}"/>
<Label Style="{StaticResource MainLabel}" Text="{Binding ProductionStatus, Mode=TwoWay}"/>
<!-- gets send when saved-->
<Entry Style="{StaticResource MainEntry}" Text="{Binding ProductionStatus, Mode=TwoWay}" Keyboard="Text" />
<Label Text="Remark" Style="{StaticResource MainLabel} "/>
<!-- gets send when saved-->
<Entry Text="{Binding Remark}" Keyboard="Text" Style="{StaticResource MainEntry}"/>
<!-- Does not bind with anything.-->
<Picker x:Name="statusPicker" ItemsSource="{Binding ProductionOrderStatusName}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding ProductionStatusName}"/>
<Button Style="{StaticResource PrimaryButton}" Text="Save" Command="{Binding SaveCommand}"></Button>
Code-behind ViewModel
public ICommand SaveCommand
{
get
{
return new Command(ExecuteSaveCommand);
}
}
private async void ExecuteSaveCommand()
{
int statusId = FindProductionOrderStatusId(ProductionStatus); //the production status should be the result of the selected value in the picker
Guid id = _CurrentProductionOrder.Id;
string remark = Remark; // value from the remark entery in the xaml
await __productionOrderService.UpdateAsync(id, remark,statusId);
}
Properties
public ObservableCollection<ProductionOrderStatus> productionOrderStatusName;
public ObservableCollection<ProductionOrderStatus> ProductionOrderStatusName
{
get { return productionOrderStatusName; }
set
{
productionOrderStatusName = value;
}
}
public int amount;
public int Amount
{
get { return amount; }
set
{
amount = value;
}
}
public DateTime finishDate;
public DateTime FinishDate
{
get { return finishDate; }
set
{
finishDate = value;
}
}
public int ordernumber;
public int OrderNumber
{
get { return ordernumber; }
set
{
ordernumber = value;
}
}
public string remark;
public string Remark
{
get { return remark; }
set
{
remark = value;
}
}
public string productionStatus;
public string ProductionStatus
{
get
{
return productionStatus;
}
set
{
productionStatus = value;
}
}
private string materialNumber;
public string MaterialNumber
{
get { return materialNumber; }
set
{
materialNumber = value;
}
}
private string materialDescription;
public string MaterialDescription
{
get { return materialDescription; }
set
{
materialDescription = value;
}
}
Code behind loading my data
public OrderViewModel()
{
_productionOrderStepService = new MockingProductionOrderStepService();
_materialService = new MockingMaterialService();
__productionOrderService = new MockingProductionOrderService();
__productionOrderStatusService = new MockingProductionOrderStatusService();
_seederService = new Seeder(__productionOrderService, _productionOrderStepService, __productionOrderStatusService, _materialService);
_seederService.EnsureSeeded();
}
public override void Init(object initData)
{
_CurrentProductionOrder = initData as ProductionOrder;
LoadItemState();
base.Init(initData);
}
private void LoadItemState()
{
ObservableCollection<ProductionOrderStatus> ProductionStatusName = new ObservableCollection<ProductionOrderStatus>(__productionOrderStatusService.GetListAllAsync().Result);
this.ProductionOrderStatusName = ProductionStatusName;
this.materialDescription = FindMaterialDescription(_CurrentProductionOrder.MaterialId);
this.materialNumber = FindMaterialNumber(_CurrentProductionOrder.MaterialId);
this.productionStatus = FindProductionOrderStatus(_CurrentProductionOrder.StatusId);
this.remark = _CurrentProductionOrder.Remark;
this.amount=_CurrentProductionOrder.Amount;
this.finishDate = _CurrentProductionOrder.FinishDate;
this.ordernumber = _CurrentProductionOrder.OrderNumber;
}
Thx for the help!
you are making this more complicated than it needs to be
<Picker x:Name="statusPicker"
// this is the List of items X to display
ItemsSource="{Binding ProductionOrderStatusName}"
// this tells the picker which property of X to display to the user
ItemDisplayBinding="{Binding Name}"
// this is the specific X the user has selected
SelectedItem="{Binding SelectedStatus}" />
then in your VM
public ObservableCollection<ProductionOrderStatus> ProductionOrderStatusName { get; set; }
public ProductionOrderStatus SelectedStatus { get; set; }
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.
I am newbie to xamarin forms. I have a Listview containing checkboxes. I bind the checkbox "isChecked" to one of the listview's itemsource bool property. the problem is, everytime i change the bool value where the checkbox is bind, checkbox appearance has'nt change. How can i achieve that kind of approach?enter image description here
[1]: https://i.stack.imgur.com/4KcT2.png
Hi #Weggie Villarante. Please try this.It's work for me
<ViewCell>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Orientation="Horizontal">
<Label Text="{Binding Title}" HorizontalOptions="StartAndExpand"></Label>
<CheckBox IsChecked="{Binding IsCheck}" HorizontalOptions="End" HeightRequest="50"></CheckBox>
</StackLayout>
</ViewCell>
NotificationModel.cs
public class NotificationModel : INotifyPropertyChanged
{
public string Message { get; set; }
public string Title { get; set; }
public bool _IsCheck = false;
public bool IsCheck
{
get
{
return _IsCheck;
}
set
{
_IsCheck = value;
this.OnPropertyChanged("IsCheck");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
NotificationViewModel.cs
class NotificationViewModel : INotifyPropertyChanged
{
ObservableCollection<NotificationModel> _Items;
public ObservableCollection<NotificationModel> Items
{
get
{
return _Items;
}
set
{
_Items = value;
OnPropertyChanged();
}
}
public NotificationViewModel()
{
Items = new ObservableCollection<NotificationModel>();
AddItems();
}
void AddItems()
{
_Items.Add(new NotificationModel { Title = "Info", Message = "This is only information message please ignor this one." ,IsCheck = false});
_Items.Add(new NotificationModel { Title = "Alert", Message = "This is only Alert message please ignor this one." , IsCheck = false });
_Items.Add(new NotificationModel { Title = "Suggesstion", Message = "This is only Suggesstion message please ignor this one." , IsCheck = false});
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
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.
I am trying to figure out how to remove the white space you see in the image below (surrounded by a red rectangle). Notice I have a ListView embedded in a parent ListView.
XAML
<ListView x:Name="___listview" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Button Image="{Binding ImageName}" Command="{Binding ShowDetailsCommand}" />
<ListView ItemsSource="{Binding Notes}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Note}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This probably isn't needed, but here is the model...
MODEL
namespace ViewCellClick
{
public class ModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Model : ModelBase
{
public Model()
{
_imageName = "ellipses_vertical.png";
_showDetails = true;
ShowDetailsCommand = new Command(() =>
{
ShowDetails = !_showDetails;
ImageName = (_imageName == "ellipses_vertical.png")
? "ellipses_horizontal.png"
: "ellipses_vertical.png";
});
}
bool _showDetails;
public bool ShowDetails
{
get { return _showDetails; }
set { if (_showDetails != value) { _showDetails = value; OnPropertyChanged("ShowDetails"); } }
}
string _imageName;
public string ImageName
{
get { return _imageName; }
set { if (_imageName != value) { _imageName = value; OnPropertyChanged("ImageName"); } }
}
public ICommand ShowDetailsCommand { get; set; }
List<ChildModel> _notes;
public List<ChildModel> Notes { get { return _notes; } set { _notes = value; } }
}
public class ChildModel : ModelBase
{
public ChildModel(string note) { _note = note; }
string _note;
public string Note
{
get { return _note; }
set { if (_note != value) { _note = value; OnPropertyChanged("Note"); } }
}
}
}
You can't do this with Xamarin.Forms.ListView and nesting them is not supported. Really on iOS this would be very difficult and I'm not sure you could get it working without some weird gesture behavior.