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 = new Thickness(0);
            }
        }
    }
}

That’s all there is to it!

Advertisement

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 – Extending Xamarin Plugins

An Introduction to Plugins

If you’re on my blog, you know I love Xamarin. Writing cross-platform applications and sharing as much code as possible is, in my opinion, the way to go. The Xamarin community has taken an awesome technology, and made it even more awesome by building tons and tons of plugins. These plugins allow us to access platform-specific functionality from shared code, which simply allows us as application developers to write even more code in our portable projects.

I ran into an interesting forum post talking about the structure of plugins made a standard by Xamarin. A lot of use these plugins, but of course, they are not made for everyone! I’ve seen many repositories riddled with issues asking for more and more features in the plugin. This post is going to show some ways to expand on those features with your own code! As well as show techniques to use these plugins with Inversion of Control and Dependency Injection, rather than through the Singleton that the standard ships with.

Suggested Resources Before Starting

It might be a good idea to read up on my blogs and watch my videos about calling platform specific code from a PCL. Check out the YouTube playlist for some different techniques. Specifically looking at the Singleton method and Dependency Injection method.

Resources for Examples

We are going to look at arguably my all time favorite plugin: UserDialogs by Allan Ritchie

If you haven’t used it yet, it’s a cool plugin that allows you to call platform specific pop ups and dialogs including alerts, toasts, prompts, action sheets, confirmation messages, and more.

We are also going to be using MvvmLight (as I tend to do) in order to use SimpleIoc for our Inversion of Control and Dependency Injection.

Let’s first look at using IoC and DI, then look at extending the functionality.

Use Dependency Injection to Call Your Plugins

If we look at Singleton of UserDialogs, we see that it simply changes the Init method based on the platform, and uses that to set the platform-specific implementation of IUserDialogs. The core of the functionality comes from that implementation of IUserDialogs. So rather than going through the Init method of the Singleton, we can instead register the implementation to an IoC container, and inject it into the constructor of our ViewModel or Service that calls it!

Let’s create a ViewModel that takes an IUserDialogs in its constructor:

public class MyViewModel : ViewModelBase
{
    ...
    private readonly IUserDialogs _userDialogs;
    public MyViewModel(IUserDialogs userDialogs)
    {
        _userDialogs = userDialogs;
    }
    ...
 }

Now that our ViewModel has a reference to an IUserDialogs, we can make calls to it from ICommands or methods.

public void ErrorToast(string message)
{
    // show a toast message with a red background
    _userDialogs.Toast(new ToastConfig(message).SetMessageTextColor(System.Drawing.Color.White).SetBackgroundColor(System.Drawing.Color.FromArgb(255, 213, 0, 0)));
}

 

 

Now all we need to do is register both our IUserDialogs platform implementation and our MyViewModelto our IoC container.

In our PCL or SharedLibrary:


...
ServiceLocator.SetLocatorProvider(() =&gt; SimpleIoc.Default);
...
SimpleIoc.Default.Register&lt;MyViewModel&gt;();
...

Then in our Android project:


...

 ActivityLifecycleCallbacks.Register(activity);
var userDialogsImplementation = new UserDialogsImpl(() =&gt; ActivityLifecycleCallbacks.CurrentTopActivity);
SimpleIoc.Default.Register&lt;IUserDialogs&gt;(() =&gt; userDialogsImplementation);
...

And in iOS:

...
// note iOS doesn't require registering lifecycle callbacks like iOS does.
SimpleIoc.Default.Register&lt;IUserDialogs, UserDialogsImpl();
...

Now when we create our MyViewModel, we don’t do it through calling new MyViewModel(), we will call to get the instance from the ServiceLocator. For example, we can set the BindingContext of a Page:

 

...
public MyMainPage()
{
    BindingContext = ServiceLocator.Current.GetInstance&lt;MyViewModel&gt;();
}
...

That’s all we need to do! As long as we make those Register calls for our MyViewModel and IUserDialogs before the call to the MyMainPage constructor, everything will be wired up and Injected.

Now that we have our application using Dependency Injection instead of the Singleton, it makes it even easier to override and extend the functionality of the plugin.

Changing Features and Functionality

There are only two things we need to change to our implementation from above.

  1. Create a new platform implementation of IUserDialogs that inherits from the UserDialogsImpl.
  2. Change the IoC Registration to use our new implementation.

