Showing posts with label Xamarin. Show all posts
Showing posts with label Xamarin. Show all posts

Friday, December 19, 2014

Checking For Network Reachabilty In Xamarin

Just ran into this little gem. Everything that I found on how to detect network reachability in says that the following code should be used.
await Network.IsReachable(url, new TimeSpan(NetworkTimeout))
This worked on every iOS device I could get, except the most important device - the client's. It works on iPhone 4s, 5, and 5s, also it worked on my iPad, but not the client's iPhone 5. After much research and frustration, I started diving into the Network.InternetConnectionStatus() function to see if that would be fruitful. I tried the following:
return !Network.InternetConnectionStatus().HasFlag(NetworkStatus.NotReachable);
which naturally (for Xamarin) it is not reliable. I ended up trying the following setup, which seemed to work for on my devices.
var networkstatus = Network.InternetConnectionStatus();
return 
    networkstatus.HasFlag(NetworkStatus.ReachableViaWiFiNetwork) || 
    networkstatus.HasFlag(NetworkStatus.ReachableViaCarrierDataNetwork);
After pushing the build out to the client, he was able to log in. This way has some downsides, as it doesn't check to see if you can actually reach the site you want, but at least you can detect if there is network connectivity.

I would love to submit a bug, but I am not sure how to reliably recreate the issue. It is little things like this that do not build my confidence in Xamarin.

Wednesday, December 17, 2014

Xamarin Forms Non-Native CheckBox

Having experience developing iOS applications, I know that there is no "check box" per say. When I searched for a Xamarin Forms built in check box and was left with only an native implementation, this would have been acceptable, except that they were causing some significant performance issues/lag when a view containing the controls would be added to the navigation stack.

In my last couple posts, I dove into creating non-native controls in Xamarin Forms. I can build on that knowledge to build a Xamarin Forms checkbox hopefully improve performance. This definitely renders a lot faster.
<localcontrol:CheckBoxView Checked="{Binding SomeBooleanProperty}" DefaultText="Check Box Text" ReadOnly="true" HorizontalOptions="FillAndExpand" TextColor="#000000" FontSize="12" />
The source code...
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       x:Class="IntPonApp.Controls.CheckBoxView">

 <StackLayout x:Name="CheckBoxStack" Orientation="Horizontal">
  <StackLayout.GestureRecognizers>
   <TapGestureRecognizer 
      Command="{Binding CheckCommand}"
      CommandParameter="#" />
  </StackLayout.GestureRecognizers>
  <Image x:Name="boxImage" 
      Source="{Binding BoxImageSource}" />
  <Label x:Name="textLabel" 
      Text="{Binding Text}" 
      LineBreakMode="WordWrap" 
      XAlign="Center" 
      HorizontalOptions="StartAndExpand" 
      VerticalOptions="Center" 
      TextColor="{Binding TextColor}" 
      Font="{Binding Font}" />
 </StackLayout>
 
</ContentView>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Labs;
using CustomControls;
using System.Diagnostics;
using System.Windows.Input;

namespace Check123Mobile.Controls
{
    public partial class CheckBoxView
    {
        #region Properties

        /// <summary>
        /// The width request in inches property.
        /// </summary>
        public static readonly BindableProperty CheckedProperty =
            BindableProperty.Create<CheckBoxView, bool>(
                p => p.Checked, false
                , propertyChanged: new BindableProperty.BindingPropertyChangedDelegate<bool>(
                    (BindableObject obj, bool oldPlaceHolderValue, bool newPlaceHolderValue) =>
                    {
                        var cbv = (CheckBoxView)obj;
                        cbv.BoxImageSource = cbv.GetCheckBoxImageSource();
                    })
                );

        protected static readonly BindableProperty BoxImageSourceProperty =
            BindableProperty.Create<CheckBoxView, ImageSource>(
                p => p.BoxImageSource, null);

