Xamarin Forms: TranslateY (or other properties) for platform

Sometimes you need to change a value for a specific platform and this value is a Double, for example. I spent a couple of minutes to find a working code.

<Label>
    <Label.TranslationY>
        <OnPlatform x:TypeArguments="x:Double" Android="-10" />
    </Label.TranslationY>
</Label>

Happy coding!

Enable and control iOS 11 Large Titles in Xamarin.Forms

Apple introduced with iOS 11 a new UI in particular for larger titles: if you open your email, you see at the top a large title. That sit in the Navigation Bar and can be scrolled away when more space is needed. We can enable large titles with a renderer.

For this example, I only created a new project and added the following renderer in the iOS project under Renderers folder. You can easily add to all your solution the following renderer in your iOS project:

using System;
using YourProject.iOS.Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(NavigationPage), 
                          typeof(CustomNavigationRenderer))]
namespace YourProject.iOS.Renderers
{
    public class CustomNavigationRenderer : NavigationRenderer
    {
        protected override void OnElementChanged(
                                VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            // Enables iOS 11 new Large Titles system-wide
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            {
                // Enable Large Titles
                NavigationBar.PrefersLargeTitles = true;

                // Adopt same TextAttributes as for normal titles
                // beacuse Xamarin.Forms NavigationPage
                // does not have a dedicated property for this yet
                UINavigationBar.Appearance.LargeTitleTextAttributes =
                    new UIStringAttributes { 
                            ForegroundColor = 
                               (Element as NavigationPage)?.
                               BarTextColor.ToUIColor() };
            }
        }
    }
}

iOS 8 Screenshot - Large title

Happy coding!

UriBuilder in Xamarin Forms

In Xamarin Forms there is a native function called UriBuilder: it allow you to create a well-formed url. In my implementation, all parameters are in a Dictionary called parameters. Using Linq, I put in builder.Query only the parameters with a value.

UriBuilder builder = new UriBuilder(yourUrl);

Dictionary<string, string> parameters = 
                              new Dictionary<string, string>();
parameters.Add("reference", Reference);
parameters.Add("param1", Param1);

builder.Query = 
    string.Join("&", 
        parameters.Where(p => !string.IsNullOrWhiteSpace(p.Value))
                  .Select(p => string.Format("{0}={1}", 
                                      Uri.EscapeDataString(p.Key), 
                                      Uri.EscapeDataString(p.Value))));
return builder.ToString();

Happy coding!

Binding FormattedString for Xamarin Forms

Xamarin Forms doesn't have a Label with a Bindable FormattedString. For example, if you want a bindable bold word in the middle of a sentence in a Label, it's very hard to design it with common control. For that, I create my own component for that.

