Xamarin.Tip – Binding a Picker to an Enum

So a recent Xamarin.Forms update released the new Bindable Picker, which allows you to bind an IList of objects to the picker (which will be ToString()‘ed). However, I’ve often find myself needing to create a form for a model that has enum properties. Previously, in order to do this, I would have to create a custom List or string from my enum and map it manually, then read from the SelectedItem bound to another string property, then when I need the actual value I’d have to map it back to the enum it “represents”.

It might have looked something like this:

MyViewModel.cs

...
private DogBreed _breedEnum; // this is our enum of: BorderCollie, LabradorRetriever, PitBull, etc.

public List<string> BreedNames
{
    get
    {
        return new List<string> { "Border Collie", "Labrador Retriever", "Pit Bull" };
    }
}

private string _selectedBreed;
public string SelectedBreed
{
    get
    {
        return _selectedBreed;
    }
    set
    {
        Set(ref _selectedBreed, value); // this is using MvvmLight
    }
}

public void DoSomethingWithTheBreed()
{
    switch(SelectedBreed)
    {
        case "Border Collie": _breedEnum = DogBreed.BorderCollie;
            break;
        case "Labrador Retriever": _breedEnum = DogBreed.LabradorRetriever;
            break;
        case "Pit Bull": _breedEnum = DogBreed.PitBull;
            break;
        //...
    }

    DoSomething(_breedEnum);
}
...

And our XAML

<Picker ItemsSource="{Binding BreedNames}" SelectedItem="{Binding SelectedBreed}"/>

As you can see, this is pretty gross…

Here’s a quick little strategy I use to make the binding process a little easier with my enums. It’s broken into just 3 quick parts:

  1. Create a extension methods to get a readable string from our `enum`
  2. Create a `Converter` to convert the `SelectedIndex` to the `enum` field
  3. Wire up the fields and XAML

Let’s create our enum extension methods to get a readable string for the UI:
StringExtensions.cs

    public static class StringExtensions
    {
        public static string SplitCamelCase(this string str)
        {
            return Regex.Replace(
                Regex.Replace(
                    str,
                    @"(\P{Ll})(\P{Ll}\p{Ll})",
                    "$1 $2"
                ),
                @"(\p{Ll})(\P{Ll})",
                "$1 $2"
            );
        }
    }

