Xamarin.Tips – iOS Bar Background Images in Xamarin.Forms

Xamarin.Forms doesn’t ship with any way to provide images as your background of either NavigationBars or TabBars. So in order to do this, we will have to build some custom renderers.

iOS

Morgan skinner has a great post here about an approach that he took to apply background images to his NavigationBars: http://www.morganskinner.com/2015/01/xamarin-formsusing-background-images-on.html

The give you the synopsis, you take your entire background image, crop it, and apply the cropped piece as the background of the bar in your renderer.

Here’s a look at how he cropped his image:
Original Marked Up_thumb[5]

I want to take a different approach here and do this programmatically so that you can be EXACT in the background of the bar so that it can match the background of your image.

Although we are approaching this as if you are applying the background image to be the same as your page background image, the same principle applies if you just want to set any image.

Here’s a renderer to use for a NavigationBar:

BackgroundImageNavigationRenderer.cs

[assembly: ExportRenderer(typeof(NavigationPage), typeof(BackgroundImageNavigationRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageNavigationRenderer : NavigationRenderer
    {
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.BackgroundColor = UIColor.Clear;
            NavigationBar.BackgroundColor = UIColor.Clear;
            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.png");
            image = image.Crop(0,
                (int)(image.CGImage.Height - NavigationBar.Frame.Height),
                (int)image.CGImage.Width,
                (int)NavigationBar.Frame.Height);
            image = image.Scale(new CGSize(NavigationBar.Frame.Width, NavigationBar.Frame.Height));
            NavigationBar.BarTintColor = UIColor.FromPatternImage(image);
            NavigationBar.ShadowImage = new UIImage();
        }

    }
}

Breaking it down, we are taking the image that we use as the background for our entire page, then opening cropping it to the size of the NavigationBar itself.

I’ve also created an extension method called Crop so that we can reuse it here and for our UITabBar:

ImageExtensions.cs

    public static class ImageExtensions
    {
        public static UIImage Crop(this UIImage image, int x, int y, int width, int height)
        {
            var imgSize = image.Size;

            UIGraphics.BeginImageContext(new SizeF(width, height));
            var imgToCrop = UIGraphics.GetCurrentContext();

            var croppingRectangle = new CGRect(0, 0, width, height);
            imgToCrop.ClipToRect(croppingRectangle);

            var drawRectangle = new CGRect(-x, -y, imgSize.Width, imgSize.Height);

            image.Draw(drawRectangle);
            var croppedImg = UIGraphics.GetImageFromCurrentImageContext();

            UIGraphics.EndImageContext();
            return croppedImg;
        }

    }

Now, if you are not doing this to have a full background image, and just want some image for your UINavigationBar only, you can do so like this:

BackgroundImageNavigationRenderer.cs

[assembly: ExportRenderer(typeof(NavigationPage), typeof(BackgroundImageNavigationRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageNavigationRenderer : NavigationRenderer
    {
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.BackgroundColor = UIColor.Clear;
            NavigationBar.BackgroundColor = UIColor.Clear;
            var image = UIImage.FromBundle("YOUR_BAR_BACKGROUND_IMAGE.png");

            image = image.Scale(new CGSize(NavigationBar.Frame.Width, NavigationBar.Frame.Height));
            NavigationBar.BarTintColor = UIColor.FromPatternImage(image);
            NavigationBar.ShadowImage = new UIImage();
        }

    }
}

Essentially just cutting out the cropping process.

Also note that I set the NavigationBar.ShadowImage to a new UIImage();. Doing this gets rid of the line below your UINavigationBar. If you don’t want to remove the line, just don’t set the ShadowImage.

Now that we’ve done this for our UINavigationBar, we can apply the same thing to our UITabBars. So let’s create another custom renderer for that!

BackgroundImageTabbedPageRenderer.cs


[assembly: ExportRenderer(typeof(TabbedPage), typeof(BackgroundImageTabbedPageRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageTabbedPageRenderer : TabbedRenderer
    {
        public override void ViewWillLayoutSubviews()
        {
            base.ViewWillLayoutSubviews();

            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.jpg");
            image =image.Crop(0,
                (int)(image.CGImage.Height - TabBar.Frame.Height),
                (int)image.CGImage.Width,
                (int)TabBar.Frame.Height);
            image = image.Scale(new CGSize(TabBar.Frame.Width, TabBar.Frame.Height));
            TabBar.BackgroundImage = image;
            TabBar.UnselectedItemTintColor = UIColor.FromRGBA(255, 255, 255, 150);
            TabBar.ShadowImage = new UIImage();
        }

    }
}

just as we did with our UINavigationBar, we crop the page background image to the location and dimensions of our UITabBar, and set the background image there. The same thing also applies with removing the ShadowImage.

And if you are not using a full background image, and just want an image for your bar, take this:

BackgroundImageTabbedPageRenderer.cs


[assembly: ExportRenderer(typeof(TabbedPage), typeof(BackgroundImageTabbedPageRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageTabbedPageRenderer : TabbedRenderer
    {
        public override void ViewWillLayoutSubviews()
        {
            base.ViewWillLayoutSubviews();

            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.jpg");

            image = image.Scale(new CGSize(TabBar.Frame.Width, TabBar.Frame.Height));
            TabBar.BackgroundImage = image;
            TabBar.UnselectedItemTintColor = UIColor.FromRGBA(255, 255, 255, 150);
            TabBar.ShadowImage = new UIImage();
        }

    }
}

And now you can use full page images over your NavigationBars and TabBars, or just set an image as the background for your bars!

 

Simulator Screen Shot Apr 7, 2017, 11.23.33 AM

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!

Xamarin.Tips – Sending Authorized SignalR Requests

In my last post, I talked about creating a CookieProvider for handling OAuth Bearer Tokens from an HTTP Cookie. Now that we have this capability, we can make a secure connection to SignalR using a Cookie store rather than adding our tokens to the Authorization HTTP Header.

By using cookies instead of the HTTP Header, we can now use a full Websocket connection rather than being forced into long polling. Using websockets increases speed of messages received and messages sent.

So here’s the quick bit of code for making a secure SignalR Websocket connection:

SignalRConnectionService.cs

    public class SignalRConnectionService
    {
        private HubConnection _connection;
        public IHubProxy AppHubProxy { get; set; }

        public SignalRConnectionService()
        {
            _connection = new HubConnection(YOUR_SIGNALR_URL);
            AppHubProxy = _connection.CreateHubProxy("AppHub");
        }

        public async Task StartAsync()
        {
            try
            {
                if(_connection.State != ConnectionState.Disconnected)
                {
                    _connection.Stop();
                }
                _connection.CookieContainer = new CookieContainer();
                _connection.CookieContainer.Add(new Uri(THE_BASE_URL_OF_YOUR_SERVER), new Cookie("BearerToken", YOUR_ACCESS_TOKEN));
                await _connection.Start();
               
        }
        public void Stop()
        {
            _connection.Stop();
        }
    }

Now we have the full speed of websockets enabled in our Xamarin, Mac, and Windows applications!

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!

Xamarin.Controls – Xamarin.Forms PinView

In  previous post, I talked about creating a BorderlessEntry view using a Custom Renderer (or an alternative Effect). We are going to use said control in this post, so you can find it here: Xamarin.Forms Borderless Entry
On top of this control, we are also going to use a custom behavior mentioned in a blog post here: Xamarin.Tips – Restrict the Length of Your Entry Text

Now let’s talk about giving your users the ability to create a PIN to secure their account in your app while giving them a nice experience. The solution is the PinView!

We are going to build this as a custom component in Xamarin.Forms:

PinView.xaml

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"               xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"               xmlns:behaviors="clr-namespace:YOUR_NAMESPACE.Behaviors;assembly=YOUR_NAMESPACE"              xmlns:views="clr-namespace:YOUR_NAMESPACE;assembly=YOUR_NAMESPACE"              x:Class="YOUR_NAMESPACE.PinView">
    <ContentView.Resources>
        <ResourceDictionary>
<Style x:Key="PinEntry" TargetType="Entry">
                <Setter Property="Keyboard" Value="Numeric"/>
                <Setter Property="IsPassword" Value="True"/>
                <Setter Property="WidthRequest" Value="50"/>
                <Setter Property="HeightRequest" Value="50"/>
                <Setter Property="Margin" Value="8,0"/>
                <Setter Property="HorizontalTextAlignment" Value="Center"/>
            </Style>
<Style x:Key="BottomBar" TargetType="BoxView">
                <Setter Property="HeightRequest" Value="2"/>
                <Setter Property="BackgroundColor" Value="White"/>
                <Setter Property="WidthRequest" Value="50"/>
                <Setter Property="VerticalOptions" Value="Start"/>
                <Setter Property="Margin" Value="0"/>
            </Style>

        </ResourceDictionary>
    </ContentView.Resources>
  <ContentView.Content>
        <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
            <StackLayout Orientation="Vertical">
                <views:BorderlessEntry x:Name="Pin1" Style="{StaticResource PinEntry}" TextColor="White">
                    <Entry.Behaviors>
                        <behaviors:EntryLengthValidatorBehavior MaxLength="1"/>
                    </Entry.Behaviors>
                </views:BorderlessEntry>
                <BoxView Style="{StaticResource BottomBar}"/>
            </StackLayout>

            <StackLayout Orientation="Vertical">
                <views:BorderlessEntry x:Name="Pin2" Style="{StaticResource PinEntry}" TextColor="White">
                    <Entry.Behaviors>
                        <behaviors:EntryLengthValidatorBehavior MaxLength="1"/>
                    </Entry.Behaviors>
                </views:BorderlessEntry>
                <BoxView Style="{StaticResource BottomBar}"/>
            </StackLayout>
            <StackLayout Orientation="Vertical">
                <views:BorderlessEntry x:Name="Pin3" Style="{StaticResource PinEntry}" TextColor="White">
                    <Entry.Behaviors>
                        <behaviors:EntryLengthValidatorBehavior MaxLength="1"/>
                    </Entry.Behaviors>
                </views:BorderlessEntry>
                <BoxView Style="{StaticResource BottomBar}"/>
            </StackLayout>
            <StackLayout Orientation="Vertical">
                <views:BorderlessEntry x:Name="Pin4" Style="{StaticResource PinEntry}" TextColor="White">
                    <Entry.Behaviors>
                        <behaviors:EntryLengthValidatorBehavior MaxLength="1"/>
                    </Entry.Behaviors>
                </views:BorderlessEntry>
                <BoxView Style="{StaticResource BottomBar}"/>
            </StackLayout>
        </StackLayout>
  </ContentView.Content>
</ContentView>

Lastly, we add some behaviors to our code-behind:

PinView.xaml.cs

 public partial class PinView : ContentView
    {
        public static BindableProperty PinProperty = BindableProperty.Create("Pin", typeof(string), typeof(PinView), defaultBindingMode: BindingMode.OneWayToSource);
        public string Pin
        {
            get
            {
                return (string)GetValue(PinProperty);
            }
            set
            {
                SetValue(PinProperty, value);
            }
        }
        public PinView()
        {
            InitializeComponent();
            Pin = string.Empty;
            Pin1.TextChanged += Pin1_TextChanged;
            Pin2.TextChanged += Pin2_TextChanged;
            Pin3.TextChanged += Pin3_TextChanged;
            Pin4.TextChanged += Pin4_TextChanged;
        }

        private void Pin4_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (Pin4.Text.Length > 0)
                Pin4.Unfocus();
            else
                Pin3.Focus();
            Pin = Pin1.Text + Pin2.Text + Pin3.Text + Pin4.Text;
        }

        private void Pin3_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (Pin3.Text.Length > 0)
                Pin4.Focus();
            else
                Pin2.Focus();
            Pin = Pin1.Text + Pin2.Text + Pin3.Text + Pin4.Text;
        }

        private void Pin2_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (Pin2.Text.Length > 0)
                Pin3.Focus();
            else
                Pin1.Focus();
            Pin = Pin1.Text + Pin2.Text + Pin3.Text + Pin4.Text;
        }

        private void Pin1_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (Pin1.Text.Length > 0)
                Pin2.Focus();
            Pin = Pin1.Text + Pin2.Text + Pin3.Text + Pin4.Text;
        }
    }

If we dig into the behavior, we set up our TextChanged events so that when each separate Entry is updated, we move the focus to the next Entry. If we clear the text of one of the Entries, we move to the Entry before it, and when we write text into the Entry, we move to the next one.

We also use the Behavior mentioned to restrict the user from entering more than 1 character in each Entry!

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!

Xamarin.Tips – Restrict the Length of Your Entry Text

Here’s a quick one on how to restrict the number of characters a user can enter in an Entry. Basically, we are going to create a custom Behavior and then apply it to our Entry.

EntryLengthValidatorBehavior.cs

 /// <summary>
    /// Behavior that restricts the length of an entry
    /// </summary>
    public class EntryLengthValidatorBehavior : Behavior<Entry>
    {
        public int MaxLength { get; set; }

        protected override void OnAttachedTo(Entry bindable)
        {
            base.OnAttachedTo(bindable);
            bindable.TextChanged += OnEntryTextChanged;
        }

        protected override void OnDetachingFrom(Entry bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.TextChanged -= OnEntryTextChanged;
        }

        void OnEntryTextChanged(object sender, TextChangedEventArgs e)
        {
            var entry = (Entry)sender;

            if (entry.Text.Length > this.MaxLength)
            {
                string entryText = entry.Text;
                entry.TextChanged -= OnEntryTextChanged;
                entry.Text = e.OldTextValue;
                entry.TextChanged += OnEntryTextChanged;
            }
        }
    }

Now we can apply it in our Xaml:

<Entry x:Name="Pin1" TextColor="White">
    <Entry.Behaviors>
        <behaviors:EntryLengthValidatorBehavior MaxLength="4"/>
    </Entry.Behaviors>
</Entry>

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!

Xamarin.University – Upcoming Guest Lecture on Cross-Platform WebRTC

Join me on Tuesday, May 23rd and learn about bringing voice and video conferencing capabilities to your mobile apps! We will talk about the general capabilities of WebRTC, how to bring it to Native mobile development, using it in Xamarin, and extending the capabilities of it.

Leave with an app that lets you share your voice and videos with others!

Bring your devices and your questions!

Here’s a link to the guest lecture! https://university.xamarin.com/guestlectures/cross-platform-webrtc

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!

Xamarin.Tips – Create Your Own Star Wars Intro Text!

Here’s a fun one – let’s make a Xamarin.Forms page that looks like the Star Wars intro scrolling text! I also put the source up here: https://github.com/SuavePirate/StarWarsPage

Here’s the XAML for the page:

StarWarsPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:StarWarsPage"              x:Class="StarWarsPage.MainPage">
    <Grid>
        <Image Source="starwarsintrobg.jpg" Aspect="AspectFill" HorizontalOptions="Fill" VerticalOptions="Fill"/>
        <ScrollView x:Name="TextScrollView" Orientation="Vertical" RotationX="24" Padding="16,800">
            <Label x:Name="Text" Text="{StaticResource StarWarsText}" TextColor="Yellow" FontAttributes="Bold" FontSize="30" HorizontalOptions="Fill"/>
        </ScrollView>
    </Grid>
    <ContentPage.Resources>
        <ResourceDictionary>
            <x:String x:Key="StarWarsText">
                It is a period of civil war.
Rebel spaceships, striking
from a hidden base, have won
their first victory against
the evil Galactic Empire.

During the battle, Rebel
spies managed to steal secret
plans to the Empire's
ultimate weapon, the DEATH
STAR, an armored space
station with enough power
to destroy an entire planet.

Pursued by the Empire's
sinister agents, Princess
Leia races home aboard her
starship, custodian of the
stolen plans that can save her
people and restore
freedom to the galaxy....

            </x:String>
        </ResourceDictionary>
    </ContentPage.Resources>
</ContentPage>

The important part here is the RotationX value on the ScrollView. This is going to set the backwards tilt of the scroll. To break down the other parts that make this up – We have a static String resource to use as the text for the intro. In this case I’m using the crawl text from A New Hope. We also wrap the whole thing in a Grid so that we can set up the background Image element.

Now we get a cool view that the user can scroll through at their own reading pace!

StarWars

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!

Xamarin.Tips – Xamarin.Forms iOS ListView Refresh Spinner Color

Here’s a quick freebie. If you want to change the color of your ListView Refreshing spinner, we can do that with a quick custom renderer!

Here it is as a renderer:

ColoredRefreshListViewRenderer.cs

    ///<summary>
    /// Custom renderer for all list views to use the custom loader color
    /// </summary>

    public class ColoredRefreshListViewRenderer : ListViewRenderer
    {
        ///<summary>
        /// Set the loader color to your custom color
        /// </summary>
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);
            var vc = ((UITableViewController)ViewController);

            // Set the color!
            if (vc?.RefreshControl != null)
                vc.RefreshControl.TintColor = UIColor.FromRGB(135, 200, 21);
        }
    }

Now we get something looking awesome like this:
 

loading_apple_green

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!

Xamarin.Tips – Changing a TableView’s Separator Color

In previous posts, I talked about the misfortunes of the Xamarin.Forms TableView. In this post, we will look at changing the separator color with a custom renderer.

For some other Xamarin.Tips on upgrading your TableView, check out these two posts on customizing the Section Titles!

Android: Xamarin.Tips – Xamarin.Forms Android Custom TableView Section Titles
iOS: Xamarin.Tips – Xamarin.Forms iOS Custom TableView Section Titles

Let’s get started with a custom view that inherits the TableView and adds a BindableProperty for our SeparatorColor:

ColoredTableView.cs

    public partial class ColoredTableView : TableView
    {
        public static BindableProperty SeparatorColorProperty = BindableProperty.Create("SeparatorColor", typeof(Color), typeof(ColoredTableView), Color.White);
        public Color SeparatorColor
        {
            get
            {
                return (Color)GetValue(SeparatorColorProperty);
            }
            set
            {
                SetValue(SeparatorColorProperty, value);
            }
        }
        public ColoredTableView()
        {
            InitializeComponent();
        }
    }

Now let’s take this view and create our renderers!

Let’s start with Android:

ColoredTableViewRenderer.cs

[assembly: ExportRenderer(typeof(ColoredTableView), typeof(ColoredTableViewRenderer))]
namespace YOUR_ANDROID_NAMESPACE
{
    public class ColoredTableViewRenderer : TableViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
                return;

            var listView = Control as Android.Widget.ListView;
            var coloredTableView = (ColoredTableView)Element;
            listView.Divider = new ColorDrawable(coloredTableView.SeparatorColor.ToAndroid());
            listView.DividerHeight = 3;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "SeparatorColor")
            {
                var listView = Control as Android.Widget.ListView;
                var coloredTableView = (ColoredTableView)Element;
                listView.Divider = new ColorDrawable(coloredTableView.SeparatorColor.ToAndroid());
            }
        }
    }
}

