I haven't written on the blog for a while and I can't say it's due to lack of time, but simply due to lack of its organization.
This article is about mobile programming, using Xamarin with Visual Studio 2019, because it is cross-platform: to a large extent, we write the code once for all mobile operating systems (we will focus on iOS and Android).
I say to a large extent because there are certain parts that need to be written separately for each OS.
We will write the code that allows us to see what we have typed in a password-type textbox.
First, we create a new project -> MyProject. This will ultimately have 3 projects: MyProject, MyProject.Android, and MyProject.iOS.
Then we will create a new folder (Effects) in all three projects in our solution. In each of the three folders, we will create a new class named ShowHidePassEffects.
We start with the class in the MyProject project. It will have the following content:
using Xamarin.Forms;
namespace MyProject.Effects
{
public class ShowHidePassEffect : RoutingEffect
{
public string EntryText { get; set; }
public ShowHidePassEffect() : base("Xamarin.ShowHidePassEffect") { }
}
}
The class in the MyProject.Android project will have more content:
using Android.Text.Method;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(MyProject.Droid.Effects.ShowHidePassEffect), "ShowHidePassEffect")]
namespace MyProject.Droid.Effects
{
public class ShowHidePassEffect : PlatformEffect
{
protected override void OnAttached()
{
ConfigureControl();
}
protected override void OnDetached()
{
}
private void ConfigureControl()
{
EditText editText = ((EditText)Control);
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.HidePass, 0);
editText.SetOnTouchListener(new OnDrawableTouchListener());
}
}
public class OnDrawableTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (v is EditText && e.Action == MotionEventActions.Up)
{
EditText editText = (EditText)v;
if (e.RawX >= (editText.Right - editText.GetCompoundDrawables()[2].Bounds.Width()))
{
if (editText.TransformationMethod == null)
{
editText.TransformationMethod = PasswordTransformationMethod.Instance;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.HidePass, 0);
editText.SetSelection(editText.Length());
}
else
{
editText.TransformationMethod = null;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.ShowPass, 0);
editText.SetSelection(editText.Length());
}
return true;
}
}
return false;
}
}
}
Finally, the class in the MyProject.iOS project:
using System;
using MyProject.iOS.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(ShowHidePassEffect), "ShowHidePassEffect")]
namespace MyProject.iOS.Effects
{
public class ShowHidePassEffect : PlatformEffect
{
protected override void OnAttached()
{
ConfigureControl();
}
protected override void OnDetached()
{
}
private void ConfigureControl()
{
if (Control != null)
{
UITextField vUpdatedEntry = (UITextField)Control;
var buttonRect = UIButton.FromType(UIButtonType.Custom);
buttonRect.SetImage(new UIImage("HidePass"), UIControlState.Normal);
buttonRect.TouchUpInside += (object sender, EventArgs e1) =>
{
if (vUpdatedEntry.SecureTextEntry)
{
vUpdatedEntry.SecureTextEntry = false;
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
}
else
{
vUpdatedEntry.SecureTextEntry = true;
buttonRect.SetImage(new UIImage("HidePass"), UIControlState.Normal);
}
};
vUpdatedEntry.ShouldChangeCharacters += (textField, range, replacementString) =>
{
string text = vUpdatedEntry.Text;
var result = text.Substring(0, (int)range.Location) + replacementString + text.Substring((int)range.Location + (int)range.Length);
vUpdatedEntry.Text = result;
return false;
};
buttonRect.Frame = new CoreGraphics.CGRect(10.0f, 0.0f, 15.0f, 15.0f);
buttonRect.ContentMode = UIViewContentMode.Right;
UIView paddingViewRight = new UIView(new System.Drawing.RectangleF(5.0f, -5.0f, 30.0f, 18.0f));
paddingViewRight.Add(buttonRect);
paddingViewRight.ContentMode = UIViewContentMode.BottomRight;
vUpdatedEntry.LeftView = paddingViewRight;
vUpdatedEntry.LeftViewMode = UITextFieldViewMode.Always;
Control.Layer.CornerRadius = 4;
Control.Layer.BorderColor = new CoreGraphics.CGColor(255, 255, 255);
Control.Layer.MasksToBounds = true;
vUpdatedEntry.TextAlignment = UITextAlignment.Left;
}
}
}
}
We also need to upload the two images we need: HidePass.png and ShowPass.png (for the Android project: Resources/drawable; for iOS: Resources).
Now, having the three classes above and the images uploaded, we return to the MyProject project. All we have left to do is apply the effect directly to the textbox
<?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:ef="clr-namespace:MyProject.Effects"
x:Class="MyProject.LoginPage">
<StackLayout>
<Grid Padding="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackLayout Margin="10,0">
<Label Text="Log Into Your Account"/>
<Entry x:Name="UserName" Placeholder="User Name"/>
<Entry x:Name="Password"
IsPassword="true"
Placeholder="Password">
<Entry.Effects>
<ef:ShowHidePassEffect />
</Entry.Effects>
</Entry>
<Button x:Name="btnLogin"
Text="Login"/>
</StackLayout>
</Grid>
</StackLayout>
</ContentPage>
That's about it. Good luck!