LabelRenderer.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace PSC.Controls
{
    public class Label : Xamarin.Forms.Label
    {
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            UpdateFormattedTextBindingContext();
        }

        protected override void OnPropertyChanged(
           [CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            if (propertyName == FormattedTextProperty.PropertyName)
                UpdateFormattedTextBindingContext();
        }

        private void UpdateFormattedTextBindingContext()
        {
            var formattedText = this.FormattedText as FormattedString;

            if (formattedText == null)
                return;

            foreach (var span in formattedText.BindableSpans)
                span.BindingContext = this.BindingContext;
        }
    }

    [ContentProperty("BindableSpans")]
    public class FormattedString : Xamarin.Forms.FormattedString
    {
        private ObservableCollection<Span> _bindableSpans = 
            new ObservableCollection<Span>();

        public IList<Span> BindableSpans { 
            get { return this._bindableSpans; } 
        }

        public FormattedString()
        {
            this._bindableSpans.CollectionChanged += OnCollectionChanged;
        }

        private void OnCollectionChanged(object sender, 
            NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (var bindableSpan in e.OldItems.Cast<Span>())
                    base.Spans.Remove(bindableSpan);
            }
            if (e.NewItems != null)
            {
                foreach (var bindableSpan in e.NewItems.Cast<Span>())
                    base.Spans.Add(bindableSpan);
            }
        }

        /// <param name="text">To be added.</param>
        /// <summary>
        /// Cast a string to a FromattedString that contains 
        /// a single span with no attribute set.
        /// </summary>
        /// <returns>To be added.</returns>
        public static implicit operator FormattedString(string text)
        {
            return new FormattedString
            {
                BindableSpans = { 
                    new Span { Text = text ?? "" } 
                }
            };
        }
        /// <param name="formatted">To be added.</param>
        /// <summary>
        /// Cast the FormattedString to a string, 
        /// stripping all the attributes.
        /// </summary>
        /// <returns>To be added.</returns>
        public static explicit operator string(FormattedString formatted)
        {
            return formatted.ToString();
        }
    }

    [ContentProperty("Text")]
    public sealed class Span : BindableObject
    {
        Xamarin.Forms.Span _innerSpan;

        public Span() : this(new Xamarin.Forms.Span()) { }

        public Span(Xamarin.Forms.Span span)
        {
            _innerSpan = span;
            // important for triggering property inheritance from parent Label
            this.BackgroundColor = this._innerSpan.BackgroundColor;
            this.FontSize = this._innerSpan.FontSize;
            this.FontAttributes = this._innerSpan.FontAttributes;
            this.FontFamily = this._innerSpan.FontFamily;
            this.ForegroundColor = this._innerSpan.ForegroundColor;
            this.Text = this._innerSpan.Text ?? "";
        }
        public static readonly BindableProperty BackgroundColorProperty =
            BindableProperty.Create(nameof(BackgroundColor), 
                typeof(Color), typeof(Span), Color.Default);

        public Color BackgroundColor
        {
            get { return (Color)GetValue(BackgroundColorProperty); }
            set { SetValue(BackgroundColorProperty, value); }
        }

        public static readonly BindableProperty FontAttributesProperty =
            BindableProperty.Create(nameof(FontAttributes), 
                typeof(FontAttributes), 
                typeof(Span), 
                FontAttributes.None);

        public FontAttributes FontAttributes
        {
            get { return (FontAttributes)GetValue(FontAttributesProperty); }
            set { SetValue(FontAttributesProperty, value); }
        }

        public static readonly BindableProperty FontFamilyProperty =
            BindableProperty.Create(nameof(FontFamily), 
                typeof(string), 
                typeof(Span), 
                string.Empty);

        public string FontFamily
        {
            get { return (string)GetValue(FontFamilyProperty); }
            set { SetValue(FontFamilyProperty, value); }
        }

        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), 
                typeof(double), 
                typeof(Span), -1.0, BindingMode.OneWay, 
                null, null, null, null, 
                bindable => Device.GetNamedSize(
                    NamedSize.Default, typeof(Label)));

        [TypeConverter(typeof(FontSizeConverter))]
        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }

        public static readonly BindableProperty 
            ForegroundColorProperty =
                BindableProperty.Create(nameof(ForegroundColor), 
                   typeof(Color), 
                   typeof(Span), 
                   Color.Default);

        public Color ForegroundColor
        {
            get { return (Color)GetValue(ForegroundColorProperty); }
            set { SetValue(ForegroundColorProperty, value); }
        }

        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(nameof(Text), 
                                    typeof(string), 
                                    typeof(Span), 
                                    string.Empty);

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

        public Xamarin.Forms.Span InnerSpan { 
            get { return _innerSpan; } 
        }

        protected override void OnPropertyChanged(
            [CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            _innerSpan.BackgroundColor = this.BackgroundColor;
            _innerSpan.FontSize = this.FontSize;
            _innerSpan.FontAttributes = this.FontAttributes;
            _innerSpan.FontFamily = this.FontFamily;
            _innerSpan.ForegroundColor = this.ForegroundColor;
            _innerSpan.Text = this.Text ?? "";
        }

        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
        }

        public static implicit operator Xamarin.Forms.Span(Span bindableSpan)
        {
            return bindableSpan.InnerSpan;
        }

        public static implicit operator Span(
            Xamarin.Forms.Span span)
        {
            return new Span(span);
        }
    }
}