Basically, we take the color and apply it as a ColorDrawable, including updating when the property changes from our Forms code.

Now let’s write up our iOS renderer:

ColoredTableViewRenderer.cs

[assembly: ExportRenderer(typeof(ColoredTableView), typeof(ColoredTableViewRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class ColoredTableViewRenderer : TableViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
                return;

            var tableView = Control as UITableView;
            var coloredTableView = Element as ColoredTableView;
            tableView.SeparatorColor = coloredTableView.SeparatorColor.ToUIColor();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "SeparatorColor")
            {
                var tableView = Control as UITableView;
                var coloredTableView = Element as ColoredTableView;

                tableView.SeparatorColor = coloredTableView.SeparatorColor.ToUIColor();
            }
        }
    }
}

Similarly to our Android implementation, we set the SeparatorColor of our Native UITableView on set up and when the value changes.

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!

Xamarin.Tips – Xamarin.Forms iOS Custom TableView Section Titles

Xamarin.Forms’ TableView is a really useful control for displaying static content. However, it has some shortfalls. The one we will look at today is customizing the Section Title. Before reading ahead, I would suggest looking at your solution and assessing whether or not your TableView could be easily replaced by a ListView. I want to point this out, because ListViews have more flexibility for GroupHeader and allow you to put any control in there without having to leave your happy place that is XAML.

