Friday, 10 April 2015

StringToColorConverter for Xamarin.Forms

You may have a requirement to bind a string property to a Color property, such as TextColor. If this is the case then you will need to create a converter to perform the conversion from string to Color. The following is a sample of such a converter :-

Firstly, the converter :-

using System;
using System.Globalization;
using Xamarin.Forms;
namespace LabelTextColorSample
{
    public class StringToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string valueAsString = value.ToString();
            switch (valueAsString)
            {
                case (""):
                    {
                        return Color.Default;
                    }
                case ("Accent"):
                    {
                        return Color.Accent;
                    }
                default:
                    {
                        return Color.FromHex(value.ToString());
                    }
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

Then the XAML which consumes it :-


    
        
            
        
    
    
Then the setting of the BindingContext :-

using Xamarin.Forms;
namespace LabelTextColorSample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            this.BindingContext = MyViewModel.Instance;
        }
    }
}

And finally the ViewModel :-

namespace LabelTextColorSample
{
    public class MyViewModel
    {
        private static MyViewModel _instance;
        public static MyViewModel Instance
        {
            get { return _instance ?? (_instance = new MyViewModel()); }
        }
        public string MyTextColor
        {
            get { return "#00FF00"; }
        }
    }
}

Hope this is helpful.

Wednesday, 8 April 2015

Range Slider Renderer for Xamarin.Forms

Based on the Range Slider component in the Xamarin Components store, I have created a renderer so that the Range Slider can be used in Xamarin.Forms.

Firstly, I created a RangeSlider control in the Xamarin.Forms PCL, as follows :-
using Xamarin.Forms;

namespace RangeSliderSample
{
 public class RangeSlider : View
 {
  public static readonly BindableProperty LeftValueProperty =
   BindableProperty.Create(p => p.LeftValue, 0f);

  public float LeftValue
  {
   get { return (float) GetValue(LeftValueProperty); }
   set { SetValue(LeftValueProperty, value); }
  }

  public static readonly BindableProperty RightValueProperty =
   BindableProperty.Create(p => p.RightValue, 0f);

  public float RightValue
  {
   get { return (float) GetValue(RightValueProperty); }
   set { SetValue(RightValueProperty, value); }
  }

  public static readonly BindableProperty MaxValueProperty =
   BindableProperty.Create(p => p.MaxValue, 1f);

  public float MaxValue
  {
   get { return (float) GetValue(MaxValueProperty); }
   set { SetValue(MaxValueProperty, value); }
  }

  public static readonly BindableProperty MinValueProperty =
   BindableProperty.Create(p => p.MinValue, 0f);

  public float MinValue
  {
   get { return (float) GetValue(MinValueProperty); }
   set { SetValue(MinValueProperty, value); }
  }

  public static readonly BindableProperty StepProperty =
   BindableProperty.Create(p => p.Step, 0f);

  public float Step
  {
   get { return (float) GetValue(StepProperty); }
   set { SetValue(StepProperty, value); }
  }
 }
}

I then placed this control on a XAML page :-

 
  
  
  
 



I then created a Custom Renderer in the Android project :-
using RangeSlider;
using RangeSliderSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(RangeSliderSample.RangeSlider), typeof(RangeSliderRenderer))]
namespace RangeSliderSample.Droid
{
 public class RangeSliderRenderer : ViewRenderer
 {
  private RangeSliderView _slider;
  
  protected override void OnElementChanged(ElementChangedEventArgs e)
  {
   base.OnElementChanged(e);

   var rangeSlider = e.NewElement as RangeSlider;

   if (rangeSlider != null)
   {
    _slider = new RangeSliderView(Context, rangeSlider.MinValue, rangeSlider.MaxValue, rangeSlider.Step);

    _slider.LeftValueChanged += value =>
    {
     rangeSlider.LeftValue = _slider.LeftValue;
    };

    _slider.RightValueChanged += value =>
    {
     rangeSlider.RightValue = _slider.RightValue;
    };

    SetNativeControl(_slider);
   }
  }
 }
}

The complete solution is here :-
http://www.smartmobiledevice.co.uk/Samples/Xamarin/RangeSliderSample.zip

Xamarin Android Player and McAfee

I normally use a device for all my debugging in Xamarin however I thought I would try the Xamarin Android Player.

After installing and downloading an emulator image I tried starting the emulator however I was present with the following error :-

 OpenGL server is unreachable. Please check that Xamarin Android Player is allowed through your firewall on public networks.

 As I use McAfee, my firewall settings are handled by McAfee rather than the Windows Firewall. I viewed the Firewall settings for the Xamarin Android Player (AndroidPlayer) under "View firewall and anti-spam settings > Firewall > Internet Connections for Programs", and it was set to use "Designated Ports". Editing this entry and setting Incoming and Outgoing to "Open ports to Work and Home networks" now allows me to use the Xamarin Android Player.