        /// <summary>
        /// The read only property.
        /// </summary>
        public static readonly BindableProperty ReadOnlyProperty =
            BindableProperty.Create<CheckBoxView, bool>(
                p => p.ReadOnly, false);

        /// <summary>
        /// The checked text property.
        /// </summary>
        public static readonly BindableProperty CheckedTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.CheckedText, string.Empty);

        /// <summary>
        /// The unchecked text property.
        /// </summary>
        public static readonly BindableProperty UncheckedTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.UncheckedText, string.Empty);

        /// <summary>
        /// The checked image property.
        /// </summary>
        public static readonly BindableProperty CheckedImageProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.CheckedImage, string.Empty);

        /// <summary>
        /// The unchecked image property.
        /// </summary>
        public static readonly BindableProperty UncheckedImageProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.UncheckedImage, string.Empty);

        /// <summary>
        /// The default text property.
        /// </summary>
        public static readonly BindableProperty DefaultTextProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.Text, string.Empty);

        /// <summary>
        /// Identifies the TextColor bindable property.
        /// </summary>
        /// 
        /// <remarks/>
        public static readonly BindableProperty TextColorProperty =
            BindableProperty.Create<CheckBoxView, Color>(
                p => p.TextColor, Color.Black);

        /// <summary>
        /// The font size property
        /// </summary>
        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create<CheckBoxView, double>(
                p => p.FontSize, -1);

        /// <summary>
        /// The font name property.
        /// </summary>
        public static readonly BindableProperty FontNameProperty =
            BindableProperty.Create<CheckBoxView, string>(
                p => p.FontName, string.Empty);

        public static ImageSource CheckedImageSource { get; protected set; }

        public static ImageSource UncheckedImageSource { get; protected set; }

        /// <summary>
        /// The checked changed event.
        /// </summary>
        public EventHandler<EventArgs<bool>> CheckedChanged;

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        public bool Checked
        {
            get
            {
                return this.GetValue<bool>(CheckedProperty);
            }

            set
            {
                this.SetValue(CheckedProperty, value);
                var eventHandler = this.CheckedChanged;
                if (eventHandler != null)
                {
                    eventHandler.Invoke(this, value);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        public bool ReadOnly
        {
            get
            {
                return this.GetValue<bool>(ReadOnlyProperty);
            }

            set
            {
                this.SetValue(ReadOnlyProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the checked text.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string CheckedText
        {
            get
            {
                return this.GetValue<string>(CheckedTextProperty);
            }

            set
            {
                this.SetValue(CheckedTextProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string UncheckedText
        {
            get
            {
                return this.GetValue<string>(UncheckedTextProperty);
            }

            set
            {
                this.SetValue(UncheckedTextProperty, value);
            }
        }

        public ImageSource BoxImageSource
        {
            get
            {

                return this.GetValue<ImageSource>(BoxImageSourceProperty) ?? GetCheckBoxImageSource();
            }

            set
            {
                this.SetValue(BoxImageSourceProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the checked text.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string CheckedImage
        {
            get
            {
                return this.GetValue<string>(CheckedImageProperty);
            }

            set
            {
                this.SetValue(CheckedImageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is checked.
        /// </summary>
        /// <value>The checked state.</value>
        /// <remarks>
        /// Overwrites the default text property if set when checkbox is checked.
        /// </remarks>
        public string UncheckedImage
        {
            get
            {
                return this.GetValue<string>(UncheckedImageProperty);
            }

            set
            {
                this.SetValue(UncheckedImageProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        public string DefaultText
        {
            get
            {
                return this.GetValue<string>(DefaultTextProperty);
            }

            set
            {
                this.SetValue(DefaultTextProperty, value);
            }
        }

        public Color TextColor
        {
            get
            {
                return this.GetValue<Color>(TextColorProperty);
            }

            set
            {
                this.SetValue(TextColorProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the size of the font.
        /// </summary>
        /// <value>The size of the font.</value>
        public double FontSize
        {
            get
            {
                return (double)GetValue(FontSizeProperty);
            }
            set
            {
                SetValue(FontSizeProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the name of the font.
        /// </summary>
        /// <value>The name of the font.</value>
        public string FontName
        {
            get
            {
                return (string)GetValue(FontNameProperty);
            }
            set
            {
                SetValue(FontNameProperty, value);
            }
        }

        public Font Font
        {
            get
            {
                return Font.SystemFontOfSize(FontSize);
            }
        }

        public string Text
        {
            get
            {
                return this.Checked
                    ? (string.IsNullOrEmpty(this.CheckedText) ? this.DefaultText : this.CheckedText)
                        : (string.IsNullOrEmpty(this.UncheckedText) ? this.DefaultText : this.UncheckedText);
            }
        }

        public ICommand CheckCommand { get; protected set; }

        #endregion Properties

        #region Constructor

        public CheckBoxView()
        {
            CheckCommand = new Command((object s) =>
                {
                    if (!ReadOnly)
                    {
                        Checked = !Checked;
                    }
                });
            InitializeComponent();
            LoadImages();
            CheckBoxStack.BindingContext = this;
            boxImage.BindingContext = this;
            textLabel.BindingContext = this;
        }

        #endregion Constructor
         
        #region Image Functions

        protected void LoadImages()
        {
            if (CheckedImageSource == null)
            {
                CheckedImageSource = ImageSource.FromResource(GetCheckedImage());
            }
            if (UncheckedImageSource == null)
            {
                UncheckedImageSource = ImageSource.FromResource(GetUncheckedImage());
            }
        }

        private ImageSource GetCheckBoxImageSource()
        {
            return this.Checked ? CheckedImageSource : UncheckedImageSource;
        }

        private string GetCheckBoxImage()
        {
            return this.Checked
                        ? GetCheckedImage()
                        : GetUncheckedImage();
        }

        private string GetCheckedImage()
        {
            return (string.IsNullOrEmpty(this.CheckedImage) ?
                            "Check123Mobile.Resources.checked_checkbox.png" :
                            this.CheckedImage);
        }

        private string GetUncheckedImage()
        {
            return (string.IsNullOrEmpty(this.UncheckedImage) ?
                            "Check123Mobile.Resources.unchecked_checkbox.png" :
                            this.UncheckedImage);
        }

        #endregion Image Functions
    }
}

Tuesday, October 7, 2014

Xamarin Forms Custom Controls Without Native Renderers

My last blog post discussed how to create reusable custom content views. It involved leveraging the ContentPage element's ViewModel. This is great when you want to easily pull common code out and make it reusable. This will be a great place to start building.

As with my last blog post, I was not able to find examples of people doing this pattern. It seems like such a common thing to want to do. I digress.

Let's say that there is a repeated layout structure which only differs by the ViewModel properties. What about making a custom control which do not depend on a ViewModel?

A custom control which doesn't depend on a ViewModel internally, will need to have properties which we can bind to a ContentPage ViewModel's property. We want to end up with something like:
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />
The first task is to add a new Forms Xaml Page to the project. I'll call it "CountLabelView".

Then, my experience with building native controls comes in handy. We can add BindableProperty fields to our custom control's code behind. One for each property we want to have available to our control.
public static readonly BindableProperty TextProperty =
    BindableProperty.Create<CountLabelView, string>(
        p => p.Text, 
        "", 
        BindingMode.TwoWay, 
        null,
        new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
        null, 
        null);

public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
}

static void TextChanged(
    BindableObject obj, 
    string oldPlaceHolderValue, 
    string newPlaceHolderValue)
{
    
}
Now we have a way for a ContentPage to bind properties to custom ContentView controls.

Next we can move our layout into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
    xmlns:custom="clr-namespace:CustomControls.Controls;assembly=CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    x:Class="IntPonApp.Controls.CountLabelView">
 
 <StackLayout 
        Orientation="Horizontal" 
        HorizontalOptions="FillAndExpand" 
        VerticalOptions="FillAndExpand" 
        Padding="0">
  <custom:RoundFrame 
            HorizontalOptions="CenterAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="7,1" 
            BorderRadius="40" 
            FillColor="#333333" 
            HasShadow="false">

   <Label    x:Name="CountTextLabel"
                Text="{Binding CountText}" 
                VerticalOptions="Center" 
                XAlign="Center" 
                TextColor="#FFFFFF" />

  </custom:RoundFrame>

  <StackLayout 
            Spacing="0" 
            HorizontalOptions="StartAndExpand" 
            VerticalOptions="CenterAndExpand" 
            Padding="0">

   <Label    x:Name="TextLabel"
                Text="{Binding Text}" 
                LineBreakMode="WordWrap" 
                XAlign="Center" 
                HorizontalOptions="StartAndExpand" 
                VerticalOptions="Center" 
                TextColor="#DEDEDE" />

  </StackLayout>
 </StackLayout>
 
</ContentView>
This is great except, if you run this, the application will fail (unless CountText and Text happen to exist in your ContentPage's ViewModel. This is problematic, but not an insurmountable obstacle. We may have a couple options:
  1. We can remove the binding from the elements, assign names to the elements which need binding, hook up onchange events (not useful in this case because we are using labels and not Entry elements) which would assign the value back to the BindableProperty, and then find the named elements and assign the value from the BindableProperty.
  2. We can assign names to the elements which need binding and then find the named elements and assign the BindingContext to the custom control object.
  3. We can assign the custom control's BindingContext to the custom control itself.
Option 1 would be a mess to implement and maintain. It has to be replicated for every control which needs binding and there is a good chance that the bindings won't work as one should expect.

Option 2: We are able to bind in a much more expected fashion and changes will cascade as we expect. However this will still result in extra 1 line of code per control needing to be bound.

Option 3: It gets rid of the inherited BindingContext and forces the control to be self sufficient and thus reusable regardless of the ViewModel. Plus, the bindings work as one expects and it is only ONE line of code.

In testing Option 3, I found that the BindingContext was not being inherited correctly to the child controls. I suspect this is a bug in Xamarin. So, at the moment Option 2 is the best option that works.

Below is the code behind for the custom control.
public partial class CountLabelView
{
    #region Properties

    public static readonly BindableProperty TextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.Text, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(TextChanged), 
            null, 
            null);

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    static void TextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {
        
    }


    public static readonly BindableProperty CountTextProperty =
        BindableProperty.Create<CountLabelView, string>(
            p => p.CountText, 
            "", 
            BindingMode.TwoWay, 
            null,
            new BindableProperty.BindingPropertyChangedDelegate<string>(CountTextChanged), 
            null, 
            null);

    public string CountText
    {
        get { return (string)GetValue(CountTextProperty); }
        set { SetValue(CountTextProperty, value); }
    }

    static void CountTextChanged(
        BindableObject obj, 
        string oldPlaceHolderValue, 
        string newPlaceHolderValue)
    {

    }

    #endregion Properties

    #region Constructor

    public CountLabelView()
    {
        InitializeComponent();
        CountText = "1";
        //this.BindingContext = this;
        CountTextLabel.BindingContext = this;
        Text1Label.BindingContext = this;
        Text2Label.BindingContext = this;
    }

    #endregion Constructor
}
A point of note, the constructor contains this line CountText = "1";. This is required because there is a custom native control surrounding the label with a binding. For some reason the rendering process executes initially before the binding finishes and not having an initial value on the control will result in the custom native control's height to be ~1px. I suspect that this is another bug in Xamarin.

Then you add the XML namespace to the ContentPage declaration and add the custom control.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:CountLabelView
    CountText="{Binding path=MessageCount}"
    Text="{Binding path=MessageText}" />

Wednesday, October 1, 2014

Xamarin Forms Reusable Custom Content Views

There is a lot (relatively) of information on how to make reusable native controls with renderers and native views. However there is next to nothing about how to make reusable controls which don't require renderers or native views.

[Edit 2014-12-05] The project I developed required the same header on each page, so I figured I would use that as a way to describe how to create resuable controls. Performance-wise, it shouldn't be any worse than loading the information on every page. I needed the NavigationPage functionality, but without the navigation bar. It is probably not ideal, but it is what the design imposed.

Let's assume that we have the following XAML which needs to be replicated on several pages.
<Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>
My first foray into this was unsuccessful because I tried using a View. I stumbled upon the ContentView. I couldn't find any examples of people using it. Having had a lot of experience with WPF and knowing what I wanted to achieve, this seemed to be the most promising path.

I created a new Forms Xaml Page and changed the ContentPage tags to ContentView. Then I moved the reused XAML into the ContentView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:IntPonApp;assembly=IntPonApp"
        x:Class="IntPonApp.Controls.SharedHeaderView">
  
  <Grid x:Name="SharedHeaderBar" HorizontalOptions="FillAndExpand" Padding="5,10,5,-5" BackgroundColor="#3FB5C1" >
    <Grid.RowDefinitions>
      <RowDefinition Height="45" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Image x:Name="homeImageiOS" Source="{local:ImageResource IntPonApp.Resources.IntPon-Logo.png}" Grid.Row="0" Grid.Column="0" />
    <Button x:Name="logoffButtoniOS" Text="{Binding Path=FullName}" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="2" />
  </Grid>

</ContentView>
It is important to understand the state of the binding context. The binding context will be inherited from the parent control where it is used. This is very convenient because I wanted to pull a value from the ContentPage's ViewModel.

I pulled over the event bindings and the needed navigation functions. Part of the functionality that I wanted to move into the ContentView needed to raise a DisplayAlert which, as it turns out, isn't available on the ContentView element. So I flexed my recursive skills and created the following to get the ContentPage element.
public ContentPage FindParentPage(Element el = null)
{
    if (el == null)
        el = this;
    return 
          (el is ContentPage) ? (ContentPage)el
        : (el.Parent != null) ? FindParentPage(el.Parent) 
        : null;
}
Then I was able to display alert messages.

Below is the resulting code behind.
public partial class SharedHeaderView
{
    public SharedHeaderView()
    {
        InitializeComponent();

        string platformName = Device.OS.ToString();

        this.FindByName<Button>("logoffButton" + platformName)
            .Clicked += OnLogoffClicked;
        this.FindByName<Image>("homeImage" + platformName)
            .GestureRecognizers.Add(new TapGestureRecognizer((view, args) =>
        {
            this.Navigation.PopToRootAsync();
        }));
    }

    protected async void OnLogoffClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(App.ApiKey))
        {
            string errorMessage = "";
            try
            {
                if (await FindParentPage()
                    .DisplayAlert("Sign Off", "Are you sure?", "Yes", "No"))
                {
                    App.Logout();
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                await FindParentPage()
                    .DisplayAlert("Help", errorMessage, "OK");
            }
        }
    }

    public ContentPage FindParentPage(Element el = null)
    {
        if (el == null)
            el = this;
        return 
              (el is ContentPage) ? (ContentPage)el
            : (el.Parent != null) ? FindParentPage(el.Parent) 
            : null;
    }
}
Then you add the XML namespace to the ContentPage declaration and add the XML node.
xmlns:localcontrol="clr-namespace:IntPonApp.Controls;assembly=IntPonApp"
<localcontrol:SharedHeaderView />
Getting this to work has really saved me a lot of unneeded duplication.

Edit 2014-12-05: Edited to add comment/response/excuse to a question on StackOverflow: Is it possible to create a custom page layout with Xamarin.Forms?