If that isn’t the case, keep reading; Let’s look at some simple custom renderers that can solve this problem for iOS specifically (Android to follow).

First, let’s create a custom view that inherits TableView:

ColoredTableView.cs

 public partial class ColoredTableView : TableView
    {
        public static BindableProperty GroupHeaderColorProperty = BindableProperty.Create("GroupHeaderColor", typeof(Color), typeof(ColoredTableView), Color.White);
        public Color GroupHeaderColor
        {
            get
            {
                return (Color)GetValue(GroupHeaderColorProperty);
            }
            set
            {
                SetValue(GroupHeaderColorProperty, value);
            }
        }

        public ColoredTableView()
        {
            InitializeComponent();
        }
    }

Now let’s create a renderer in our iOS project:

ColoredTableViewRenderer.cs


[assembly: ExportRenderer(typeof(ColoredTableView), typeof(ColoredTableViewRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class ColoredTableViewRenderer : TableViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
                return;

            var tableView = Control as UITableView;
            var coloredTableView = Element as ColoredTableView;
            tableView.WeakDelegate = new CustomHeaderTableModelRenderer(coloredTableView);
        }

        private class CustomHeaderTableModelRenderer : UnEvenTableViewModelRenderer
        {
            private readonly ColoredTableView _coloredTableView;
            public CustomHeaderTableModelRenderer(TableView model) : base(model)
            {
                _coloredTableView = model as ColoredTableView;
            }
            public override UIView GetViewForHeader(UITableView tableView, nint section)
            {
                return new UILabel()
                {
                    Text = TitleForHeader(tableView, section),
                    TextColor = _coloredTableView.GroupHeaderColor.ToUIColor(),
                    TextAlignment = UITextAlignment.Center
                };
            }
        }
    }
}