Wednesday, 4 March 2015

Setting Color as HSLA using XAML Extension for Xamarin.Forms

The following shows how to set a Color to a HSLA value using a XAML Extension.

Firstly, define a new class called ColorAsHslaExtension.cs :-

[ContentProperty("ColorAsHsla")]
public class ColorAsHslaExtension : IMarkupExtension
{
 public string ColorAsHsla { get; set; }

 public object ProvideValue(IServiceProvider serviceProvider)
 {
  var elements = ColorAsHsla.Split(',');

  double h = double.Parse(elements[0]);
  double s = double.Parse(elements[1]);
  double l = double.Parse(elements[2]);
  double a = double.Parse(elements[3]);

  return Color.FromHsla(h, s, l, a);
 }
}

Next, define the XAML that you make use of the Extension :-


 

Hope this helps.

Update

After reading page 146 of the following :-

https://download.xamarin.com/developer/xamarin-forms-book/BookPreview2-Ch08-Rel0203.pdf

It seems there is a much simpler way of achieving this :-


    
       
          
             0.67
             1.0
             0.5
             1.0
          
       
    

Friday, 20 February 2015

Xamarin.Forms ListView Drag and Drop to Reorder

We are currently looking at ways to add some UX improvements to our application so I thought I would investigate drag and drop on ListView.

There doesn't seem to be anything out of the box yet in Xamarin.Forms so I did a search and found the following :-

http://xamurais.com/drag-and-drop-entre-listview-en-xamarin-android/

This sample was written in classic Xamarin.Android, however I was looking for a Xamarin.Forms implementation. This sample however provided me with the ground work for the sample I propose below.

My sample is only targeting Android at the moment.

My implementation consists of a ViewCellRenderer, this allows you to define a ListView with an ItemTemplate, so that you can bind your ListView to more than a List.

MyViewCellRenderer.cs :-

using System.Collections;
using Android.Content;
using Android.Views;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using View = Android.Views.View;

[assembly: ExportRenderer(typeof(ViewCell), typeof(MyViewCellRenderer))]
namespace ListViewDragDropSample.Droid
{
 public class MyViewCellRenderer : ViewCellRenderer
 {
  public ListView ParentListView { get; set; }

  public IList Items { get; set; }

  protected override View GetCellCore(Cell item, View convertView, ViewGroup parent, Context context)
  {
   ParentListView = item.ParentView as ListView;

   if (ParentListView != null)
   {
    Items = ParentListView.ItemsSource as IList;
   }

   var cellcore = base.GetCellCore(item, convertView, parent, context);

   cellcore.Drag -= CellcoreOnDrag;
   cellcore.Drag += CellcoreOnDrag;

   return cellcore;
  }

  private void CellcoreOnDrag(object sender, View.DragEventArgs args)
  {
   ViewGroup = sender as ViewGroup;

   if (ViewGroup != null)
   {
    ListView = ViewGroup.Parent.Parent as Android.Widget.ListView;
   }

   switch (args.Event.Action)
   {
    case DragAction.Started:
     args.Handled = true;
     break;

    case DragAction.Entered:
     args.Handled = true;

     if (ListView != null)
     {
      if (FirstIndex == -1)
      {
       FirstIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
      }
     }

     break;

    case DragAction.Exited:
     args.Handled = true;
     break;

    case DragAction.Drop:
     args.Handled = true;

     if (SecondIndex == -1)
     {
      SecondIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
     }

     if (FirstIndex != -1)
     {
      var firstItem = Items[FirstIndex];

      if (firstItem != null)
      {
       Items.RemoveAt(FirstIndex);
       Items.Insert(SecondIndex, firstItem);

       ParentListView.ItemsSource = null;
       ParentListView.ItemsSource = Items;
      }
     }

     FirstIndex = -1;
     SecondIndex = -1;

     break;
    case DragAction.Ended:
     args.Handled = true;
     break;
   }
  }

  public Android.Widget.ListView ListView { get; set; }

  public ViewGroup ViewGroup { get; set; }

  private static int _firstIndex = -1;
  private static int _secondIndex = -1;

  public static int FirstIndex
  {
   get { return _firstIndex; }
   set { _firstIndex = value; }
  }
  public static int SecondIndex
  {
   get { return _secondIndex; }
   set { _secondIndex = value; }
  }
 }
}

MyListViewRenderer.cs :-

using Android.Content;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(ListView), typeof(MyListViewRenderer))]
namespace ListViewDragDropSample.Droid
{
 public class MyListViewRenderer : ListViewRenderer
 {
  protected override void OnElementChanged(ElementChangedEventArgs e)
  {
   base.OnElementChanged(e);

   Control.ItemLongClick += (s, args) =>
   {
    ClipData data = ClipData.NewPlainText("List", args.Position.ToString());
    MyDragShadowBuilder myShadownScreen = new MyDragShadowBuilder(args.View);
    args.View.StartDrag(data, myShadownScreen, null, 0);
   };
  }
 }
}

