Ever came across an instance where you wished if you had more control or customization over the Text and Icon properties of your Xamarin.Forms Button? even better even without any Custom Renderers or Platform Specific code? Welcome to another lightening short post of me hacking around Xamarin.Forms elements!
Uh oh, in Xamarin.Forms?!
The default Xamarin.Forms Button has it’s own limitations for customisation, specially in the Text and Icon, there’s not much control over those properties in terms of,
- Alignment of Text and Icon
- Positioning of Text and Icon
- Default upper case Text in Android
- Icon Image Size and Aspect Property
- Icon Source only limited for Local Images
Talking of the Icon Source, you can only use Platform Specific Local Images for it, you cannot use Embedded Resources for it.
So I thought of making use of my own crazy imagination and hack my way around to fix all those limitations!
No custom renderers, no platform specific code and no third party libraries! Just by using pure out of the box Xamarin.Forms! 😉
Usual approach…
Now as an out of the box solution for this we could avoid using Xamarin.Forms Button totally and switch to a Xamarin.Forms Label or Image control with a Tap gesture recognizer attached to it. But then it wouldn’t actually give that nice look and feel of a button does it, specially in Android that nice ripple effects and on iOS the fade out effect on the button click is something really nice to have on your UI.
So… wait for it….
XFHACKS Receipe!
Here’s my awesome solution, we’re still going to stick to Xamarin.Forms Button button, and we’ll be laying down a Label or an Image on top of our Button view inside a Grid layout. This is actually very common hack of mine if you had gone through previous XFHACKS articles of my blog.
Yep sounds super simple, yet solves all of the issues I just mentioned above. Basically we’re constructing our own Custom Button control right from Xamarin.Forms elements, properties and behaviours without any custom renderers or platform code.
So getting into more details, here’s how we’re going to solve the above talked issues. For Alignment of the Text and Icon in the button, we are going to use the HorizontalOption of both Image and Label element that we’re placing on top of the Button. Then the Position of those elements, we shall resort to the Margin property of each. Since we’re using the Image control itself the Source property issue is automatically solved. Then as an added advantage you could also have the control over the Aspect property of the Image Icon you want to display in your button.
Now on top of all that one might wonder when you place a Label or Image on top of a Button, wouldn’t it obstruct the Clickable touch area of the Button? That’s where InputTransparent comes into rescue, passing down the touch even down to the Button straight away!
To add some cherry on top of the icing, we’re going to use the IsClippedToBounds property to crop out any areas of the inner elements of the Grid being rendered outside the View bounds of the Grid itself, so everything comes together as a single Element on UI.
Now all these comes together solving the issues that I have pointed out at the beginning! 😉
Sneak Peak!
That’s what we gonna be build yol!
Code and Run!
Behold the golden XAML code!
<!-- Button with Text and Icon -->
<Grid
Grid.Row="1"
Grid.Column="0"
HeightRequest="40"
HorizontalOptions="Fill"
IsClippedToBounds="True"
VerticalOptions="Center">
<!-- Button Control -->
<Button BackgroundColor="#2196F3">
<Button.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="Android" Value="-4,-6,-4,-5" />
<On Platform="iOS" Value="0" />
</OnPlatform>
</Button.Margin>
</Button>
<!-- Text Label -->
<Label
Margin="10,0,0,0"
FontAttributes="Bold"
FontSize="Small"
HorizontalOptions="Start"
HorizontalTextAlignment="Center"
InputTransparent="True"
Text="go next"
TextColor="White"
VerticalOptions="Center"
VerticalTextAlignment="Center" />
<!-- Icon Image -->
<Image
Margin="0,0,5,0"
HeightRequest="30"
HorizontalOptions="End"
InputTransparent="True"
Source="{extensions:ImageResource XFHacks.Resources.rightarrowicon.png}"
VerticalOptions="Center"
WidthRequest="30" />
</Grid>
There you have it as we discussed earlier, our empty Button and on top of that the Label and the Image elements inside a Grid. As you can see the Button has some Margin value added for Android run time of, “-4,-6,-4,-5” which is to get rid of the default empty space that’s rendered around the Button at Android run time. The button will be spread across the whole Grid in background with its default HorizontalOptions=”Fill” property.
The Grid’s IsClippedToBounds=”True” property value makes sure it will cut off any inner elements that will render themselves out of the bounds of Grid. You can set whatever the HeightRequest or WidthRequest as you wish if you want to customize this even further.
Now speaking of the Label and Image you can see how I’m using the advantage of HorizontalOptions to align the elements as whatever the way I wish along with the Margin property of them, adding space wherever I wish. In here we have pushed the Label to the beginning of the Button and the Icon to the End of the Button horizontally.
Next the InputTransparent=”True” comes in solving the touch issue, which will pass the touch action down to the Button element when the user clicks on it, giving the exact effect of a Button. The use of Image element we can now set whatever the size we wish for our Icon inside the button and adjust its Aspect property and so on.
And let’s try something else as well! Lets have a Button which has its Text and Icons aligned to the Right most corner.
<!-- Button with Text and Icon -->
<Grid
Grid.Row="0"
Grid.Column="1"
HeightRequest="40"
HorizontalOptions="Fill"
IsClippedToBounds="True"
VerticalOptions="Center">
<!-- Button Control -->
<Button Grid.ColumnSpan="2" BackgroundColor="#2196F3">
<Button.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="Android" Value="-4,-6,-4,-5" />
<On Platform="iOS" Value="0" />
</OnPlatform>
</Button.Margin>
</Button>
<!-- Text Label -->
<Label
Grid.Column="0"
Margin="0,0,5,0"
FontAttributes="Bold"
FontSize="Small"
HorizontalOptions="End"
HorizontalTextAlignment="End"
InputTransparent="True"
Text="favs"
TextColor="White"
VerticalOptions="Center"
VerticalTextAlignment="Center" />
<!-- Icon Image -->
<Image
Grid.Column="1"
Margin="0,0,10,0"
HeightRequest="30"
HorizontalOptions="End"
InputTransparent="True"
Source="{extensions:ImageResource XFHacks.Resources.staricon.png}"
VerticalOptions="Center"
WidthRequest="30" />
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
Here’s another example where I have pushed the Image Icon to the End of the Button and the Label to be following it horizontally.
You can follow the same implementation and have our Button’s Text and Icon aligned t other Left most corner.
Now just like that you can customize all the aspects of a Button using this hack I just shared, just set up the Label and Icon with whatever the properties and customization as you wish on top of a Button. That’s it!
Important: You could also move that whole piece of XAML to a separate XAML file, so that you could set it up as a reusable Control in your project! 😉
Fire it up!
Let me share some examples I’ve built using this awesomeness!
There you go our awesome Custom Button control running on Android, as you can see with all the preserved Button click effect! 😀
Since its completely out of the box Xamarin.Forms, you can run it across all the native platforms and expect the same results! 😉
Grab it on Github!
https://github.com/UdaraAlwis/XFHacks
Well then, that’s it for now. More awesome stuff on the way!
Cheers! 😀 share the love!