Let’s create a new class called MyUserDialogsImpl that will inherit from UserDialogsImpl. In this class, we will override the Alert method and do something like change the Tint color on iOS:


public class MyUserDialogsImpl : UserDialogsImpl
{
    ...
    public override IDisposable Alert(AlertConfig config)
    {
        return this.Present(() =&gt;
        {
             var alert = UIAlertController.Create(config.Title ?? String.Empty, config.Message, UIAlertControllerStyle.Alert);

            // custom piece:
            alert.View.TintColor = UIColor.Red;

             alert.AddAction(UIAlertAction.Create(config.OkText, UIAlertActionStyle.Default, x =&amp;amp;amp;amp;amp;gt; config.OnAction?.Invoke()));
             return alert;
         });
     }

 ...
}

Now where we registered the IUserDialogs before, we just substitute our new implementation!

 

...
// note iOS doesn't require registering lifecycle callbacks like iOS does.
SimpleIoc.Default.Register&lt;IUserDialogs, MyUserDialogsImpl&gt;();
...

So now our IoC set up will automatically set up our new implementation, so when we call the Alert method, it will show the Tint color as Red.

Adding New Features to Plugins

Similarly to how we changed functionality, we can add completely new functionality in just a few steps:

  1. Create a new Interface that inherits IUserDialogs in our shared code.
  2. Add a new method to that interface
  3. Change our new implementation to also implement that new interface
  4. Change our IoC Registration to register our implementation as our new interface instead of IUserDialogs
  5. Change our injected dependency in our MyViewModel to our new interface

In our shared code, let’s create an interface called ICustomDialogs and add a method to its definition:


public interface ICustomDialogs : IUserDialogs
{
    void SayHello();
}

 

Now, let’s update our MyUserDialogsImpl to implement our new interface:


public class MyUserDialogsImpl : UserDialogsImpl, ICustomDialogs
{
    public void SayHello()
    {
        this.Alert(&quot;Hello&quot;, &quot;World&quot;);
    }
}

Next, we need to update our IoC Register call:

 

 


...
SimpleIoc.Default.Register&lt;ICustomDialogs, MyUserDialogsImpl&gt;();
...

Lastly, let’s change our MyViewModel to use our new interface:

public class MyViewModel : ViewModelBase 
{ 
    ...
    private readonly ICustomDialogs _userDialogs; 
    public MyViewModel(ICustomDialogs userDialogs)
    {
        _userDialogs = userDialogs; 
    } 
    ...
}

Now we can call our SayHello method from our ViewModel too!

Recap

  1. Xamarin Plugins are awesome
  2. You don’t have to use Plugin Singletons!
  3. You can use dependency injection to inject your plugin implementations
  4. You can override methods in your platform specific implementations of your plugins
  5. You can create new functionality through another interface and updated injections!

Xamarin.Controls – JsonTextView

I had a relatively unique requirement where we needed to list out items in a single list that were originated from Json. That’s obviously pretty straightforward. However, each of these items coming back as Json had DRASTICALLY different properties.

The go-to thought is to use different DataTemplates for each unique type that was coming back. However, there was no consistency on top of around 100 different types we were going to get. This sounds like a design flaw in what is coming down from the server, but we didn’t have control over that.

I put my solution up on GitHub as a reusable control: https://github.com/SuavePirate/JsonTextView

It’s simply a control for Xamarin.Forms that has a bindable property called Json that consumes a Json string. It then takes that Json and puts it into an organized Label using Span classes:

public void SetJson(string json)
{
    try
    {
        var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
        foreach (var pair in dict)
        {
            var boldSpan = new Span()
            {
                Text = UpperCase ? $"{pair.Key.SplitCamelCase().ToTitleCase()}: " : $"{pair.Key.SplitCamelCase()}: ",
                FontAttributes = FontAttributes.Bold
            };
            var detailSpan = new Span()
            {
                Text = pair.Value.SplitCamelCase().ToTitleCase()
            };
            var lineBreak = new Span()
            {
                Text = "\n"
            };
            FormattedText.Spans.Add(boldSpan);
            FormattedText.Spans.Add(detailSpan);
            FormattedText.Spans.Add(lineBreak);
        }
    }
    catch
    {
        WriteLine("Failed to parse json.");
    }
}

This also uses two extension methodsthat come in handy:

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"
     );
}