This SplitCamelCase method will take a string that is camel cased and split it out into separate words such as `”ThisIsMyValue”.SplitCamelCase(); // “This Is My Value”

Now that we have the ability to get a readable string from the enum values, let’s create our ViewModel properties we will need.

MyViewModel.cs

...
private DogBreed _selectedBreed;
public DogBreed SelectedBreed
{
    get
    {
        return _selectedBreed;
    }
    set
    {
        Set(ref _selectedBreed, value);
    }
}

public List<string> BreedNames
{
    get
    {
        return Enum.GetNames(typeof(DogBreed)).Select(b => b.SplitCamelCase()).ToList();
    }
}

public void DoSomethingWithBreed()
{
    DoSomething(SelectedBreed);
}
...

So much cleaner already. Now we need to create a Converter that our XAML can use to actually set the SelectedBreed property of our ViewModel.

IntEnumConverter.cs

    public class IntEnumConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is Enum)
            {
                return (int)value;
            }
            return 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value is int)
            {
                return Enum.ToObject(targetType, value);
            }
            return 0;
        }
    }

Last thing to do is use our IntEnumConverter and our properties to create our view in XAML:

MyView.xaml

<ContentPage.Resources>
    <ResourceDictionary>
        <converters:IntEnumConverter x:Key="IntEnum"/>
    </ResourceDictionary>
</ContentPage.Resources>
<Picker ItemsSource="{Binding BreedNames}" SelectedIndex="{Binding SelectedBreed, Converter=IntEnum}"/>

Here’s what we have!

Now you have the means to bind any of your Pickers quite easily to any of your custom enum fields!

“Woah! How did you get that Material Design Looking Picker on iOS”… Stay tuned!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Xamarin.Forms Borderless Entry

Here’s a quick freebee. If you want to create an entry that has no border, it can be done with a pretty simple custom renderer.

Let’s first create a new control that inherits Entry:

BorderlessEntry.cs

 public partial class BorderlessEntry : Entry
    {
        public BorderlessEntry()
        {
            InitializeComponent();
        }
    }

We aren’t doing any special logic or anything here since all we need to do is remove the border.

Now let’s create our renderer on Android:

BorderlessEntryRenderer.cs


[assembly: ExportRenderer(typeof(BorderlessEntry), typeof(BorderlessEntryRenderer))]
namespace YOUR_NAMESPACE
{
    public class BorderlessEntryRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement == null)
            {
                Control.Background = null;
            }
        }
    }
}

Note that setting the background drawable to null will kill the border (including the bottom line in AppCompt).

Now let’s create our renderer on iOS:

BorderlessEntryRenderer.cs

[assembly: ExportRenderer(typeof(BorderlessEntry), typeof(BorderlessEntryRenderer))]
namespace YOUR_NAMESPACE
{
    public class BorderlessEntryRenderer : EntryRenderer
    {
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            
            Control.Layer.BorderWidth = 0;
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

The main property here is the BorderStyle.

Lastly, UWP (this should also be the same thing for WP and Win8):

BorderlessEntryRenderer.cs

[assembly: ExportRenderer(typeof(BorderlessEntry), typeof(BorderlessEntryRenderer))]
namespace YOUR_NAMESPACE
{
    public class BorderlessEntryRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.BorderThickness = 0;
            }
        }
    }
}

That’s all there is to it!

Xamarin.Controls – Creating your own Markdown TextBlock in UWP

After talking about using the MarkdownTextView I created and how to accomplish rendering Markdown in iOS and Android without a WebView, I received a few requests to do it for UWP as well.

Check these previous posts out:

  1. Xamarin.Controls – MarkdownTextView
  2. Xamarin.Controls – Creating Your Own Android Markdown TextView
  3. Xamarin.Controls – Creating Your Own iOS Markdown UILabel

Let’s dive in to rendering markdown into a TextBlock in UWP. We’ll break it down into a few steps:

  1. Parse a markdown string into an html string
  2. Create a Behavior for the TextBlock
  3. Parse the html into relevant Span tags in the behavior
  4. Use the new Behavior in our XAML or C#

 

Parsing Markdown

This is traditionally the most difficult part. However, our community is awesome and open sourced a Markdown processor with an MIT license (so use it freely!).

I won’t put the actual code in here because it is overwhelmingly long, but here is a link to it:

https://github.com/SuavePirate/MarkdownTextView/blob/master/src/Forms/SPControls.MarkdownTextView/SPControls.MarkdownTextView/Markdown.cs

Note that this is portable, so you can use it in a PCL without a problem and share it between your platforms.

Now that we have our means of processing the Markdown, let’s create some extension methods to make it easier to parse and do some extra processing like cleaning up our tags, line breaks, etc.

#region MARKDOWN STYLES
private const string ORIGINAL_PATTERN_BEGIN = "<code>";
private const string ORIGINAL_PATTERN_END = "</code>";
private const string PARSED_PATTERN_BEGIN = "<font color=\"#888888\" face=\"monospace\"><tt>";
private const string PARSED_PATTERN_END = "</tt></font>";

#endregion

public static string ToHtml(this string markdownText)
{
    var markdownOptions = new MarkdownOptions
    {
        AutoHyperlink = true,
        AutoNewlines = false,
        EncodeProblemUrlCharacters = false,
        LinkEmails = true,
        StrictBoldItalic = true
    };
    var markdown = new Markdown(markdownOptions);
    var htmlContent = markdown.Transform(markdownText);
    var regex = new Regex("\n");
    htmlContent = regex.Replace(htmlContent, "
");

    var html = htmlContent.HtmlWrapped();
    var regex2 = new Regex("\r");
    html = regex.Replace(html, string.Empty);
    html = regex2.Replace(html, string.Empty);
    return html;
}

///
<summary>
/// Wrap html with a full html tag
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public static string HtmlWrapped(this string html)
{
    if (!html.StartsWith("<html>") || !html.EndsWith("</html>"))
    {
        html = $"<html><body>{html}</body></html>";
    }
    return html;
}

///<summary>
/// Parses html with code or pre tags and gives them proper
/// styled spans so that Android can parse it properly
/// </summary>
/// <param name="htmlText">The html string</param>
/// <returns>The html string with parsed code tags</returns>
public static string ParseCodeTags(this string htmlText)
{
    if (htmlText.IndexOf(ORIGINAL_PATTERN_BEGIN) < 0) return htmlText;
    var regex = new Regex(ORIGINAL_PATTERN_BEGIN);
    var regex2 = new Regex(ORIGINAL_PATTERN_END);

    htmlText = regex.Replace(htmlText, PARSED_PATTERN_BEGIN);
    htmlText = regex2.Replace(htmlText, PARSED_PATTERN_END);
    htmlText = htmlText.TrimLines();
    return htmlText;
}

public static bool EqualsIgnoreCase(this string text, string text2)
{
    return text.Equals(text2, StringComparison.CurrentCultureIgnoreCase);
}

public static string ReplaceBreaks(this string html)
{
    var regex = new Regex("
");
    html = regex.Replace(html, "\n");
    return html;
}

public static string ReplaceBreaksWithSpace(this string html)
{
    var regex = new Regex("
");
    html = regex.Replace(html, " ");
    return html;
}

public static string TrimLines(this string originalString)
{
    originalString = originalString.Trim('\n');
    return originalString;
}

Now we can properly parse markdown to html:

var markdown = "# Hello *World*";
var html = markdown.ToHtml();
// html = "<h1>Hello <strong>World</strong></h1>"

Create a Behavior

I’m going to take some inspiration from Shawn Kendrot and his post here.

The first thing Shawn does is implement a base Behavior class, so let’s go ahead and use that here:

    // WinRT Implementation of the base Behavior classes
    public abstract class Behavior<T> : Behavior where T : DependencyObject
    {
        protected T AssociatedObject
        {
            get { return base.AssociatedObject as T; }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            if (this.AssociatedObject == null) throw new InvalidOperationException("AssociatedObject is not of the right type");
        }
    }

    public abstract class Behavior : DependencyObject, IBehavior
    {
        public void Attach(DependencyObject associatedObject)
        {
            AssociatedObject = associatedObject;
            OnAttached();
        }

        public void Detach()
        {
            OnDetaching();
        }

        protected virtual void OnAttached()
        {

        }

        protected virtual void OnDetaching()
        {

        }

        protected DependencyObject AssociatedObject { get; set; }

        DependencyObject IBehavior.AssociatedObject
        {
            get { return this.AssociatedObject; }
        }
    }

Now we can create our actual Behavior implementation to handle the html parsing. If you want the entire file, you can find it in a gist here. Or keep reading so we can break it down.

HtmlTextBehavior.cs

public class HtmlTextBehavior : Behavior<TextBlock>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated += OnAssociatedObjectLayoutUpdated;
    }
 
    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
        AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
    }
 
    private void OnAssociatedObjectLayoutUpdated(object sender, object o)
    {
        UpdateText();
    }
 
    private void OnAssociatedObjectLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        UpdateText();
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
    }
 
    private void UpdateText()
    {
        if (AssociatedObject == null) return;
        if (string.IsNullOrEmpty(AssociatedObject.Text)) return;

        string text = AssociatedObject.Text;

        // Just incase we are not given text with elements.
        string modifiedText = string.Format("<div>{0}</div>", text);

        // reset the text because we will add to it.
        AssociatedObject.Inlines.Clear();
        try
        {
            var element = XElement.Parse(modifiedText);
            ParseText(element, AssociatedObject.Inlines);
        }
        catch (Exception)
        {
            // if anything goes wrong just show the html
            AssociatedObject.Text = text;
        }
        AssociatedObject.LayoutUpdated -= OnAssociatedObjectLayoutUpdated;
        AssociatedObject.Loaded -= OnAssociatedObjectLoaded;
    }
    
    /// <summary>
    /// Traverses the XElement and adds text to the InlineCollection.
    /// </summary>
    /// <param name="element"></param>
    /// <param name="inlines"></param>
    private static void ParseText(XElement element, InlineCollection inlines)
    {
        if (element == null) return;

        InlineCollection currentInlines = inlines;
        var elementName = element.Name.ToString().ToUpper();
        switch (elementName)
        {
            case ElementA:
                var link = new Hyperlink();
                var href = element.Attribute("href");
                if(href != null)
                {
                    try
                    {
                        link.NavigateUri = new Uri(href.Value);
                    }
                    catch (System.FormatException) { /* href is not valid */ }
                }
                inlines.Add(link);
                currentInlines = link.Inlines;
                break;
            case ElementB:
            case ElementStrong:
                var bold = new Bold();
                inlines.Add(bold);
                currentInlines = bold.Inlines;
                break;
            case ElementI:
            case ElementEm:
                var italic = new Italic();
                inlines.Add(italic);
                currentInlines = italic.Inlines;
                break;
            case ElementU:
                var underline = new Underline();
                inlines.Add(underline);
                currentInlines = underline.Inlines;
                break;
            case ElementBr:
                inlines.Add(new LineBreak());
                break;
            case ElementP:
                // Add two line breaks, one for the current text and the second for the gap.
                if (AddLineBreakIfNeeded(inlines))
                {
                    inlines.Add(new LineBreak());
                }

                Span paragraphSpan = new Span();
                inlines.Add(paragraphSpan);
                currentInlines = paragraphSpan.Inlines;
                break;
            // TODO: Add ElementH1 - ElementH6 handlers here. They should behave the same way as ElementP with increased font size.
            case ElementLi:
                inlines.Add(new LineBreak());
                inlines.Add(new Run { Text = " • " });
                break;
            case ElementUl:
            case ElementDiv:
                AddLineBreakIfNeeded(inlines);
                Span divSpan = new Span();
                inlines.Add(divSpan);
                currentInlines = divSpan.Inlines;
                break;
        }
        foreach (var node in element.Nodes())
        {
            XText textElement = node as XText;
            if (textElement != null)
            {
                currentInlines.Add(new Run { Text = textElement.Value });
            }
            else
            {
                ParseText(node as XElement, currentInlines);
            }
        }
        // Add newlines for paragraph tags
        if (elementName == ElementP)
        {
            currentInlines.Add(new LineBreak());
        }
    }
    /// <summary>
    /// Check if the InlineCollection contains a LineBreak as the last item.
    /// </summary>
    /// <param name="inlines"></param>
    /// <returns></returns>
    private static bool AddLineBreakIfNeeded(InlineCollection inlines)
    {
        if (inlines.Count > 0)
        {
            var lastInline = inlines[inlines.Count - 1];
            while ((lastInline is Span))
            {
                var span = (Span)lastInline;
                if (span.Inlines.Count > 0)
                {
                    lastInline = span.Inlines[span.Inlines.Count - 1];
                }
            }
            if (!(lastInline is LineBreak))
            {
                inlines.Add(new LineBreak());
                return true;
            }
        }
        return false;
    }
}

That’s a lot of stuff to look at. Let’s break it down. The core of the processing is in the UpdateText and ParseText methods.

Essentially what we are doing here is some layout management, and then parsing the html (parsed into an XElement). It’s different from the approaches we took in Android and iOS where there are native APIs that can parse HTML automatically. But if you looked at the Android post I made before, you’ll remember the extra TagHandler we created that took unsupported html element types and parsed them into Spans for proper formatting. What we did in that situation is a simplified version of what we accomplish here. We take relevant html tags, and create Spans, Bolds, HyperLinks and other relevant types in order to be able to render it within our final TextBlock.

There are a few element types in the parsing that are not included, but can be pretty easily added. For example, code, pre, h1h6, etc. However, these can be easily added to the switch statement! Most of these are going to be handled the same way that ElementP does. So you can add them, and adjust the FontSize or FontFamily of your Span!

Applying the Behavior

Now that we have our HtmlTextBehavior built out, we can apply it to our actual TextBlock!

In XAML:

<TextBlock Text="{Binding MyHtmlText}" FontSize="20" TextWrapping="Wrap">
    <Interactivity:Interaction.Behaviors>
        <local:HtmlTextBehavior />
    </Interactivity:Interaction.Behaviors>
</TextBlock>

Now we can render out MyHtmlText property from our ViewModel directly in our TextBlock!

Xamarin.Tips – Create a Bindable Picker in Xamarin.Forms

EDIT: I forgot to mention, but there was a Pull Request approved and added to the Xamarin.Forms repository that is currently available in Xamarin.Forms v2.3.4-pre1 and should release with the next major update. https://developer.xamarin.com/releases/xamarin-forms/xamarin-forms-2.3/2.3.4-pre1/

It’s unfortunate that Xamarin.Forms’ Picker does not ship with any ability to bind the options. To get around this, we can create a pretty simple control: The BindablePicker

public partial class BindablePicker : Picker
{
    public BindablePicker()
    {
    InitializeComponent();
    this.SelectedIndexChanged += OnSelectedIndexChanged;
    }

    public static BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(BindablePicker), default(IEnumerable), propertyChanged: OnItemsSourceChanged);

    public static BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(BindablePicker), default(object), propertyChanged: OnSelectedItemChanged);
    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    private static void OnItemsSourceChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var picker = bindable as BindablePicker;
        picker.Items.Clear();
        if (newvalue != null)
        {

            foreach (var item in (IEnumerable)newvalue)
            {
                picker.Items.Add(item.ToString());
            }
        }

        // TODO: Add more methods for removing items here
    }

    private void OnSelectedIndexChanged(object sender, EventArgs eventArgs)
    {
        if (SelectedIndex &lt; 0 || SelectedIndex &gt; Items.Count - 1)
        {
            SelectedItem = null;
        }
        else
        {
            SelectedItem = Items[SelectedIndex];
        }
    }
    private static void OnSelectedItemChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var picker = bindable as BindablePicker;
        if (newvalue != null)
        {
            picker.SelectedIndex = picker.Items.IndexOf(newvalue.ToString());
        }
    }
}

Then you can use the picker in your XAML like so:

<views:BindablePicker ItemsSource="{Binding TypeList}" Title="Type" SelectedItem="{Binding CurrentType}"/>;

intellitect.com also has a great post on how to do something similar to allow for an enum to be bound to the options.

Xamarin.Android Continuous Integration with Visual Studio Team Services

Have you or someone you know been physically harmed or emotionally scarred by the stress of setting up Continuous Integration on VSTS for your Xamarin.Android project? If your answer is yes, you are entitled to no compensation, but this blog post might help.

Easily one of the most frustrating experiences I’ve had with CI so far. It seemed that after I worked through one issue breaking the build, another two popped up. It’s like fighting the Hydra of builds.

Of course everything built fine on my local machine as well as on my other developers’ machines! So WTF VSTS???

Let’s look at some common errors you might have run into. If none of these cover your problems, leave a comment and we can try to sort it out after.

“Major version 51 is newer than 50, the highest major version supported by this compiler.”

Maybe you’ve run into this problem during your actual builds in the past. Basically the issue is that the latest Android Support Libraries build with a newer version of the compiler. The fix is simple – in VSTS, set your JDK version explicitly to 8.

  1. In your build definition, go to your Build Xamarin.Android project step
    vstsdroid
  2. Scroll down the the JDK options section
  3. Select JDK Version 
  4. Select JDK 8 from the JDK Version dropdown
    VSTSJDK.PNG
  5. Save
  6. Run

 

“java.lang.OutOfMemoryError. Consider increasing the value of $(JavaMaximumHeapSize)”

The error is at least pretty explanatory. You ran out of memory in your Java Heap.

The solution is to increase your Java Heap explicitly:

  1. In your IDE (VS or XS), go to your Android project
  2. Open the properties for your project
  3. Set your configuration to Release (or whatever configuration your VSTS build runs as)
  4. Go to the Android Options Tab
  5. Go to the Advanced tab
  6. Go down to the Advanced Android Build Settings
  7. Set the Java Max Heap Size to something larger such as “1G”

javaheap

 

These errors can be common and are only applicable to basic Android builds. This does not cover Continuous Deployment to services such as HockeyApp or Visual Studio Mobile Center.

Don’t forget to leave a comment if you’ve run into any different issues!

[Video Series] Xamarin.Tips: Calling Platform-Specific Code from a PCL

Learn how to use 4 methods to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.

  1. Singleton Method
  2. Xamarin.Forms Dependency Service
  3. Service Locator
  4. Dependency Injection

 

Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable