Binding a decimal to a UITextField does not let you put a "." in, as you type "1.", the push to source strips it out. I get why it happens, MakeSafeValue converts it to a decimal, which gets read back out without the ".", overwriting the Text entered. This seems like a keyup vs onblur issue, which a lot of binding frameworks let you override.
I know I could bind a string on the view model instead, but handling the EditingDidEnd instead of EditingChanged seems better. That way I could intercept ShouldEndEditing to check validity.
Where would I register my binding to override the MvvmCross implementation? I tried adding in my Setup.FillTargetFactories, but it is called before MVXTouchBindingBuilder.FillTargetFactories where the MvvmCross one is.
I tried a different property name, in FillTargetFactories I have:
registry.RegisterPropertyInfoBindingFactory(typeof (UITextFieldTextDidEndTargetBinding), typeof (UITextField), "DidEndText");
but it does not work when I bind.
set.Bind(textField).For("DidEndText").To(vm => vm.GMoney);
looks like MvxPropertyInfoTargetBindingFactory.CreateBinding does not work if the target does not have that property = makes sense. Does this leave me with starting at MvxTargetBinding and creating a custom binding? I hate to recreate most of the code in MvxPropertyInfoTargetBinding.
So, I guess the question(s) are...
- what is the syntax/location to override the default UITextField binding with mine?
- how can I specify a different binding for Text with the Bind Syntax?
Thanks in advance.
what is the syntax/location to override the default UITextField binding with mine?
If you want to simply replace the existing binding, then you can do this via the RegisterPropertyInfoBindingFactory
extension method.
MvvmCross operates a simple 'last registered wins' system.
The easiest place your registration in order that happens last is to place it at the end of InitializeLastChance
in your Setup
:
protected override void InitializeLastChance()
{
base.InitializeLastChance();
var factory = base.Resolve<IMvxTargetBindingFactoryRegistry>();
factory.RegisterPropertyInfoBindingFactory(
typeof(MyBindingType),
typeof(UITextField),
"Text");
}
There's some more info on order of initialization/setup in https://github.com/slodge/MvvmCross/wiki/Customising-using-App-and-Setup (work in progress).
how can I specify a different binding for Text with the Bind Syntax?
The line:
registry.RegisterPropertyInfoBindingFactory(
typeof (UITextFieldTextDidEndTargetBinding),
typeof (UITextField),
"DidEndText");
doesn't work because it tries to use a property called DidEndText
- which doesn't exist.
Two possible ways around this are:
1 Subclass UITextField
to provide a real property
public class MyTextField : UITextField
{
// ctors
public string MySpecialText
{
get { return base.Text; }
set { base.Text = value; }
}
}
An example of UITextField
subclassing is shown in the N=33 video
2 Or use a non-propertyInfo-based binding
For known properties and event pairs this is pretty simple to do:
public class MySpecialBinding : MvxTargetBinding
{
public MySpecialBinding (UIEditField edit)
: base(edit)
{
edit.SpecialEvent += Handler;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var text = (UITextField)Target;
if (text != null)
{
text.SpecialEvent -= Handler;
}
}
base.Dispose(isDisposing);
}
protected override void Handler(object s, EventArgs e)
{
var text = (UITextField)target;
if (text == null)
return;
FireValueChanged(text.Text);
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
public override Type TargetType
{
get { return typeof(string); }
}
public override void SetValue(object value)
{
var text = (UITextField)target;
if (text == null)
return;
text.Text = value;
}
}
This special binding can be registered using RegisterCustomBindingFactory
registry.RegisterCustomBindingFactory<UITextField>(
"FooFlipper",
textField => new MySpecialBinding(textField));
After which bindings like:
set.Bind(textField).For("FooFlipper").To(vm => vm.GMoney);
will work.
As well as your 'use a string in the ViewModel' suggestion, another possible workaround for this double-text conversion, might be to use a two-way value converter. If your UI is handling money, this could insist on displaying decimal places for all values.