public static string ToTitleCase(this string str)
{
    return string.Join(" ", str.Split(' ').Select(i => $"{i.Substring(0, 1).ToUpper()}{i.Substring(1).ToLower()}").ToArray());
}

The usage is then pretty simple:

XAML


<ContentPage
  ...
  xmlns:sp="clr-namespace:SPControls;assembly=SPControls"
  ...>
    <Grid>
      <sp:JsonTextView Json="{Binding JsonString}" />
    </Grid>
</ContentPage>

Or in C#

...
var jsonTextView = new SPControls.JsonTextView();
jsonTextView.Json = jsonString;
...

Check out the repository for the full code, and help contribute! Fork the repository, add what features you’d like to see, then submit a pull request.

Xamarin.Tips – Super Simple Sqlite

Thinking about locally storing data and entity structures can be intimidating. You might have a complex data structure in your backend server structure and are trying to match that type of data in your mobile apps. Perhaps you’ve just never done it before and need a solution quickly. You might be over thinking it!

Let’s break it down into some straightforward steps.

  1. Create a Xamarin PCL or Shared project. It doesn’t matter if it’s Xamarin.Forms or native.
  2. Install this nuget package in your PCL or Shared project: https://www.nuget.org/packages/sqlite-net-pcl/
  3. Install the same nuget package in your Android, iOS, UWP, and any other project.
  4. Create your model in your PCL or Shared project. In this case, we will use a basic example model:
    public class Profile
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Handle { get; set; }
        public DateTime CreatedDate { get; set; }
    }
    
  5. Create a DbContext class in your PCL or Shared project:
    public class DbContext
    {
        public static string LocalFilePath; // Set this before creating from platform project
        public SQLiteAsyncConnection Database { get; }
        /// <summary>
        /// Initialized a new DbContext
        /// </summary>
        public DbContext()
        {
            Database = new SQLiteAsyncConnection(LocalFilePath + "localdb.db3");
        }
    
        /// <summary>
        /// Creates a table for a given type in sql lite
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<CreateTablesResult> CreateTableAsync<T>() where T : new()
        {
        return await Database.CreateTableAsync<T>();
        }
    
        /// <summary>
        /// Gets a table by it's type from the db.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public AsyncTableQuery<T> Set<T>() where T : new()
        {
            return Database.Table<T>();
        }
    }
    
  6. Create a GenericRepository Class:
    public class GenericSqliteRepository<T> : IGenericRepository<T> where T : new()
    {
        protected readonly DbContext _context;
        public GenericSqliteRepository(DbContext context)
        {
            _context = context;
        }
        public virtual async Task InitializeAsync()
        {
            await _context.CreateTableAsync<T>();
        }
        public virtual async Task AddAsync(T entity)
        {
            await _context.Database.InsertOrReplaceAsync(entity);
        }
        public virtual async Task AddRangeAsync(IEnumerable<T> entities)
        {
            await _context.Database.InsertAllAsync(entities);
        }
    
        public virtual async Task<T> FindAsync(Expression<Func<T, bool>> predicate)
        {
            return await _context.Set<T>().Where(predicate).FirstOrDefaultAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate)
        {
            return await _context.Set<T>().Where(predicate).ToListAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(int skip, int take)
        {
            return await _context.Set<T>().Skip(skip).Take(take).ToListAsync();
        }
    
        public virtual async Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate, int skip, int take)
        {
            return await _context.Set<T>().Where(predicate).Skip(skip).Take(take).ToListAsync();
        }
    
        public virtual async Task RemoveAsync(T entity)
        {
            await _context.Database.DeleteAsync(entity);
        }
    
        public virtual async Task UpdateAsync(T entity)
        {
            await _context.Database.UpdateAsync(entity);
        }
    }
    
  7. Create a ProfileRepository Class:
    public class ProfileRepository : GenericSqliteRepository<Post>
    {
        public ProfileRepository(DbContext context) : base(context)
        {
        }
    }
    
  8. Set your file path in your platform code:
    1. Android
      DbContext.LocalFilePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
      
    2. iOS
      var docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
      string libFolder = Path.Combine(docFolder, "..", "Library", "Databases");
      
      if (!Directory.Exists(libFolder))
      {
          Directory.CreateDirectory(libFolder);
      }
      DbContext.LocalFilePath = libFolder;
      
  9. Use your repository in your shared code or platform code or wherever you want.
    var repo = new ProfileRepository(new DbContext());
    await repo.InitializeAsync();
    ...
    await repo.AddAsync(new Profile {...});
    ...
    var profiles = await repo.GetAsync(0, 10);
    ...
    var profile = await repo.FindAsync(p => p.Id == "foo");
    ...
    
  10. Start storing your things!

