I am trying to bind the property PlainText
(which is a TwoWay Bindable property which I created in my CustomEditor) of CustomEditor
to the variable KeyString
when on iOS
, and bind the property Text
to KeyString
when on Android
.
I know that the binding of PlainText
to KeyString
when on iOS
works properly (I have tested to ensure this), however the binding of Text
to KeyString
for Android
fails with System.ArgumentNullException
Value cannot be null. Parameter name: binding
Also IntelliSense underlines the parts of my code that use BindingBase
in x:TypeAgruments
. For the first part Intellisense says: PlainText does not support values of type OnPlatform(BindingBase)
, but the code still works when I run it on my iOS
emulator. It gives me the error Text does not support values of type OnPlatform(BindingBase)
for the Android
binding part of the code and that is the part of my XAML which it fails to run.
Below is my XAML
code, any idea what I might be doing wrong?
<Frame Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BorderColor="Black" Margin="0" Padding="0">
<controls:CustomEditor HeightRequest="80" IsPassword="True">
<controls:CustomEditor.PlainText>
<OnPlatform x:TypeArguments="BindingBase">
<On Platform="iOS" Value="{Binding KeyString}"/>
</OnPlatform>
</controls:CustomEditor.PlainText>
<controls:CustomEditor.Text>
<OnPlatform x:TypeArguments="BindingBase">
<On Platform="Android" Value="{Binding KeyString}"/>
</OnPlatform>
</controls:CustomEditor.Text>
<controls:CustomEditor.Effects>
<controls:PasswordEffect>
</controls:PasswordEffect>
</controls:CustomEditor.Effects>
</controls:CustomEditor>
</Frame>
In my custom Editor class I have this code:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace MyApp.CustomControls
{
public class CustomEditor : Editor
{
public static readonly BindableProperty IsPasswordProperty =
BindableProperty.Create(nameof(IsPassword), typeof(bool), typeof(CustomEditor), false);
public static readonly BindableProperty PlainTextProperty =
BindableProperty.Create(nameof(PlainText),
typeof(string),
typeof(CustomEditor),
String.Empty,
defaultBindingMode:BindingMode.TwoWay,
propertyChanged:OnPlainTextChanged);
public bool IsPassword
{
get { return (bool)GetValue(IsPasswordProperty); }
set { SetValue(IsPasswordProperty, value); }
}
public string PlainText {
get { return (string)GetValue(PlainTextProperty); }
set { SetValue(PlainTextProperty, value); }
}
private static void OnPlainTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (CustomEditor)bindable;
if (newValue != null)
{
control.PlainText = newValue.ToString();
}
}
}
}
As I don't like working with OnPlatform
, I'd like to propose a workaround that I've mentioned in the comment.
In you CustomEditor
add the following code:
public static BindableProperty CustomTitleProperty = BindableProperty.Create(
propertyName: nameof(CustomTitle),
returnType: typeof(string),
declaringType: typeof(CustomEditor),
defaultValue: null);
public string CustomTitle
{
get { return (string)GetValue(CustomTitleProperty); }
set { SetValue(CustomTitleProperty, value); }
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if(propertyName==CustomTitleProperty.PropertyName)
{
SetCustomTitle();
}
}
private void SetCustomTitle()
{
switch(Device.RuntimePlatform)
{
case Device.iOS:
{
PlainText = CustomTitle;
return;
}
case Device.Android:
{
Text = CustomTitle;
return;
}
default:
{
throw new NotSupportedException($"{Device.RuntimePlatform} not supported in {nameof(SetCustomTitle)}");
}
}
}
What I did there is I simply moved OnPlatform code to your control, so you can keep your xaml code cleaner.
With this approach you should be able to use it like
<controls:CustomEditor HeightRequest="80" IsPassword="True" CustomTitle="{Binding KeyString}">
<controls:CustomEditor.Effects>
<controls:PasswordEffect/>
</controls:CustomEditor.Effects>
</controls:CustomEditor>