Set binding value as a parameter on a behavior from another control value in Xamarin Forms - xamarin

I am trying to validate a entry value using the entry behaviors. In order to do that I need to pass the value to behavior class from another control in the same xaml.
I have below two controls
<Entry
x:Name="RegisterQty"
Grid.Column="0"
WidthRequest="120"
TextChanged="RegisterQty_TextChanged"
TextColor="Black"
HorizontalOptions="Start"
VerticalOptions="Center"
FontAttributes="Bold"
PlaceholderColor="Black"
Keyboard="Numeric"
FontSize="20">
<Entry.Behaviors>
<local:RegisterQtyBehavior LoadQty = "{Binding BindingContext.qty, Source={x:Reference RegisterQty}} "/>
</Entry.Behaviors>
</Entry>
public class RegisterQtyBehavior : Behavior<Entry>
{
public static readonly BindableProperty LoadQtyProperty = BindableProperty.Create(nameof(LoadQty), typeof(double), typeof(RegisterQtyBehavior), 0);
public double LoadQty
{
get { return (double)GetValue(LoadQtyProperty); }
set { SetValue(LoadQtyProperty, value); }
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result = LoadQty;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Red : Color.Default;
}
}
I want to pass label binding Qty to entry control. How could i achieve this? Adding directly LoadQty = "{Binding Qty}"
does not work.