Basically what we are doing is overriding the WeakDelegate of the native UITableView that is under the Xamarin.Forms TableView. That WeakDelegate is of type TableViewModelRenderer which also inherits the UITableViewSource and holds our native overrides for getting cell heights, etc. Note that this inherits NOT TableViewModelRenderer, but UnEvenTableViewRenderer. This is because my TableView has set HasUnevenRows to true. This is a business rule for my application, but if you aren’t, then you can use the default TableViewModelRenderer.

The important piece is the override of GetViewForHeader. We override this in order to create a custom label that can use the properties and styles we need!

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!

Xamarin.Controls – BadgeView

Who knew it could be so difficult to just put a number on a circle in Xamarin.Forms? Here’s a freebie to make your life easier in creating your own badges.

Before we dive in, I want to note that with the Xamarin.Forms.Themes, this can be easier. Check out the docs here: https://developer.xamarin.com/guides/xamarin-forms/user-interface/themes/. Basically, they added a StyleClass for BoxView that allows you to render it as a circle (although I’ve had problems with it in the past). This example is going to be avoiding the Themes package with a custom rolled implementation.

To start, we are going to create a custom CircleView. That CircleView is going to inherit from BoxView and use a custom renderer to give us the rounded edges we want. After that, we are going to make a reusable view called BadgeView that will essentially just slap a Label on top of our new CircleView.

