Taoffi's blog

prisonniers du temps

Xamarin Forms Maps – a (very) quick start!

Solutions to avoid some pitfalls for starting up with Xamarin forms maps package.

Before to use?

Some steps are required for your map application to be able to start and to display maps.

 

In Android project, before to use maps, you must first generate an API key. This can be done in your google account (create one if needed). You can optionally in this step link your API key to a specific application. This API key should be specified in the AndroidManifest.xml of your Android project:

<application android:label="Your app name" android:icon="@drawable/your icon">
    <meta-data android:name="com.google.android.geo.API_KEY"
            android:value="put you api ur key" />
<meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
</application>


In this same file, you should also specify the required location access permissions:

 

For iOS, you should specify the some values for the following keys in info.plist file:

<key>NSLocationAlwaysUsageDescription</key>
<string>Please allow us to use your location.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>


For WinPhone, you should specify some application capabilities as (at least):

 

For All platforms:

In each platform, call Xamarin.FormsMaps.Init(); after Xamarin.Forms.Forms.Init()

Without this call, your map will not show!

  • iOS - AppDelegate.cs file, in the FinishedLaunching method.
  • Android - MainActivity.cs file, in the OnCreate method.
  • Win Phone - MainPage.xaml.cs file, in the MainPage constructor.

 

That is what the Xamarin Forms Maps documentation saysJ. In fact I tested: that is required for iOS, not required for Android… and for Win Phone… just a minute… the emulator is starting up… IT IS required!

 

How to use?… Look at it working

To use these items, you simply insert a Map View in your Xaml page (or ContentView). Which can also be done in code. In xaml, don't forget to mention the namespace!

Here is an example of a ContentView (a UserControl for our WPF/Silverlight friends!)

<?xml version="1.0" encoding="UTF-8"?> 
<ContentView    x:Class="iMaps.iMapCtrl1" 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:map="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps" 
    > 
    <map:Map x:Name="theMap" /> 
</ContentView> 

 

Important remark: for a mysterious reason (you will see many in Xamarin environment), if the 'map' namespace specifies the assembly's extension ('.dll') that doesn't work in WinPhone (i.e. app crash!)

Should write this:

xmlns:map="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"

Not this (results in WinPhone crash):

xmlns:map="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps.dll"

 

Now, for this sample, we can insert some Pins at the ContentView (UserControl) creation:

public iMapCtrl1()
{
    InitializeComponent();
    PutSomePinsOnMap();
}

 

void PutSomePinsOnMap()
{
    // define a center point and some sample pins
    Position        tourEiffel    = new Position(48.859217, 2.293914);
    Pin[]        pins            =
    {
        new Pin() {  Label = "Tour Eiffel",
            Position = new Position(48.858234, 2.293774), Type = PinType.Place },
        new Pin() {  Label = "Concorde",
            Position = new Position(48.865475, 2.321142), Type = PinType.Place },
        new Pin() {  Label = "Étoile",
            Position = new Position(48.873880, 2.295101), Type = PinType.Place },
        new Pin() {  Label = "La Défense",
            Position = new Position(48.892418, 2.236180), Type = PinType.Place },
    };

    foreach(Pin p in pins)
    {
        theMap.Pins.Add(p);
    }

    // center the map on Tour Eiffel / set the zoom level
    theMap.MoveToRegion(MapSpan.FromCenterAndRadius(tourEiffel, Distance.FromKilometers(2.5)));
}
 

 

Once this ContentView in a Page that gives a nice view like this:

 

 

Xamarin forms: so you lost your rounded buttons?

Xamarin forms buttons can have rounded corners. Well… eventually, in Win Phone, you should handle this in a custom renderer or in a control template… we will see this in another post.

For other platforms, the way to do this is quite simple:

<Button Text="I am rounded" BorderRadius="20" BackgroundColor="Gray" />  

Android AppCompat issues

That works great on Android, until you install the Xamarin AppCompat package!

AppCompat just strips away your rounded corners and you find your button with the traditional rectangular old style!

 