This is obviously a simple situation that doesn’t cover all needs, but it’s a place to start for complex data models. Build repositories for each of your types. Control your queries that are very specific in those model-specific repositories.

Look out for a follow up post about some patterns and tips to use for complex and large data sets!

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.Tips – Calling Platform-Specific Code from a Portable Class Library – Xamarin.Forms DependencyService

For those who just want code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable

For those who just want the video: [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Xamarin.Forms DependencyService)

As we develop our applications to be cross-platform, we try to share as much code between different platforms as possible. Many times, though, we have a need to call platform-specific code from that shared code; whether it be something such as playing a sound file, handling hardware readings, or using a third-party SDK that isn’t abstracted to be shareable.

In these examples, we will look at implementing parts of the HockeyApp SDK that are specific to the platform you are running against such as showing the Feedback control to ask your users to report bugs or give feedback.

There are a few ways to accomplish this, but in this post, we will look at using the Xamarin.Forms DependencyService.

The Xamarin.Forms DependencyService allows us to register implementations in our platform code as the implementation of an interface defined in our shared code. We can then use the Get() method from the DependencyService to get a reference to the platform code in our shared code.

To start, in our shared code, we will create an interface that will be implemented on our platforms:

public interface IHockeyService
{
    void Init();
    void GetFeedback();
}

 

Now in our platform projects, we will implement our IHockeyService. The important thing to note is the assembly level attribute tag we put over our namespace. This tag is what registers our implementation to the Dependency Service!

iOS:

[assembly: Xamarin.Forms.Dependency(typeof(HockeyService))]
namcespace YOUR_NAMESPACE
{
    public class HockeyService : IHockeyService
    {
        public HockeyService()
        {
        }

        public void GetFeedback()
        {
            BITHockeyManager.SharedHockeyManager.FeedbackManager.ShowFeedbackListView();
        }

        public void Init()
        {
            var manager = BITHockeyManager.SharedHockeyManager;
            manager.Configure("HOCKEY_APP_ID");
            manager.StartManager();
        }
    }
}

Android:

[assembly: Xamarin.Forms.Dependency(typeof(HockeyService))]</pre>
namcespace YOUR_NAMESPACE
{
    public class HockeyService : IHockeyService
    {
        private const string HOCKEYAPP_KEY = "HOCKEY_APP_ID";
        private readonly Android.App.Application _androidApp;
        private readonly Activity _context;

        public HockeyAppService(Activity context, Android.App.Application androidApp)
        {
            _context = context;
            _androidApp = androidApp;
        }

        public void GetFeedback()
        {
            FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext);
        }

        public void Initialize()
        {
            CrashManager.Register(_context, HOCKEYAPP_KEY);
            MetricsManager.Register(_androidApp, HOCKEYAPP_KEY);
            UpdateManager.Register(_context, HOCKEYAPP_KEY);
            FeedbackManager.Register(_context, HOCKEYAPP_KEY);
        }
    }
}

Now we need to initialize the HockeyApp manager from our shared code.

App.xaml.cs

public App ()
{
    DependenyService.Get<IHockeyService>().Init();
}

Now that our service is registered to the DependencyService, we can make calls to it from our Shared code. In this example, we will add a button to a Xamarin.Forms page with a click handler that calls the service:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourNamespace.MainPage">

    <Button Text="Get feedback with the dependency service!"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            Clicked="Feedback_Clicked"/>

</ContentPage>

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void Feedback_Clicked(object sender, EventArgs e)
    {
        DependencyService.Get<IHockeyService>().GetFeedback();
    }
}

Now our results will show that our shared code call to the service successfully shows the HockeyApp UI

simulator-screen-shot-feb-15-2017-1-47-26-pm

Generating Unique Ids on the Fly

Let’s do some JavaScript!

This post may be a little off-topic from my normal content, although it isn’t Xamarin, it could be used for hybrid frameworks like Ionic or base Cordova.

What we’re going to do is look at ways to generate unique ids on the fly for anchor tags. This could be used to help with your Google analytics or general user interaction tracking.

First thing we need is a function to generate the ids:


var lastId = 0;
function uniqueId(prefix){
    lastId++;
    return prefix+lastId;
}

