I'm new to MAUI with basic knowledge on Xamarin.Forms
I would like to add a bottom border (and border tickness) to an Entry control in MAUI.
On Xamarin.Forms, we had to create a Custom Control and then a Renderer for each platforms.
I did research on the internet before posting this message. It concerns the Handlers and I have the impression that it only allows basic modifications (changing the color of the background, etc...).
I'm a bit confused with all this information, if anyone could enlighten me I would appreciate it.
Customize specific control instances shows a trivial example of a custom Entry, that only customizes some properties per platform.
I've started to create a .Net Maui advanced custom Entry example. See that repo for the implementation so far.
Status:
Limitations (current):
Entry
properties
need to be mapped to the contained TextBox.Entry
events
, to see if they need any special handling.If you wish to extend this farther, google xamarin forms customize entry renderer
for examples of platform-specific code. Hopefully I've shown enough to give a sense of how/where to add such code.
At this time, doing an advanced example seemed to be MORE work than the corresponding Xamarin Forms "custom renderers".
REASONS:
IEntry
and IEntryHandler
. HOW OVERRIDE type of PlatformView
??TBD: Perhaps there is a way to avoid the complications I encountered.
ALSO there may be code I copied, that could be omitted.
These are the steps that need to be done:
MyEntry : Entry
with desired additional properties.MyEntryHandler
to render to native UI object(s).1. Define class MyEntry : Entry
with desired additional properties.
Here, we add UnderlineColor
and UnderlineThickness
.
public class MyEntry : Entry
{
/// <summary>
/// Color and Thickness of bottom border.
/// </summary>
public static BindableProperty UnderlineColorProperty = BindableProperty.Create(
nameof(UnderlineColor), typeof(Color), typeof(MyEntry), Colors.Black);
public Color UnderlineColor
{
get => (Color)GetValue(UnderlineColorProperty);
set => SetValue(UnderlineColorProperty, value);
}
public static BindableProperty UnderlineThicknessProperty = BindableProperty.Create(
nameof(UnderlineThickness), typeof(int), typeof(MyEntry), 0);
public int UnderlineThickness
{
get => (int)GetValue(UnderlineThicknessProperty);
set => SetValue(UnderlineThicknessProperty, value);
}
public MyEntry()
{
}
}
2. Define class MyEntryHandler
to render to native UI object(s).
This is done with a partial class
. One part is cross-platform, then need another part for each platform that you implement.
In my repo, find MyEntryHandler.cs
, Windows/MyEntryHandler.Windows.cs
, and Android/MyEntryHandler.Android.cs
.
MyEntryHandler.cs:
This contains the "Mapper" for MyEntryHandler.
// Cross-platform partial of class. See Maui repo maui\src\Core\src\Handlers\Entry\EntryHandler.cs
public partial class MyEntryHandler : IMyEntryHandler //: EntryHandler
{
// static c'tor.
static MyEntryHandler()
{
// TBD: Fill MyMapper here by copying from Entry.Mapper, then add custom ones defined in MyEntry?
}
//public static IPropertyMapper<IEntry, IEntryHandler> MyMapper => Mapper;
public static IPropertyMapper<IEntry, MyEntryHandler> MyMapper = new PropertyMapper<IEntry, MyEntryHandler>(ViewMapper)
{
// From Entry.
[nameof(IEntry.Background)] = MapBackground,
[nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing,
[nameof(IEntry.ClearButtonVisibility)] = MapClearButtonVisibility,
[nameof(IEntry.Font)] = MapFont,
[nameof(IEntry.IsPassword)] = MapIsPassword,
[nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
[nameof(IEntry.VerticalTextAlignment)] = MapVerticalTextAlignment,
[nameof(IEntry.IsReadOnly)] = MapIsReadOnly,
[nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
[nameof(IEntry.Keyboard)] = MapKeyboard,
[nameof(IEntry.MaxLength)] = MapMaxLength,
[nameof(IEntry.Placeholder)] = MapPlaceholder,
[nameof(IEntry.PlaceholderColor)] = MapPlaceholderColor,
[nameof(IEntry.ReturnType)] = MapReturnType,
[nameof(IEntry.Text)] = MapText,
[nameof(IEntry.TextColor)] = MapTextColor,
[nameof(IEntry.CursorPosition)] = MapCursorPosition,
[nameof(IEntry.SelectionLength)] = MapSelectionLength,
// From MyEntry
[nameof(MyEntry.UnderlineThickness)] = MapUnderlineThickness
};
// TBD: What is this for? Cloned one on Entry.
private static void MapUnderlineThickness(MyEntryHandler arg1, IEntry arg2)
{
}
public MyEntryHandler() : base(MyMapper)
{
}
I did not yet create minimal partial classes in ALL platform folders. In the repo's cross-platform MyEntryHandler, you'll see code inside #if WINDOWS
. The intent is that this NOT need to be wrapped in #if
. You'll also see a lot of commented out code; this was so I could see what methods needed to be implemented on each platform.
MyEntryHandler.Windows.cs:
The essence is CreatePlatformView()
. On Windows, I chose to implement as a Border
(with zero on all sides except bottom) containing a TextBox
.
protected override PlatformView CreatePlatformView()
{
var myentry = VirtualView as MyEntry;
var textbox = new MauiPasswordTextBox
{
// From EntryHandler.
IsObfuscationDelayed = s_shouldBeDelayed
// TODO: pass some entry properties through to textbox?
};
MauiColor color = myentry != null
? myentry.UnderlineColor
: MyEntry.UnderlineColorProperty.DefaultValue as MauiColor;
int thickness = myentry != null
? myentry.UnderlineThickness
: (int)MyEntry.UnderlineThicknessProperty.DefaultValue;
var border = new Border
{
Child = textbox,
BorderBrush = color.ToPlatform(),
BorderThickness = new Thickness(0, 0, 0, thickness)
};
return border;
}
There are many other lines of the Windows Handler. These are all copied from Maui source. TBD which (if any) of these are needed. If I'd figured out how to simply inherit from Maui's EntryHandler, those would not be needed. But there were type
conflicts when I inherited.
3: AddHandler in MauiProgram.
MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(MyEntry), typeof(MyEntryHandler));
})
...
You'll see other classes added to the Maui project in repo. These are copied from Maui sources.
These other classes are referenced by the classes mentioned above.
Hopefully most of the other classes go away, once this topic is better understood.
On Windows, AppShell + MainPage with two MyEntrys. One is underlined and colored.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:myviews="clr-namespace:MauiCustomEntryHandler"
x:Class="MauiCustomEntryHandler.MainPage">
<ScrollView>
<VerticalStackLayout
WidthRequest="500" HeightRequest="400"
Spacing="25" Padding="30,0" BackgroundColor="LightBlue"
HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="Hello, Maui!" FontSize="24" HorizontalOptions="Center" />
<myviews:MyEntry Text="test" FontSize="20" UnderlineThickness="8"
UnderlineColor="Purple" BackgroundColor="HotPink" />
<myviews:MyEntry UnderlineThickness="0" BackgroundColor="LightGray" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>