Well… what is 'AppCompat'?

It is an android specific feature presented by Xamarin as:

…devices running earlier versions of Android could enjoy the beauty of Material Theming. With the latest enhancements in Xamarin.Forms 1.5.1, you can easily utilize all of the new AppCompat features, including Material Design and Theme, with just a few changes to your app code.

 

What about Win Phone and iOS in benefiting from such an interesting approach?... nobody knows for the moment. I thought we were in a cross-platform context… apparently that doesn't always apply!

 

In any case, with this great feature you simply lose some of the 'beauty' you had before in your 'traditional' environment. C'est le progrèsJ

Before AppCompat:

Android

iOS

After:

Android (AppCompat)

iOS (no AppCompat)

 

Many discussions on Xamarin forums suggest this is a 'bug'… it is not. And this thread clarifies a little bit about XF choice in AppCompat:

The old Xamarin.Forms.Platform.Android.ButtonRenderer uses the internal Xamarin.Forms.Platform.Android.ButtonDrawable which does respect it.
So you should file a bug on bugzilla (if there is none already) and write a custom renderer until they fix it.
Be sure to inherit from Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer and not from Xamarin.Forms.Platform.Android.ButtonRenderer in your renderer.

 

An interesting article (@wintellect) provides a valuable solution to implement a button renderer in Xamarin forms. Adapting the provided code to AppCompat is not much work.

By now, that should be done… let us start deploy and see. Nothing. My button is still with no rounding corners!

A debug session simply reveals that my new custom renderer is NEVER CALLED!

 

Surfing again for an answer for such strange situation, I found this answer:

Kelly Adams took the time to decompile FormsAppCompatActivity.LoadApplication and gives us a more clear answer as to why our custom renderer is never called: it is in fact simply IGNORED!

Such discrepancies make me feel: Xamarin platform is still immature!

 

This week's Hack!

AppCompat simply registers its own renderers and ignores all the rest for the following controls: Button, CarouselPage, Frame, MasterDetailPage, NavigationPage, Picker, Switch, TabbedPage and (finally) View (this last one seems to have been added after a deep thought?J)

 

 

Kelly Adams revelation opens a door for hacking!

In fact there is a Boolean field (renderersAdded) in that class. And 'default' renderers are loaded if it is still false.

So, an acrobatic solution would be to set that field to true and register your own renderers so that they can be called. So silly but effective (at least for nowJ)

 

 

// get the AppCompat activity Type information
Type         appCompact = typeof(FormsAppCompatActivity);

// get the field info (even if it is private!)
FieldInfo    renderersAdded = appCompact.GetField("renderersAdded", 
                              BindingFlags.Instance | BindingFlags.NonPublic);

// set the field value to true (fool the loader not to load its default renderers
renderersAdded.SetValue(this, true);

// get the registration method
MethodInfo regMethod = appCompact.GetMethod ("RegisterHandlerForDefaultRenderer", 
                              BindingFlags.Instance | BindingFlags.NonPublic);

