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!

Microsoft release new XAML Controls Gallery app to help developers implement Fluent Design

xalm-controls-gallery

Microsoft is hoping developers will be updating their UWP apps en masse to support their new Fluent Design language, and to help them along Microsoft has published an app in the store that demonstrates all the controls available.

XAML Controls Gallery” demonstrates all of the controls available in Fluent Design System and XAML. It’s the interactive companion to the Fluent Design System web site which can be seen here.

According to Microsoft, the new Microsoft Fluent Design System will deliver “intuitive, harmonious, responsive and inclusive cross-device experiences and interactions” for users. As for developers, the Microsoft Fluent Design System will allow them to deliver engaging experiences that work across a wide range of devices with input diversity.

The source code to the app is available on Github here and developers can download the app from the Store here.

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!

Microsoft Launcher review: A beautiful Android experience

Microsoft-Launcher-with-Surface

After Microsoft gives up on Windows 10 Mobile, Microsoft Launcher is the upgraded version of the Microsoft Garage project Arrow Launcher, and we covered the key changes that came with that upgrade last week. It's free and can be picked up from the Google Play Store.

After some heavy usage over the last few days, we're breaking down what works, what doesn't, and where Microsoft should take their launcher from here.

As Microsoft Launcher gains more publicity, there have been some rumblings about how it doesn't look like Windows 10 Mobile. It's important to point out that it doesn't seem to be the goal of Microsoft to make Android look exactly like Windows 10 Mobile. For example, you won't find Live Tiles anywhere in the launcher. If you're looking for as close to a facsimile of Windows 10 Mobile on Android as possible, there are other options, such as Squarehome 2.

But this isn't a bad thing. Microsoft isn't trying to turn Android into Windows 10 Mobile, they are trying to integrate Microsoft services into the Android experience while also adding some design elements that will be familiar to Windows users. And in that respect, Microsoft Launcher is phenomenal.

For example, there's also an option for a transparent theme. With all the transparent design elements coming in the Windows 10 Fall Creators Update, having a glass effect throughout all of your devices helps them feel more like siblings. Microsoft Launcher's transparency is found on every page in the launcher, including your newsfeed, calendar, people section, and more.

Microsoft-Launcher-Microsoft-Services

Arrow Launcher already had features such as Wunderlist and Outlook calendar integration. Microsoft Launcher takes that idea further by bringing "Continue on PC" to Android. This lets you start doing things on your phone and easily jump to another device. This will be familiar to anyone who has taken advantage of Project Rome. It's a nice addition to Android and will hopefully get better over time. You can take a document you're working on and push it over to your PC. It also works with links, even if you're browsing on Chrome on your phone and have Edge as the default browser on PC. It works fairly well, though it can take a couple seconds to open on your PC.

Microsoft gives up on Windows 10 Mobile

Belfiore_window10_mobile

The company's Windows 10 chief has tweeted that developing new features and hardware for the Mobile version of the OS was no longer a "focus".

Joe Belfiore added that he had also switched to Android himself.

Windows 10 Mobile tried to attract users by letting them run the same "universal apps" on both their PCs and handsets, but the concept failed to catch on.

The OS accounted for just 0.03% of the global market - based on smartphone shipments - between April and June, according to research company IDC.

The market intelligence provider said the news had been a long time coming.

"There wasn't a wide range of devices running Windows 10 Mobile, so it wasn't attractive to retailers or operators," said IDC's Francisco Jeronimo.

"And from a consumer perspective, the operating system didn't provide as good an experience as Android or iOS."

Mr Belfiore began a series of tweets on Sunday by discussing the recent launch of a test version of Microsoft's Edge web browser for Android and iOS - the latest in a series of releases of its core software for rival mobile platforms.

He then went on to respond to questions about whether there was any point sticking with Windows 10 Mobile.

He said that while Microsoft would support the "many companies" that had adopted the platform, he had switched to Android for the diversity of its apps and hardware.

"Of course we'll continue to support the platform... bug fixes, security updates, et cetera," he said.

"But building new features or hardware is not the focus."

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

Planet Xamarin

Planet Xamarin

Calendar

<<  October 2017  >>
MonTueWedThuFriSatSun
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Month List