Start here, with your CircleView in your PCL or Shared Library:

CircleView.cs

    public partial class CircleView : BoxView
    {
        public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(CircleView), 0.0);

        public double CornerRadius
        {
            get { return (double)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public CircleView()
        {
            InitializeComponent();
        }
    }

Now let’s create our custom renderers. I want to note, that for iOS it is much simpler, and could also be done as an Effect rather than a BoxRenderer, however, in order to be consistent with the more complicated Android implementation, we are doing both as renderers.

First, and easiest – iOS:

CircleViewRenderer.cs


[assembly: ExportRenderer(typeof(CircleView), typeof(CircleViewRenderer))]
namespace YourNamespace.iOS
{
    public class CircleViewRenderer : BoxRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
                return;

            Layer.MasksToBounds = true;
            Layer.CornerRadius = (float)((CircleView)Element).CornerRadius / 2.0f;
        }

    }
}

and of course Android:

CircleViewRenderer.cs


[assembly: ExportRenderer(typeof(CircleView), typeof(CircleViewRenderer))]
namespace YouNamespace.Droid
{
    public class CircleViewRenderer : BoxRenderer
    {
        private float _cornerRadius;
        private RectF _bounds;
        private Path _path;
        protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
            {
                return;
            }
            var element = (CircleView)Element;