Now what we need to do is use this function to set the ids on all our anchors:


document.addEventListener("DOMContentLoaded", function(event) {
    var anchors = document.getElementsByTagName("a")
    for(var i = 0; i < anchors.length; i++){
        anchors[i].id = uniqueId("generatedId");
    }
});

Doing it this way ensures that our ids are the same each time we load the page since it is based on their position when the DOM is loaded. This set up isn’t fool proof, obviously. If your page’s content is inconsistent, tags could end up with different ids than they had before. Some of these problems can be handled better by using a smarter prefix for your id.

Xamarin.Tips – Calling Platform-Specific Code from a Portable Class Library – The Singleton Method

For those who just want code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable

For those who just want the video: [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Singleton Method)

As we develop our applications to be cross-platform, we try to share as much code between different platforms as possible. Many times, though, we have a need to call platform-specific code from that shared code; whether it be something such as playing a sound file, handling hardware readings, or using a third-party SDK that isn’t abstracted to be shareable.

In these examples, we will look at implementing parts of the HockeyApp SDK that are specific to the platform you are running against such as showing the Feedback control to ask your users to report bugs or give feedback.

There are a few ways to accomplish this, but in this post, we will look at using the Singleton Method.

The Singleton Method is where we create a universal Singleton in our shared code that is initialized in our platform code, and then called from our shared code.

To start, in our shared code, we will create an interface that will be implemented on our platforms:

public interface IHockeyService
{
    void Init();
    void GetFeedback();
}

Then we create our actual Singleton in our shared code:

public class HockeyManager
{
    public static IHockeyService Current;
}

Now in our platform projects, we will implement our IHockeyService:

iOS:

public class HockeyService : IHockeyService
{
    public HockeyService()
    {
    }

    public void GetFeedback()
    {
        BITHockeyManager.SharedHockeyManager.FeedbackManager.ShowFeedbackListView();
    }

    public void Init()
    {
        var manager = BITHockeyManager.SharedHockeyManager;
        manager.Configure("HOCKEY_APP_ID");
        manager.StartManager();
    }
}

Android:

public class HockeyService : IHockeyService
{
    private const string HOCKEYAPP_KEY = "HOCKEY_APP_ID";
    private readonly Android.App.Application _androidApp;
    private readonly Activity _context;

    public HockeyAppService(Activity context, Android.App.Application androidApp)
    {
        _context = context;
        _androidApp = androidApp;
    }

    public void GetFeedback()
    {
        FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext);
    }

    public void Initialize()
    {
        CrashManager.Register(_context, HOCKEYAPP_KEY);
        MetricsManager.Register(_androidApp, HOCKEYAPP_KEY);
        UpdateManager.Register(_context, HOCKEYAPP_KEY);
        FeedbackManager.Register(_context, HOCKEYAPP_KEY);
    }
}

Now we need to initialize our Singleton from our platform-specific code. In iOS, we will do this in our AppDelegate, and in Android – our MainActivity:

iOS

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    HockeyManager.Current = new HockeyService();
    global::Xamarin.Forms.Forms.Init();
    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}

Android:

protected override void OnCreate(Bundle bundle)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;
    base.OnCreate(bundle);
    HockeyManager.Current = new HockeyService(this, Application);
    global::Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App());
}

Now we need to initialize the HockeyApp manager from our shared code.

App.xaml.cs

public App ()
{
    HockeyManager.Current.Init();
}

Now that our Singleton is created, we can make calls to it from our Shared code. In this example, we will add a button to a Xamarin.Forms page with a click handler that calls the service:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourNamespace.MainPage">

    <Button Text="Get feedback with a singleton!"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            Clicked="Feedback_Clicked"/>

</ContentPage>
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void Feedback_Clicked(object sender, EventArgs e)
    {
        HockeyManager.Current.GetFeedback();
    }
}

Now our results will show that our shared code call to the Singleton successfully shows the HockeyApp UI

simulator-screen-shot-feb-15-2017-1-47-26-pm

Xamarin.University Guest Lecture – Xamarin.Flux

Excited to announce I’ll be instructing a lecture on Xamarin University on February 23rd, 2017!

Be sure to come check it out: https://university.xamarin.com/guestlectures/architecting-your-app-with-xamarin-facebook-flux 

The topic is on using the Flux design pattern to build Xamarin applications as seen in my video and GitHub.