MainPage.xaml :-

 
  
   
    
     
      
    
   
  
 


MainPage.xaml.cs :-

using System.Collections.Generic;
using Xamarin.Forms;

namespace ListViewDragDropSample
{
 public partial class MainPage : ContentPage
 {
  public MainPage()
  {
   InitializeComponent();

   Items = new List();

   for (int i = 1; i < 11; i++)
   {
    Items.Add(new Item()
    {
     Title = "Title : " + i,
     Description = "Description : " + i,
    });
   }

   BindingContext = this;
  }

  public List Items { get; set; }
 }
}



MyDragShadowBuilder.cs :-
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;

namespace ListViewDragDropSample.Droid
{
 public class MyDragShadowBuilder : View.DragShadowBuilder
 {
  private Drawable shadow;

  public MyDragShadowBuilder(View v)
   : base(v)
  {
   v.DrawingCacheEnabled = true;
   Bitmap bm = v.DrawingCache;
   shadow = new BitmapDrawable(bm);
   shadow.SetColorFilter(Color.ParseColor("#4EB1FB"), PorterDuff.Mode.Multiply);
  }

  public override void OnProvideShadowMetrics(Point size, Point touch)
  {
   int width = View.Width;
   int height = View.Height;
   shadow.SetBounds(0, 0, width, height);
   size.Set(width, height);
   touch.Set(width / 2, height / 2);
  }

  public override void OnDrawShadow(Canvas canvas)
  {
   base.OnDrawShadow(canvas);
   shadow.Draw(canvas);
  }
 }
}


And finally the Item.cs :-

namespace ListViewDragDropSample
{
 public class Item
 {
  public string Title { get; set; }
  public string Description { get; set; }
 }
}

The complete sample is here :-

http://www.smartmobiledevice.co.uk/Samples/Xamarin/ListViewDragDropSample.zip

Xamarin.Forms TabbedPage and Swipe

As of version 1.3.5-pre1 of Xamarin.Forms, there doesn't seem to be a way to swipe a TabbedPage on Android out of the box.

I first thought I could add the gesture via a GestureListener in a Custom Renderer, however this causes issues when trying to swipe while showing a full screen button or ListView, as these two controls consume the Gestures, so this wasn't an option.

I then had an idea about combining a CarouselPage and TabbedPage, hence this post.

This is very convoluted and will not perform very well when used in complex UI layouts, but it might be useful.

First the XAML :-

 
  
   
    
     
      
     
    
    
    
   
  
  
   
    
    
     
      
     
    
    
   
  
  
   
    
    
    
     
      
     
    
   
  
 


As you can see this layout won't work for Windows Phone and I haven't tested this on iOS however I am only really targeting Android at the moment.

Next is the C# :-

public partial class MainPage : TabbedPage
{
 public MainPage()
 {
  InitializeComponent();

  AttachCurrentPageChanged();
 }

 private void AttachCurrentPageChanged()
 {
  Page1.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
  Page2.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
  Page3.CurrentPageChanged += MultiPage_OnCurrentPageChanged;
 }

 private void DetachCurrentPageChanged()
 {
  Page1.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
  Page2.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
  Page3.CurrentPageChanged -= MultiPage_OnCurrentPageChanged;
 }

 private void MultiPage_OnCurrentPageChanged(object sender, EventArgs e)
 {
  DetachCurrentPageChanged();

  CarouselPage carouselPage = sender as CarouselPage;
  if (carouselPage != null)
  {
   int indexOf = carouselPage.Children.IndexOf(carouselPage.CurrentPage);

   var tabbedPage = carouselPage.ParentView as TabbedPage;

   if (tabbedPage != null)
   {
    tabbedPage.CurrentPage = tabbedPage.Children[indexOf];

    var newCarouselPage = tabbedPage.CurrentPage as CarouselPage;

    if (newCarouselPage != null)
    {
     newCarouselPage.CurrentPage = newCarouselPage.Children[indexOf];
    }
   }
  }

  AttachCurrentPageChanged();
 }
}
The OnCurrentPageChanged is used to track the Page Change of the CarouselPage, the index of this page is then used to set the CurrentPage of the next CarouselPage.

As this tracking needs to occur the number of child Pages in each of the CarouselPages needs to equal the numbwe of Tabs you would like to show.

If you want to add more Tabs, you add more CarouselPage's as children of the TabbedPage and then ensure that each CarouselPage is updated with the correct number of children.

This is by far an ideal solution but was interesting nevertheless.

The complete sample is here :-

http://www.smartmobiledevice.co.uk/Samples/Xamarin/SwipeTabbedPageSample.zip