I am trying to achieve perfect MVVM in Xamarin.Forms with a record.
My model contains properties whose type includes a string, int ?, decimal ?, bool ?, etc. Whenever I bind to a string type, two-way binding works because the text property has a string type (they match). But as soon as you try to bind yourself to the model, and the property is int or int ?, it does not update the value of the model property.
During my research and using Xamarin support, this was a very useful question on how to handle NULL types:
Nullable type in x: TypeArguments
XAML Code:
<controls:NullableIntEntry Grid.Column="1" Grid.Row="14" NumericText="{Binding BusinessOwnership, Mode=TwoWay}" x:Name="lblBusinessOwnership"></controls:NullableIntEntry>
Run codeHide resultBindableEntry (extension code):
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using Xamarin.Forms;
namespace CreditBuilderApp.Controls
{
public class BindableEntry<T> : Entry
{
static bool firstLoad;
public static readonly BindableProperty NumericTextProperty =
BindableProperty.Create("NumericText", typeof(T), typeof(BindableEntry<T>),
null, BindingMode.TwoWay, propertyChanged: OnNumericTextChanged);
static void OnNumericTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var boundEntry = (BindableEntry<T>)bindable;
if (firstLoad && newValue != null)
{
firstLoad = false;
boundEntry.Text = newValue.ToString();
}
}
public T NumericText
{
get { return (T)GetValue(NumericTextProperty); }
set { SetValue(NumericTextProperty, value); }
}
public BindableEntry()
{
firstLoad = true;
this.TextChanged += BindableEntry_TextChanged;
}
private void BindableEntry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!String.IsNullOrEmpty(e.NewTextValue))
{
this.NumericText = (T)Convert.ChangeType(e.NewTextValue, typeof(T));
}
else
{
this.NumericText = default(T);
}
}
}
}
Run codeHide resultNullableIntEntry and NullableDecimalEntry (Bindable Entry extension indicating the type):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CreditBuilderApp.Controls
{
public class NullableIntEntry : BindableEntry<Int32?>
{
}
public class NullableDecimalEntry : BindableEntry<Decimal?>
{
}
}
Run codeHide resultModel:
private int? _businessOwnership { get; set; }
public int? BusinessOwnership
{
get { return _businessOwnership; }
set
{
if (_businessOwnership != value)
{
_businessOwnership = value;
RaisePropertyChanged();
}
}
}
Run codeHide result, , , , , . , BindableEntry , . ( T int?, T .. ALONG, , e.NewTextValue.
: .
this.NumericText = (T)Convert.ChangeType(e.NewTextValue, typeof(T));
Hide result(), this.NumericText T .
, , , T int? AS WELL AS :
, , Convert.ChangeType T, . , , .
ConvertType
, - . !