In your XAML you use this control like that:

<ctl:Label
    FontSize="Small"
    TextColor="#333"
    HorizontalOptions="FillAndExpand">
    <Label.FormattedText>
        <ctl:FormattedString>
            <ctl:Span Text="{Binding YourVar1}" FontAttributes="Bold"/>
            <ctl:Span Text="{Binding YourVar2, StringFormat=' {0}'}"/>
        </ctl:FormattedString>
    </Label.FormattedText>
</ctl:Label>

Download the code from GitHub.

Happy coding!

Xamarin Forms Repeater View

A ListView is a kind of repeater but isn’t always what I want. It’s surprising something like this isn’t included in the framework but making your own is fairly simple.

namespace PSC.Controls
{
    /// <summary>
    /// Repeater view.
    /// </summary>
    public class RepeaterView : StackLayout
    {
        /// <summary>
        /// The item template property.
        /// </summary>
        public static readonly BindableProperty ItemTemplateProperty = 
                BindableProperty.Create(
                    "ItemTemplate",
                    typeof(DataTemplate),
                    typeof(RepeaterView),
                    null,
                    propertyChanged: (bindable, value, newValue) => 
                        Populate(bindable));

        /// <summary>
        /// The items source property.
        /// </summary>
        public static readonly BindableProperty ItemsSourceProperty = 
            BindableProperty.Create(
                "ItemsSource",
                typeof(IEnumerable),
                typeof(RepeaterView),
                null,
                BindingMode.OneWay,
                propertyChanged: (bindable, value, newValue) => 
                  Populate(bindable));

        /// <summary>
        /// Gets or sets the items source.
        /// </summary>
        /// <value>The items source.</value>
        public IEnumerable ItemsSource
        {
            get => (IEnumerable)this.GetValue(ItemsSourceProperty);
            set => this.SetValue(ItemsSourceProperty, value);
        }

        /// <summary>
        /// Gets or sets the item template.
        /// </summary>
        /// <value>The item template.</value>
        public DataTemplate ItemTemplate
        {
            get => (DataTemplate)this.GetValue(ItemTemplateProperty);
            set => this.SetValue(ItemTemplateProperty, value);
        }

        /// <summary>
        /// Populate the specified bindable.
        /// </summary>
        /// <returns>The populate.</returns>
        /// <param name="bindable">Bindable.</param>
        private static void Populate(BindableObject bindable)
        {
            var repeater = (RepeaterView)bindable;

            // Clean
            repeater.Children.Clear();

            // Only populate once both properties are received
            if (repeater.ItemsSource == null || 
                repeater.ItemTemplate == null)
                return;

            foreach (var viewModel in repeater.ItemsSource)
            {
                var content = repeater.ItemTemplate.CreateContent();
                if (!(content is View) && !(content is ViewCell))
                {
                    throw new Exception(
                          $"Invalid visual object {nameof(content)}");
                }

                var view = content is View ? content as View : 
                                             ((ViewCell)content).View;
                view.BindingContext = viewModel;

                repeater.Children.Add(view);
            }
        }
    }
}



The view is based on StackLayout which will take care of positioning the items.

There are a couple of bindable properties – ItemTemplate for the item layout and ItemsSource which provides the items. Note that ItemsSource hooks an action to PropertyChanging – I’ll come back to this later on.

Usage in XAML is similar to ListView, with an ItemTemplate, DataTemplate, and ViewCell.

<ui:Repeater x:Name="MainRepeater">
    <ui:Repeater.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Orientation="Horizontal">
                    <Label Text="{Binding Title}" 
                           HorizontalOptions="StartAndExpand" 
                           VerticalTextAlignment="Center"/>
                    <Button Text="Select" />
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ui:Repeater.ItemTemplate>
</ui:Repeater>

Don't forget to define ui

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:ui="clr-namespace:PSC.Controls;assembly=PSC.Controls">

Advertsing

125X125_06

TagCloud

MonthList

CommentList