According to your description, you want to parameter in entry behaviors using binding, am I right? if yes, I do one sample you can take a look:
<Entry x:Name="registerqty">
<Entry.Behaviors>
<local:RegisterQtyBehavior LoadQty="{Binding BindingContext.qty, Source={x:Reference registerqty}}}" />
</Entry.Behaviors>
</Entry>
public partial class Page37 : ContentPage, INotifyPropertyChanged
{
private int _qty;
public int qty
{
get { return _qty; }
set
{
_qty = value;
RaisePropertyChanged("qty");
}
}
public Page37 ()
{
InitializeComponent ();
qty = 100;
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class RegisterQtyBehavior:Behavior<Entry>
{
public static readonly BindableProperty LoadQtyProperty = BindableProperty.Create("LoadQty", typeof(int), typeof(RegisterQtyBehavior), defaultValue: 0);
public int LoadQty
{
get { return (int)GetValue(LoadQtyProperty); }
set
{
SetValue(LoadQtyProperty,value);
}
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
//double result;
//bool isValid = double.TryParse(args.NewTextValue, out result);
//((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
if(!string.IsNullOrEmpty(args.NewTextValue))
{
if (Convert.ToInt32(args.NewTextValue) > LoadQty)
{
((Entry)sender).TextColor = Color.Red;
}
else
{
((Entry)sender).TextColor = Color.Default;
}
}
}
}

Related

the view is invisible when Drop

I Create A Custom View named ScrollView
The ability of this view is just like its name
this view of children include only Label,
And support scroll and click,even drop
In actual use,scrolling and clicking works well,dropping was too
but the Label will invisible when drop
this label still occupy the layout,just invisible
the drop func will still execute when i drop once again in that invisible label
I execute apps on Android
ScrollPicker of XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DebugTest.MyView.ApplyView.ScrollPicker">
<ContentView.Content>
<ScrollView x:Name="ScrollView" x:FieldModifier="Public">
<StackLayout x:Name="MainView" x:FieldModifier="Public">
</StackLayout>
</ScrollView>
</ContentView.Content>
</ContentView>
And C# Code
public partial class ScrollPicker : ContentView
{
public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(StackOrientation), typeof(ScrollPicker), StackOrientation.Vertical, propertyChanged: Orientation_Changed);
public static readonly BindableProperty ItemSourceProperty = BindableProperty.Create("ItemSource", typeof(List<string>), typeof(ScrollPicker), propertyChanged: ItemSource_Changed);
private static void Orientation_Changed(BindableObject bindable, object oldValue, object newValue)
{
if(((StackOrientation)newValue) == StackOrientation.Horizontal)
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Horizontal;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Horizontal;
}
else
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Vertical;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Vertical;
}
}
private static void ItemSource_Changed(BindableObject bindable, object oldValue, object newValue)
{
var view = ((ScrollPicker)bindable);
view.ItemSourceList = (List<string>)newValue;
view.ResetLabelList();
}
public StackOrientation Orientation
{
get { return (StackOrientation)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public List<string> ItemSource
{
get { return (List<string>)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public DropGestureRecognizer DropGR;
public event EventHandler<IndexArgs> ItemClickEvent;
public event EventHandler<IndexArgs> DropEvent;
public List<string> ItemSourceList;
public List<Label> LabelList = new List<Label>();
public ScrollPicker()
{
InitializeComponent();
ItemClickEvent += ScrollPicker_ItemClickEvent;
DropEvent += ScrollPicker_DropEvent;
}
private void ScrollPicker_DropEvent(object sender, EventArgs e)
{
}
private void CE_Click(Element obj)
{
if(LabelList != null && LabelList.Count != 0)
{
if(LabelList.Exists(label => label == obj))
{
int index = LabelList.IndexOf((Label)obj);
ItemClickEvent.Invoke(obj, new IndexArgs(index));
}
}
}
private void ScrollPicker_ItemClickEvent(object sender, EventArgs e)
{
}
private void ResetLabelList()
{
MainView.Children.Clear();
LabelList.Clear();
if (ItemSourceList != null && ItemSourceList.Count != 0)
{
for(int i=0;i< ItemSourceList.Count;i++)
{
//创建Label
Label temp = CreateSubView(ItemSourceList[i]);
//绑定CE
ClickEffect CE = new ClickEffect();
CE.Click += CE_Click;
Global.ClickEffect_BindClickEffect(temp, CE);
//添加Drop
DropGR = new DropGestureRecognizer();
DropGR.DragOver += DropGR_DragOver;
DropGR.DragLeave += DropGR_DragLeave;
DropGR.Drop += DropGR_Drop;
temp.GestureRecognizers.Add(DropGR);
//添加视图
LabelList.Add(temp);
MainView.Children.Add(temp);
if (i == 0) temp.Opacity = 0.1;
}
}
}
private void DropGR_DragOver(object sender, DragEventArgs e)
{
}
private void DropGR_DragLeave(object sender, DragEventArgs e)
{
}
private void DropGR_Drop(object sender, DropEventArgs e)
{
var view = (sender as GestureRecognizer).Parent as View;
int index = MainView.Children.IndexOf(view);
DropEvent.Invoke(view, new IndexArgs(index));
}
private Label CreateSubView(string text)
{
Label label = new Label()
{
Text = text,
FontSize = 20,
WidthRequest = 75,
VerticalTextAlignment = TextAlignment.Start,
HorizontalTextAlignment = TextAlignment.Start,
};
return label;
}
}
public abstract partial class ScrollPickerItemView : ContentView
{
}
public class IndexArgs: EventArgs
{
public int Index;
public IndexArgs(int index =0)
{
Index = index;
}
}
public enum ScrollPickerOrientation
{
Vertical,
Horizontal
}
How Use In Page:
......
<AbsoluteLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,0.1" BackgroundColor="#66CCFF">
<Label AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,0.2,1"
Text="Project:" FontSize="Large" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<applyview:ScrollPicker x:Name="ProjectPicker" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="1,0,0.8,1" Orientation="Horizontal"
ItemClickEvent="ProjectPicker_ItemClickEvent" DropEvent="ProjectPicker_DropEvent"/>
</AbsoluteLayout>
I don't know how to solve this problem. Can someone help me?
I'm afraid I've found the problem
I added a PropertyChanged callback function to each label. I was surprised to find that when I triggered the drop function, the text of the label changed and became Empty
That's why he disappeared!!!
I'm very sorry, but it's a shame
Although the problem has been solved, I still don't know why the text of label becomes null when I trigger the drop function

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.

Create an ICommand Bindable property on Xamarin Forms

I have a custom checkbox control that I created with an ICommand property and the corresponding bindable property (my checkbox is a Xamarin.Forms XAML Page), the code is:
CheckBox.xaml
<Image x:Name="imgCheckBox"
WidthRequest="20"
HeightRequest="20"/>
<Label x:Name="lblCheckBox"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
<TapGestureRecognizer Tapped="OnCheckBoxTapped"/>
CheckBox.xaml.cs
public partial class CheckBox : ContentView
{
private static ImageSource uncheckedImage;
private static ImageSource checkedImage;
public CheckBox()
{
InitializeComponent();
uncheckedImage = ImageSource.FromResource("cbUnchecked.png");
checkedImage = ImageSource.FromResource("cbChecked.png");
imgCheckBox.Source = uncheckedImage;
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool>(
checkbox =>
checkbox.IsChecked,
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, newValue);
}
});
public bool IsChecked
{
set { SetValue(IsCheckedProperty, value); }
get { return (bool)GetValue(IsCheckedProperty); }
}
void OnCheckBoxTapped(object sender, EventArgs args)
{
IsChecked = !IsChecked;
if (IsChecked)
{
imgCheckBox.Source = checkedImage;
}
else
{
imgCheckBox.Source = uncheckedImage;
}
}
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
}
This checkbox implementation is on another Page called TermsAndConditionsPage, that is also a a Xamarin.Forms XAML Page, the code of the implementation is:
<toolkit:CheckBox Text="{Binding txtCheckBox}"
FontSize="Small"
CheckBoxCommand="{Binding OnCheckBoxTapChanged}"
IsChecked="{Binding IsCheckedChanged, Mode=TwoWay}"/>
<Button Text="Next"
Command="{Binding Next_OnClick}"
IsEnabled="{Binding Next_IsEnabled}"
HorizontalOptions="CenterAndExpand"
Clicked="OnNextClicked"/>
The Code Behind of this page is empty (Constructur with InitializeComponent()).
I also have the ViewModel of this page with this code:
TermsAndConditionsViewModel.cs
private string _txtCheckBox;
public string txtCheckBox
{ get { return _txtCheckBox; }
set
{
_txtCheckBox = value;
OnPropertyChanged("txtCheckBox");
}
}
private bool _Next_IsEnabled;
public bool Next_IsEnabled
{
get { return _Next_IsEnabled; }
set
{
_Next_IsEnabled = value;
OnPropertyChanged("Next_IsEnabled");
}
}
private bool _IsCheckedChanged;
public bool IsCheckedChanged
{
get { return _IsCheckedChanged; }
set
{
_IsCheckedChanged = value;
OnPropertyChanged("IsCheckedChanged");
}
}
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
txtCheckBox = "I agree with the terms and conditions";
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{ Next_IsEnabled = true; }
else
{ Next_IsEnabled = false; }
}
private void NextClicked()
{ App.Current.MainPage = new Views.HelloWorld(); }
#region INPC
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Now, the question time: the problem I'm having is the CheckBoxTapped Command is not working, I mean, it doesn't do anything, although the checkbox image changes every time I touch it, it does not change the Next_IsEnabled property of my button. I'd like to know what I am missing here to make this command work properly.
EDIT
What I'm looking for is a Command that behaves similarly to the one that Buttons have.
Thanks all for your time!
Since the original answer is now obsolete, here is the new method:
using System.Windows.Input;
public partial class MyControlExample : ContentView
{
// BindableProperty implementation
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(MyControlExample), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Helper method for invoking commands safely
public static void Execute(ICommand command)
{
if (command == null) return;
if (command.CanExecute(null))
{
command.Execute(null);
}
}
public MyControlExample()
{
InitializeComponent();
}
// this is the command that gets bound by the control in the view
// (ie. a Button, TapRecognizer, or MR.Gestures)
public Command OnTap => new Command(() => Execute(Command));
}
Something like that (pseudocode):
public class YourClassName : View
{
public YourClassName()
{
var gestureRecognizer = new TapGestureRecognizer();
gestureRecognizer.Tapped += (s, e) => {
if (Command != null && Command.CanExecute(null)) {
Command.Execute(null);
}
};
var label = new Label();
label.GestureRecognizers.Add(gestureRecognizer);
}
public static readonly BindableProperty CommandProperty =
BindableProperty.Create<YourClassName, ICommand>(x => x.Command, null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}

Xamarin Forms Password and Confirm Password Validation

I have a Xamarin Forms application in which I have a registration form in which I need to validate Password and Confirm Password fields should be same.
Is there any way to implement this using Xamarin Behaviour?
I have already implemented Xamarin Behaviour for Required Field validation and Email Regex validation like this below-
public class RequiredValidatorBehavior : Behavior<Entry>
{
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid", typeof(bool), typeof(RequiredValidatorBehavior), false);
static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
public bool IsValid
{
get { return (bool)base.GetValue(IsValidProperty); }
private set { base.SetValue(IsValidPropertyKey, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.Unfocused += HandleFocusChanged;
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.Unfocused -= HandleFocusChanged;
base.OnDetachingFrom(bindable);
}
void HandleFocusChanged(object sender, FocusEventArgs e)
{
IsValid = !string.IsNullOrEmpty (((Entry)sender).Text);
}
}
Implementation of Behaviour in XAML Content Page-
<Entry x:Name="password" Placeholder="New Password">
<Entry.Behaviors>
<local:RequiredValidatorBehavior x:Name="passwordValidator"/>
</Entry.Behaviors>
</Entry>
The problem is - I am new in Xamarin development and have no idea how can I get the value of both Password and Confirm Password fields in Behaviour so that I can compare them. I do not wish to compare them on click of button when the form is submitted, fields should be compared while the user is typing in the fields. Any code, help or guidance is appreciable.
https://forums.xamarin.com/discussion/34695/xaml-how-do-you-pass-a-control-view-reference-into-a-behavior
This will help you.
You will use Compare Validator behavior for this as following.
<behaviors:CompareValidator x:Name="ComparePasswordsValidator"
CompareToEntry="{Binding Source={x:Reference PasswordEntry}}" />
Finally The Solution:
XAML-
<Entry x:Name="password" Placeholder="New Password" IsPassword="true">
<Entry.Behaviors>
<local:RequiredValidatorBehavior x:Name="passwordValidator"/>
</Entry.Behaviors>
</Entry>
<Entry x:Name="confirmPassword" Placeholder="Confirm Password" IsPassword="true">
<Entry.Behaviors>
<local:ConfirmPasswordBehavior x:Name="confirmPasswordBehavior" CompareToEntry="{Binding Source={x:Reference password}}" />
</Entry.Behaviors>
</Entry>
BEHAVIOR-
public class ConfirmPasswordBehavior : Behavior<Entry>
{
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid", typeof(bool), typeof(ConfirmPasswordBehavior), false);
public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
public static readonly BindableProperty CompareToEntryProperty = BindableProperty.Create("CompareToEntry", typeof(Entry), typeof(ConfirmPasswordBehavior), null);
public Entry CompareToEntry
{
get { return (Entry)base.GetValue(CompareToEntryProperty); }
set { base.SetValue(CompareToEntryProperty, value); }
}
public bool IsValid
{
get { return (bool)base.GetValue(IsValidProperty); }
private set { base.SetValue(IsValidPropertyKey, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= HandleTextChanged;
base.OnDetachingFrom(bindable);
}
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
var password = CompareToEntry.Text;
var confirmPassword = e.NewTextValue;
IsValid = password.Equals (confirmPassword);
}
}
I've make some changes on Punnet answer:
First, to fix the null exception on HandleTextChanged that was thrown when the comparable string was null
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
string theBase = CompareToEntry.Text;
string confirmation = e.NewTextValue;
// here is the change
IsValid = (bool)theBase?.Equals(confirmation);
((Entry)sender).TextColor = IsValid ? Color.Green : Color.Red;
}
And the second was to check if the comparable string was changed after the validation becomes true.
void baseValue_changed(object sender, TextChangedEventArgs e)
{
IsValid = (bool)((Entry)sender).Text?.Equals(thisEntry.Text);
thisEntry.TextColor = IsValid ? Color.Green : Color.Red;
}
So the new code become
public class ComparisonBehavior : Behavior<Entry>
{
private Entry thisEntry;
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid", typeof(bool), typeof(ComparisonBehavior), false);
public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
public static readonly BindableProperty CompareToEntryProperty = BindableProperty.Create("CompareToEntry", typeof(Entry), typeof(ComparisonBehavior), null);
public Entry CompareToEntry
{
get { return (Entry)base.GetValue(CompareToEntryProperty); }
set
{
base.SetValue(CompareToEntryProperty, value);
if (CompareToEntry != null)
CompareToEntry.TextChanged -= baseValue_changed;
value.TextChanged += baseValue_changed;
}
}
void baseValue_changed(object sender, TextChangedEventArgs e)
{
IsValid = ((Entry)sender).Text.Equals(thisEntry.Text);
thisEntry.TextColor = IsValid ? Color.Green : Color.Red;
}
public bool IsValid
{
get { return (bool)base.GetValue(IsValidProperty); }
private set { base.SetValue(IsValidPropertyKey, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
thisEntry = bindable;
if (CompareToEntry != null)
CompareToEntry.TextChanged += baseValue_changed;
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= HandleTextChanged;
if (CompareToEntry != null)
CompareToEntry.TextChanged -= baseValue_changed;
base.OnDetachingFrom(bindable);
}
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
string theBase = CompareToEntry.Text;
string confirmation = e.NewTextValue;
IsValid = (bool)theBase?.Equals(confirmation);
((Entry)sender).TextColor = IsValid ? Color.Green : Color.Red;
}
}
I have developed custom control to validate our model using data annotation method.
Here is project url.
https://github.com/MaulikParmar/XamarinForms/tree/master/ValidationDemo
It is not working for window 8.1 phone.
Here is youtube url for project description and how to use this control more effectively.
https://www.youtube.com/watch?v=eEi-Oky4U08
If you want to add exception handling because of System.InvalidOperationException error is showing when typing confirm password first before typing a password.
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
try
{
string theBase = CompareToEntry.Text;
string confirmation = e.NewTextValue;
IsValid = (bool)theBase?.Equals(confirmation);
((Entry)sender).TextColor = IsValid ? Color.FromHex("#e6a94d")/*correct*/ : Color.FromHex("#CD5C5C")/*incorrect*/;
}
catch (System.InvalidOperationException)
{
string theBase = CompareToEntry.Text;
string confirmation = e.NewTextValue;
((Entry)sender).TextColor = IsValid ? Color.FromHex("#e6a94d")/*correct*/ : Color.FromHex("#CD5C5C")/*incorrect*/;
}
}

Binding listbox with MVVM Windows Phone

Hi i'm new using MVVM and i'm trying to binding a listbox but it doesn't work. Here's my code
Model
public class Musicmodel : INotifyPropertyChanged
{
//variables privadas
private String _artista;
private Uri _href;
private String _informacion;
private Double _Dvalue;
public String artista
{
get
{
return this._artista;
}
set
{
this._artista= value;
this.RaisePropertyChanged("artista");
}
}
public Uri href {
get {
return this._href;
}
set
{
this._href = value;
this.RaisePropertyChanged("href");
}
}
public String informacion {
get
{
return this._informacion;
}
set
{
this._informacion = value;
this.RaisePropertyChanged("informacion");
}
}
public Double Dvalue
{
get
{
return this._Dvalue;
}
set
{
this._Dvalue = value;
this.RaisePropertyChanged("Dvalue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ViewModel
public class DownloadFileViewModel : INotifyPropertyChanged
{
private WebClient clienteDownload;
private ObservableCollection<Model.Music>_musicSource= new ObservableCollection<Model.Music>();
public ObservableCollection<Model.Music> musicSource
{
get
{
return this._musicSource;
}
set
{
this._musicSource = value;
RaisePropertyChanged("musicSource");
}
}
private int index = 0;
//request para descargar la canción
public void request(Model.Musicmodel item)
{
this.clienteDownload = new WebClient();
this.clienteDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(clienteDownload_DownloadProgressChanged);
//agregamos el item al music
this.musicSource.Add(item);
this.clienteDownload.OpenReadAsync(this.musicSource[index].href);
}
private void clienteDownload_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.musicSource[index].Dvalue=(double)e.ProgressPercentage;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
View
<ListBox x:Name="list" ItemsSource="{Binding musicSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding artista}"/>
<ProgressBar Value="{Binding Dvalue}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code Behind
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DownloadFileViewModel download = new DownloadFileViewModel();
Model.Music newMusic = new Model.Music() { href = new Uri("http://media1.li.ru/b/4/mp3/2/95366/953662_14Friday_Im_In_Love.mp3", UriKind.Absolute), artista = "the cure" };
download.request(newMusic);
this.DataContext = download;
base.OnNavigatedTo(e);
}
I've debuged this and the download works fine and my ObservableCollection fills correctly whithout any problem but when i try to binding my listbox fails.
please what do i'm doing wrong?
thanks
The problem is quite simple. You initialize your musicSource property at the begining in
private ObservableCollection<Model.Music>_musicSource= new ObservableCollection<Model.Music>();
And then just add stuff to it after the request completes. The RaiseProperyChanged("Property") will only fire when you add a new observable collection but not when you add items to it.
Add this line again to the end of the request (when you populate your musicSource)
RaisePropertyChanged("musicSource");
This will trigger another update in the view
EDIT:
Another approach is to have an additional field like
private ObservableCollection<Model.Music>_anotherMusicSource= new ObservableCollection<Model.Music>();
And do everything on it and after that just say:
musicSource = _anotherMusicSource;
This will then trigger the notification and everything should work
You have an underscore in your property name
private ObservableCollection<Model.Musicmodel> musicSource= new ObservableCollection<Model.Musicmodel>();
public ObservableCollection<Model.Musicmodel> _musicSource
{
get
{
return this.musicSource;
}
set
{
this.musicSource = value;
RaisePropertyChanged("musicSource");
}
}
You have this mixed up - the underscore should (traditionally) be on the private member, not the public - your binding is targeting musicSource which is private
The standard convention which is advocated for .NET is:
// Private member variables
private int _someInteger;
// Public ones
public int SomeInteger { get { ... } set { ... } }

Resources