            _cornerRadius = TypedValue.ApplyDimension(ComplexUnitType.Dip, (float)element.CornerRadius, Context.Resources.DisplayMetrics);

        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);
            if (w != oldw && h != oldh)
            {
                _bounds = new RectF(0, 0, w, h);
            }

            _path = new Path();
            _path.Reset();
            _path.AddRoundRect(_bounds, _cornerRadius, _cornerRadius, Path.Direction.Cw);
            _path.Close();
        }

        public override void Draw(Canvas canvas)
        {
            canvas.Save();
            canvas.ClipPath(_path);
            base.Draw(canvas);
            canvas.Restore();
        }
    }
}

Cool. Now we can draw pretty circles in our Xamarin.Forms views:

<views:CircleView CornerRadius="16" WidthRequest="16" HeightRequest="16"/>

Now let’s apply that to a reusable BadgeView.

BadgeView.xaml

<Grid      xmlns="http://xamarin.com/schemas/2014/forms"      xmlns:local="clr-namespace:your_local_namespace"     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"      x:Class="your_local_namespace.BadgeView"     HeightRequest="16"     WidthRequest="16">
    <local:CircleView x:Name="BadgeCircle" HeightRequest="16" WidthRequest="16" CornerRadius="16" VerticalOptions="Center" HorizontalOptions="Center" />
    <Label x:Name="BadgeLabel" TextColor="White" VerticalOptions="Center" HorizontalOptions="Center" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" FontSize="10"/>