// set your own renderers J
regMethod.Invoke(...

 

 

So sad… But that is the price to go for such 'unfinished' implementation of an API

Be prepared to change this in the future: the unfinished API may get a little more polished! 

 

That works (for nowJ)

You may have a look at the sample code for more details.

Xamarin forms – extending the BindableObject

 

This a follow up to my post about avoiding strings in notifying property change events.

In fact, objects deriving from BindableObject (Views, Pages…) are some of the most common places where we do notify property changes.

These notifications are naturally done using the OnPropertyChanged method of the root BindableObject. Which is presented as:

 

protected virtual void OnPropertyChanged([string propertyName = null])

Member of Xamarin.Forms.BindableObject
Summary:

Call this method from a child class to notify that a change happened on a property.
Parameters:

propertyName: The name of the property that changed.
Remarks:

A Xamarin.Forms.BindableProperty triggers this by itself. An inheritor only needs to call this for properties without Xamarin.Forms.BindableProperty as the backend store.

 

So if you have a property that requires change notification, you would write:

    this.OnPropertyChanged("myPropertyName");

 

Which brings again the string constant problem: if you, one day, change the name of your property, you should remember visiting all code that may use this name in a string constant… hard and risky labor!

 

I thought of extending the BindableObject by providing a new OnPropertyChanged method using the Linq.Expressions to avoid string constants. That cannot be straightforward though because the BindableObject's OnPropertyChanged is protected!

 

For now, I found this solution. Please feel free to change or adapt to your needs. If you may find a better way, I would be happy to hear about your solutions!

 

The recipe

Define a delegate signature of a method to be called:

    public delegate void PropertyChangedHandler(string propertyName); 

 

 

Define a class for this (and future) extensions:

    public static class BindableObjectExtensions 

 

 

Include this extension method in the class:

 

public static void OnPropertyChangedExt<TProperty>(this BindableObject obj,
                    Expression<Func<TProperty>> property,
                    PropertyChangedHandler propertyChangedHandler)
{
        if (property == null || propertyChangedHandler == null)
            return;


        string        name = GetPropertyName(property);
        propertyChangedHandler.Invoke(name);
}

 

The GetPropertyName helper method:

 

public static string GetPropertyName<TProperty>(Expression<Func<TProperty>> property)
{
    if (property == null)
        return null;


    var expression = property.Body as MemberExpression;


    if (expression == null || expression.Member == null)
        return null;


    return expression.Member.Name;
}



 

Sample usage

 

public class TestBindableExtension: BindableObject
{
    string        _test        = "Test";


    public string TestProperty
    {
        get { return _test; }
        set
        {
            _test = value;
            this.OnPropertyChangedExt(() => TestProperty, this.OnPropertyChanged);
        }
    }



 

Xamarin forms – the complete WP circle image!

James Montemagno published a post about implementing circle images using Xamarin Forms.

His proposed implementation for Win Phone was good. It clipped the image into a circle, but stopped a little short to draw the circle image border.

 

Droid

WP

 

I played with this to solve this small issue.

Here is a more complete version for WP renderer:

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
	base.OnElementPropertyChanged(sender, e);

	CircleImage		circleImg	= Element as CircleImage;

	if(circleImg == null || Control == null || Control.Clip != null || circleImg.Source == null)
		return;
			
	double		controlRadius	= Math.Min(Element.Width, Element.Height) / 2.0f;
	double		controlDiameter	= controlRadius * 2.0;

	if(controlRadius <= 0)
		return;


	// **********************************************
	// Note: this section ignores PNG transparency. 
	// **********************************************
	try
	{ 
		BitmapImage			srcBmp		= Control.Source as BitmapImage;

		WriteableBitmap		wrBmpSrc	= new WriteableBitmap((int) controlDiameter,
												 (int) controlDiameter);
		ImageBrush		imgBrush	= new ImageBrush();

		// use an image brush with the control’s image
		imgBrush.ImageSource = srcBmp;

		// set border thickness
		int	borderThickness	= circleImg.BorderThickness;
		Color	borderColor			= XamarinColor2WinColor(circleImg.BorderColor);

		// create a path with an ellipse geometry
		EllipseGeometry				ellipseG	= new EllipseGeometry()
			{
				Center		= new System.Windows.Point(controlRadius, controlRadius),
				RadiusX		= controlRadius,
				RadiusY		= controlRadius
			};
		GeometryGroup				geomGroup	= new GeometryGroup();

		geomGroup.Children.Add(ellipseG);

		System.Windows.Shapes.Path	path		= new System.Windows.Shapes.Path()
		{
			Stroke 		= new SolidColorBrush(borderColor),
			StrokeThickness	= borderThickness,
			Data			= geomGroup,
			Width			= controlDiameter,
			Height		= controlDiameter,
			Fill			= imgBrush,
		};

		wrBmpSrc.Render(path, null);
		wrBmpSrc.Invalidate();

		using (MemoryStream memStream = new MemoryStream())
		{
			wrBmpSrc.SaveJpeg(memStream, wrBmpSrc.PixelWidth, wrBmpSrc.PixelHeight, 0, 100);

			srcBmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache;

			srcBmp = new BitmapImage();
			srcBmp.SetSource(memStream);
		}

		Control.Source			= srcBmp;
	}
	catch(Exception ex)
	{
		Debug.WriteLine(ex.Message);
		return;
	}

	// clip the control into circle
	Control.Clip = new EllipseGeometry
		{
			Center	= new System.Windows.Point(controlRadius, controlRadius),
			RadiusX	= Math.Max(controlRadius, 0),
			RadiusY	= Math.Max(controlRadius, 0)
		};
}

 

 

Color conversion helper method

 

		public static Color XamarinColor2WinColor(Xamarin.Forms.Color xamColor)
		{
			return Color.FromArgb (	(byte)(xamColor.A * 255),
									(byte)(xamColor.R * 255),
									(byte)(xamColor.G * 255),
									(byte)(xamColor.B * 255));
		}

 

 

 

Droid

WP

 

Xamarin forms fonts: Handling droid formatted text spans

As we have seen in a previous post, it is relatively easy to handle custom fonts using Xamarin forms. The only tedious platform in this domain is Android where you need to write a custom renderer.

In the first version of Droid custom renderer, I didn't handle the Labels Formatted Text.

Formatted texts can be used like in this sample:

 

<Label>
    <Label.FormattedText>
        <FormattedString>
            <FormattedString.Spans>
                <Span
                    Text="Allura span"
                    ForegroundColor="Lime"
                    FontSize="36"
                    FontFamily="{StaticResource fontAlluraName}"
                    />
                <Span
                    Text=" "
                    FontSize="{StaticResource awesomeSize}"
                    ForegroundColor="Lime"
                    FontFamily="{StaticResource fontawsomeName}"
                   />
           </FormattedString.Spans>
        </FormattedString>
    </Label.FormattedText>
</Label>



 

That is a Droid-only problem!

In Windows Phone and iOS, once the fonts have been included in the project, you have nothing to do. The problem is with the Droid custom renderer!

Android has a font Typeface which acts on the Label globally, and another Type 'TypefaceSpan' which acts on a region (span) of the label's formatted text.

In the first version of Droid renderer sample, we have set the font TypeFace for the label. If that label contained formatted text with spans, they were simply ignored (rendered with a default system font).

 

TypefaceSpan (namespace Android.Text.Style in Mono.Android.dll) is a class derived from à CharacterStyle whose role is to draw a span's text with the specified font family name.

 

A solution

After searching the Internet for a while, the question didn't seem to have many people talking about.

Luckily, I fall on a piece of code (a secretJ) where I found a link about an awesome solution proposed in this page.

In fact, you may derive a class based on TypefaceSpan, and override its methods according to your specific information (notably the span's font and its attributes).

 

A Label object has a FormattedText member which may contain one or more text Spans. Each span has a set of properties of which you find the font family (and font attributes) to be applied to the span's text.

So that now becomes manageable: your derived CustomTypefaceSpan can then create the font and draws the span's text accordingly.

 

 

 

The updated version of the sample code contains more details. Have fun doing more additions… would be nice to keep me posted!

 

Xamarin forms: What is my (current) app version?

Knowing which version of our application is currently running is a useful information (for end user, but also for us, developers!)

Fortunately, as Xamarin Forms is a .NET framework, we can easily obtain this at runtime:

public string AppVersion
{

    get
    {
        Assembly        asm        = this.GetType().GetTypeInfo().Assembly;
        string        name        = iAssemblyInfo.GetAssemblyTitle(asm),
                        copyright    = iAssemblyInfo.GetAssemblyCopyright(asm);

            return name + "\n" + copyright + "\n" + asm.FullName;
    }
}

 

Some details: this (somehow concise) code benefits of a helper static class that may give you some ideas:

 

  publicstaticclass iAssemblyInfo

 

 

The class exposes several methods like:

public static string GetAssemblyCopyright(Assembly asm)
{
    if(asm == null)
        return "";

    try
    {
        var        attrib    = asm.GetCustomAttribute<AssemblyCopyrightAttribute>();

        return attrib == null ? "" : attrib.Copyright;
    }
    catch (Exception)
    {
        return "";
    }
}

 

public static string GetAssemblyTitle(Assembly asm)
{
    if(asm == null)
        return "";

    try
    {
        var        attrib    = asm.GetCustomAttribute<AssemblyTitleAttribute>();

        return attrib == null ? "" : attrib.Title;
    }
    catch (Exception)
    {
        return "";
    }
}

 

public static string GetAssemblyCompanyName(Assembly asm)
{
    if(asm == null)
        return "";

    try
    {
        var        attrib    = asm.GetCustomAttribute<AssemblyCompanyAttribute>();

        return attrib == null ? "" : attrib.Company;
    }
    catch (Exception)
    {
        return "";
    }
}

Xamarin forms: a radio button pause!

 

Check boxes and Radio buttons are two UI elements frequently used in many apps.

In Xamarin forms those two (standard) items seem to create so many discussions without really getting a final answer. Many approaches go through 'custom renderers'… and I finally dislike this. That is simply because it questions the very reason why we use Xamarin forms itself. If everyone is going to create 'custom renderers' for such standard controls, it may become useless for XF to evolve providing new standard renderers… and there will be fewer reasons to use XF (see this post).

Like for Buttons (see this post), I think a check box or radio button can be defined as a surface containing graphical shapes (and animations) to represent the state of a property. In the case of such buttons: Checked/Unchecked state (a simple Boolean).

 

Waiting for XF to bring us a 'standard' check box and radio button, we have to manufacture them ourselves in the less costly possible way: avoiding custom renderers (again: because custom renderers will, one day, conflict with XF standard renderers)

 

The case of radio buttons is more interesting for an example because the selected (checked) option is exclusive: it should automatically uncheck all other options of one same group.

There is an interesting sample here. The only thing is that it again uses custom renderers where I think we don't need them.

To illustrate this in a simple way: a radio button can be checked:

Or unchecked:

All what we need is to select the image (or path… or whatever graphical shape) related to the current option state.

Naturally, this graphical element is accompanied by a Label (or image…) which describe the related option.

As you will see in the sample code, there is much more work to represent the Option's state objects than the graphical UI part that represents this state!

The radio button control (ContentView)

The radio button control (ContentView) may look like this:

  • A grid with two column cells
  • The left cell contains the two images of the option states checked/unchecked. One of them is hidden (according to current option state).
  • The right cell contains the description of the option (here: a Label)

 

<ContentView.Content>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="28" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Image x:Name="imgUnselected" />
        <Image x:Name="imgSelected" IsVisible="{Binding IsSelected}" />
        <Label Grid.Column="1" Text="{Binding Label}"/>
    </Grid>
</ContentView.Content>


To show this simple control in action, we have to create some objects to represent the option group object and the option object.

 

  • ISelectable Interface defines the elementary option
    Note: the ISelectable (wording and Interface) have been borrowed from this interesting blog: Adventure in Xamarin Forms.
  • IExclusiveOptionSet is a template collection of ISelectable options which, in the same time, implements the ISelectableExclusiveSet Interface (see below)

 

public abstract class iExclusiveOptionSet<T> : XObjectListNotifier<T>, ISelectableExclusiveSet
                        whereT : ISelectable

 

  • XObjectListNotifier<T> list template is a root collection responsible of notifying collection changes (please see the sample code for more information)
  • The ISelectableExclusiveSet Interface is defined as:

 

public interface ISelectableExclusiveSet
{
    string        Label                { get; set; }
    ISelectable    SelectedItem    { get; set; }
}

 

  • Finally, for this sample, a demo (singleton) class provides some options to demo:

 

iOptionGroup _sampleOptionGroup    = new iOptionGroup("Sample option group");

 

_sampleOptionGroup.Add(new iOption(){ Id = 1, Label = "Sample option 1", IsSelected = true });
_sampleOptionGroup.Add(new iOption(){ Id = 2, Label = "Sample option 2", IsSelected = false });
_sampleOptionGroup.Add(new iOption(){ Id = 3, Label = "Sample option 3", IsSelected = false });
_sampleOptionGroup.Add(new iOption(){ Id = 4, Label = "Sample option 4", IsSelected = false });

 

  • An Option group control contains a stack layout. At the OnBindingContextChanged event, the stack layout is filled with the options of the received IEnumerable<iOption> collection.

 

protected override void OnBindingContextChanged()
{
    base.OnBindingContextChanged();

    this.panelItems.Children.Clear();

    var optionList = this.BindingContext as IEnumerable<iOption>;

    if (optionList == null)
        return;

    foreach(var item in optionList)
    {
        RadioButtonCtrl    ctrl    = new RadioButtonCtrl(_isReadOnly)
        {
            BindingContext        = item,
            VerticalOptions        = LayoutOptions.Start,
            HorizontalOptions    = LayoutOptions.FillAndExpand
        };

        this.panelItems.Children.Add( ctrl);
    }
}


You may download the sample code here. Have fun adding animation, colors… I promise to submit this to my friends @UXDivers… you will probably see something much more elaborate later if they find this of interest!

 

 

Xamarin forms: Traversing the View tree

 

It is sometimes necessary to find the parent of a View element.

Imagine you have a navigation control (a menu for instance) in which each button gives access to a specific page.

What if the target page is the current displayed one? (If a copy of this navigation control is, for instance, inserted inside this same page)

It would be useless (and maybe rather ugly, confusing and probably troublesome!) to try to display (push) the current page on itself.

In that case, it would be good to know if one of the ascendants of the control is this target page before trying to do the navigation.

A simple helper method can do this for us:

//
// Try to find an ascendant (parent) of type T
public static T GetParentPage<T>(View control) where T : ContentPage
{
    if(control == null)
        return null;

    var        parent    = control.Parent;

    while(parent != null)
    {
        if(parent is T)
            return parent as T;

        parent = parent.Parent;
    }
    return null;
}


Usage example:

 

//
var currentPage = UiHelpers.GetParentPage<CompanyPage>(this);

// It is alreay on screen: do nothing
if(currentPage != null)
    return;

// push a new page
await Navigation.PushAsync(new CompanyPage())

 

Searching a descendant is a little trickier… because one same page may contain several instances of one same ContentView (control). Combined with a specific BindingContext, that can be more effective. Something like:

//
public static T GetChildCntrol<T>(View control, object BindindingContext) where T : ContentView

 

You may try to play with this… Good luckJ

Xamarin forms: avoid using string constants in NotifyPropertyChanged

 

As you know, notifying property changes is an essential pattern for Bindings to work as expected on xaml UI controls. Solution objects that are related to the UI (at least those of the 'VM' in: MV[VM]) should implement the INotifyPropertyChanged Interface. That is: expose a PropertyChanged event (of Type PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e)).

Property changes consequently raise this event for any subscriber to be notified of the change.

The PropertyChangedEventArgs sent to the subscriber contains the changed property name.

So, for instance, if you change a property named BirthDate:

public DateTime BirthDate
{
    get { return _birthdate; }
    set
    {
        _birthdate = value;
        OnPropertyChanged("BrirthDate");
    }
}

One known issue is that when you change the property name, you may forget changing the name used in the change notification. And that, of course, produces unexpected behaviors that may be difficult to track.

A solution I use to avoid this (don't remember where I did found it!), is using System.Linq.Expressions to write something similar to this:

 

// Note: TProperty is the calling property Type 
protected void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property) 
{
    var expression     = property.Body as MemberExpression; 
    if (expression == null || expression.Member == null) 
        return; 
    string name        = expression.Member.Name;

    OnPropertyChanged(name); 
}

And that would allow us to avoid using string constants for property names… like in:

 

public DateTime BirthDate
{
    get { return _birthdate; }
    set
    {
        _birthdate = value;
        NotifyPropertyChanged(() => BrirthDate);
    }
}

 

 

Xamarin forms: Did you say Buttons?

 

Buttons are still the traditional elements through which the user interacts with software features.

With the mobile environment development context (dimensions, touch screen, gestures…) buttons became graphical parts that have to indicate a metaphor of the feature they give access to.

So, no surprise, many developers ask how to customize buttons to achieve this: using colors, images, symbols… etc.

The Xamarin Forms Button object

Like many other buttons, Xamarin forms button is far from able to satisfy this metaphoric aspects needed by modern mobile developers.

It represents a simple (rather poor) set of properties and leaves the developer out of intuitively finding a modern choice. You just have: Border, Font, Text…

It exposes a property Image… prowess?J… Not so fast, this property is simply defined as: Gets or sets the optional image source to display next to the text in the Button. (Faire enough!)

 

What is a Button?

After struggling sometime to find a way for 'customizing' buttons, we can just stop and ask ourselves this question. Yes, let us forget object libraries and just ask: What is a button?

As far as I know, it is a surface containing some graphic shapes… and, when clicked executes a command.

When tapped, the button produces a visual effect to mimic the reaction of a real life button.

 

Well, that now seems easy to build.

 

It can simply be a ContentView on which we may put whatever graphic elements we need (multiline texts, images… etc.). When clicked it plays an animation to mimic the press effect, and it then executes the command of our choice.

For this control to be reusable, we may expose properties needed for setting the graphic content and for handling the Click event as the developer may need.

 

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x
="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class
="iButton.iButtonCtrl" BackgroundColor="Gray">
<Frame x:Name="buttonFrame" HasShadow="False" OutlineColor="Black" />
</ContentView>

In the code behind, we expose the Content of our buttonFrame to be set with any ContentView we may find useful to use:

 

 

public ContentView ButtonContent 
{ 
    get { return this.buttonFrame.Content; } 
    set {    this.buttonFrame.Content    = value; } 
}

 

We define a Tap gesture recognizer to play the press graphical effect and raise a Clicked event to be handled by our control user.

public event ClickedHandler        ControlClicked;
ICommand    _tapCommand;

public ICommand WidgetTapped 
{ 
    get
    { 
        if(_tapCommand == null) 
        { 
            _tapCommand = new Command(async(obj) => 
            { 
               // play a press animation
                await Blink(this, 300);

                // someone subscribed to our event?: notify him
                if (ControlClicked != null) 
                    ControlClicked.Invoke(this, new EventArgs()); 
            }); 
        } 
 
        return _tapCommand; 
    } 
}

 

We can now create buttons the way we like or find useful… example:

First we include a reference to the button namespace:

    xmlns:ctrls="clr-namespace:iButton;assembly=iButton"
 

Then we can go ahead and create our buttons:

 

<StackLayoutSpacing="20"HorizontalOptions="FillAndExpand"Padding="10" >
   <ctrls:iButtonCtrlx:Name="ibutton1"BackgroundColor="#ffff9800" />
   <ctrls:iButtonCtrlx:Name="ibutton2" />
   <ctrls:iButtonCtrlx:Name="ibutton3"BackgroundColor="#ffcac8ff"/>
</StackLayout>

We insert the ContentViews we need for each button:

this.ibutton1.ButtonContent    = new iButtonView1(); 
this.ibutton2.ButtonContent    = new iButtonView2(); 
this.ibutton3.ButtonContent    = new iButtonView3(); 

// we subscribe to the Click event

ibutton1.ControlClicked += Ibutton1_ControlClicked;

 

 

 

 

Final professional touch: xaml markup extensions!

In order for people using this control to be able to use our button properties inside xaml files, we need to declare a bindable property that gives access to our: ButtonContent.

 

The sample code contains such final touches!