</Grid>

BadgeView.xaml.cs

 public partial class BadgeView : Grid
    {
        public static BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(BadgeView), "0", propertyChanged: (bindable, oldVal, newVal) =>
        {
            var view = (BadgeView)bindable;
            view.BadgeLabel.Text = (string)newVal;
        });

        public static BindableProperty BadgeColorProperty = BindableProperty.Create("BadgeColor", typeof(Color), typeof(BadgeView), Color.Blue, propertyChanged: (bindable, oldVal, newVal) =>
        {
            var view = (BadgeView)bindable;
            view.BadgeCircle.BackgroundColor = (Color)newVal;
        });

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
        public Color BadgeColor
        {
            get
            {
                return (Color)GetValue(BadgeColorProperty);
            }
            set
            {
                SetValue(BadgeColorProperty, value);
            }
        }
        public BadgeView()
        {
            InitializeComponent();
            BadgeLabel.Text = Text;
            BadgeCircle.BackgroundColor = BadgeColor;
        }
    }

This is obviously a super simple example, but you can always add any other properties you want such as handling changing sizes, corners, shapes, colors, etc.

But now we can see our final results when using our control:

 <Grid>
    <Label HorizontalTextAlignment="Center" Text="Look at me!"/>
    <views:BadgeView Text="3" BadgeColor="Green" VerticalOptions="Start" HorizontalOptions="End"/>
</Grid>

BadgeExample

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!