OnIdiom and OnPlatform

When developing a Xamarin Forms cross platform application, we’ll have a shared library where (hopefully) the bulk of our code will go, be it C# or XAML.

However we may still need to make platform specific changes to our XAML, whether it’s images, margins, padding etc. Along with the possible differences per platform we also have the added complexity of the idiom, i.e. desktop, phone or tablet (for example) which might require differences, for example maybe we display more controls on a tablet compared to the phone.

OnPlatform

We’re still likely to need to handle different platforms within this shared code, for example in Xamarin Forms TabbedPage we used OnPlatform like this

<ContentPage.Icon>
   <OnPlatform x:TypeArguments="FileImageSource">
      <On Platform="iOS" Value="history.png"/>
   </OnPlatform>
</ContentPage.Icon>

The above simply declares that on iOS the ContentPage Icon is used and is located in the file history.png.

We can handle more than just images, obviously we might look to handle different margins, fonts etc. depending upon the platform being used.

We can declare values for multiple platforms using comma separated values in the Platform attribute, for example

<OnPlatform x:TypeArguments="FileImageSource">
   <On Platform="iOS, Android" Value="history.png"/>
   <On Platform="UWP" Value="uwphistory.png"/>
</OnPlatform>

OnPlatform’s Platform attribute currently supports iOS, Android, UWP, macOS, GTK, Tizen and WPF.

Ofcourse we can simply use Device.RuntimePatform in code-behind to find out what platform the application is running on if preferred.

OnIdiom

Working in much the same was as OnPlatform. OnIdiom is used for handling different XAML or code based upon the current device is classed as a Unsupported, Phone, Tablet, Desktop, TV or Watch.

In XAML we might change the StackLayout orientation, for example

<StackLayout.Orientation>
   <OnIdiom x:TypeArguments="StackOrientation">
      <OnIdiom.Phone>Vertical</OnIdiom.Phone>
      <OnIdiom.Tablet>Horizontal</OnIdiom.Tablet>
   </OnIdiom>
</StackLayout.Orientation>

We can use the Idiom within code-behind by checking the state of Device.Idiom.

Handling orientation in Xamarin Forms

Many years ago I wrote some Windows CE/Pocket PC code. One of the problems was handling different orientation in the same UI, so for example when the device is switched from Portrait to Landscape – in some cases we can use the same layout in both orientations other times and probably in more cases, this isn’t so simple.

Xamarin Forms does not raise orientation events or the likes, instead we will need to override a Pages OnSizeAllocated method, for example

protected override void OnSizeAllocated(double width, double height)
{
   base.OnSizeAllocated(width, height);

   if (width < height)
   {
       // portrait orientation
   }
   else if (height < width)
   {
       // landscape orientation
   }
   else
   {
      // square layout
   }
}

Note: There is also a Device Orientation plugin/nuget package for Xamarin Forms which raises events when orientation changes.

Now we can handle changes to our layout in one of few ways.

We might be to reparent controls, changing layout orientation and/or changes to rows/columns and so on, all in code. This is fairly efficient in terms of reusing the existing controls but is obviously less useful from a design perspective.

We might be able to create the layout in such a way that we can just change orientation of StackLayout’s etc. Hence changing a few properties to reflect the new orientation. This might be more difficult to setup successfully on complex pages.

An alternative method, which is a little wasteful in turns of control creation, but gives us a good design story, is two design two ContentView’s, one for Portrait and one for Landscape. Obviously we’ll need to bind the controls to the same view model properties etc. However, with this solution we can more rapidly get our UI up and running.

<controls:OrientationView>
   <controls:OrientationView.Landscape>
      <views:LandscapeView />
   </controls:OrientationView.Landscape>
   <controls:OrientationView.Portrait>
      <views:PortraitView />
   </controls:OrientationView.Portrait>
</controls:OrientationView>

The OrientationView, might look like this

public class OrientationView : ContentView
{
   public View Landscape { get; set; }
   public View Portrait { get; set; }
   public View Square { get; set; }

   private Page _parentPage;

   protected override void OnParentSet()
   {
      base.OnParentSet();

      _parentPage = this.GetParentPage();
      if (_parentPage != null)
      {
         _parentPage.SizeChanged += PageOnSizeChanged;
      }
   }

   private void PageOnSizeChanged(object sender, EventArgs eventArgs)
   {
      if (_parentPage.Width < _parentPage.Height)
      {
         Content = Portrait ?? Landscape ?? Square;
      }
      else if (_parentPage.Height < _parentPage.Width)
      {
         Content = Landscape ?? Portrait ?? Square;
      }
      else
      {
         Content = Square ?? Portrait ?? Landscape;
      }
   }
}

Here’s the GetParentPage extension method

public static class ViewExtensions
{
   public static Page GetParentPage(this VisualElement element)
   {
      if (element != null)
      {
         var parent = element.Parent;
         while (parent != null)
         {
            if (parent is Page parentPage)
            {
               return parentPage;
            }
            parent = parent.Parent;
         }
      }
      return null;
   }
}

This allows us to basically design totally different UI’s for portrait and landscape, maybe adding extra controls in landscape and removing in portrait. The obvious downside is the duplication of controls.

Self hosting Tomcat in a Java Web application

Following on from my previous post where I created a web application in Java, let’s now look at hosting the WAR within an embedded Tomcat instance.

Adding dependencies to include embedded Tomcat

Add the following to your pom.xml (after the description tag)

<properties>
   <tomcat.version>9.0.0.M6</tomcat.version>
</properties>

Note: There’s a newer version of a couple of the dependencies, but this version exists for all three of the dependencies we’re about to add.

Now, add following dependencies

<dependencies>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
   <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-logging-juli</artifactId>
      <version>${tomcat.version}</version>
   </dependency>
</dependencies>

Time to run mvn install if not auto-importing.

Time to create the entry point/application

Let’s add a new package to the src folder, com.putridparrot now add a Java file, mine’s HostApp.java, here’s the code

package com.putridparrot;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.ServletException;
import java.io.File;

public class HostApp {
    public static void main(String[] args) throws ServletException, LifecycleException {

        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("temp");
        tomcat.setPort(8080);

        String contextPath = "";
        String webappDir = new File("web").getAbsolutePath();

        tomcat.addWebapp(contextPath, webappDir);

        tomcat.start();
        tomcat.getServer().await();
    }
}

In the above we create an instance of Tomcat and then set up the port and the context for the web app, including the path to the web folder where our index.jsp is hosted in our WAR file.

We then start the server and then wait until the application is closed.

By the way, it’s also worth adding logging to the pom.xml. The embedded Tomcat server using “standard” Java based logging, so we can add the following to the pom.xml dependencies

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.15</version>
</dependency>

Create a run configuration

Now select Edit Configuration and add a configuration that runs main from HostApp.

Adding gzip/compression support, before we start the server. Hence select Application and set Main class to com.putridparrot.HostApp.

Testing

Run the newly added application configuration, don’t worry about the exceptions. You should see a line similar to

INFO: Starting ProtocolHandler [http-nio-8080]

At this point the server is running, so navigate your browser to http://localhost:8080 and check that the index.jsp page is displayed.

Configuring the embedded Tomcat for gzip/compression support

I’ve been looking into compression with gzip on some web code and hence wanted to configure this embedded Tomcat server to handle compression if/when requested via Accept-Type: gzip etc.

So add the following to the main method (before tomcat.start())

Connector c = tomcat.getConnector();
c.setProperty("compression", "on");
c.setProperty("compressionMinSize", "1024");
c.setProperty("noCompressionUserAgents", "gozilla, traviata");
c.setProperty("compressableMimeType", "text/html,text/xml,text/css,application/json,application/javascript");
tomcat.setConnector(c);

You’ll also need the import import org.apache.catalina.connector.Connector;.

Testing gzip/compression

To test whether Tomcat is using compression is best done with something like curl. I say this because, whilst you can use a browser (such as Chrome’s) debug tools and see a response with Content-Type: gzip, I really wanted to see the raw compressed data to feel I really was getting compressed responses, the browser automatically decompressed the responses for me.

Go the main method and just change “on” to “force”

i.e.

c.setProperty("compression", "force");

This just forces compression to be on all the time.

Now to test our server is set up correctly. Thankfully Windows 10 seems to have curl available from a command prompt, so this works in Linux or Windows.

Run the following

curl -H "Accept-Encoding: gzip,deflate" -I "http://localhost:8080/index.jsp"

This command adds the header Accept-Encoding and then outputs the header (-I) results from accessing the URL. This should show a Content-Encoding: gzip if everything was set up correctly.

To confirm everything is as expected, we can download the content from the URL and save it (in this case saved to index.jsp.gz) and then use gzip -d to decompress the file if we wish.

curl -H "Accept-Encoding: gzip,deflate" "http://localhost:8080/index.jsp" -o index.jsp.gz

This will create index.jsp.gz which should be compressed so we can use

gzip -d index.jsp.gz

to decompress it and we should see the expected web page.

Creating a Java Web Application with IntelliJ

Creating our project

  • Choose File | New | Project
  • Select Java Enterprise
  • Then tick the Web Application

If you have an application server setup, select or add it using New, I’m going to ultimately add an embedded Tomcat container, so leaving this blank.

Finally give the project a name, i.e. MyWebApp.

Adding a Maven pom.xml

I want to use Maven to import packages, so add a pom.xml to the project root add then supply the bare bones (as follows)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>MyWebApp</groupId>
    <artifactId>MyWebApp</artifactId>
    <packaging>war</packaging>
    <name />
    <version>MyWebApp.0.0.1-SNAPSHOT</version>
    <description />

</project>

In IntelliJ, select the pom.xml, right mouse click and select Add as Maven project.

Next, we want to tell Maven how to compile and generate our war, so add the following after the description tag in the pom.xml

<build>
   <sourceDirectory>${basedir}/src</sourceDirectory>
   <outputDirectory>${basedir}/web/WEB-INF/classes</outputDirectory>
   <resources>
      <resource>
         <directory>${basedir}/src</directory>
         <excludes>
            <exclude>**/*.java</exclude>
         </excludes>
      </resource>
   </resources>
   <plugins>
      <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <configuration>
            <webappDirectory>${basedir}/web</webappDirectory>
            <warSourceDirectory>${basedir}/web</warSourceDirectory>
         </configuration>
      </plugin>
      <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
            <source>1.8</source>
            <target>1.8</target>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
            <source>8</source>
            <target>8</target>
         </configuration>
      </plugin>
   </plugins>
</build>

Creating a run configuration

Whilst my intention is to add a Tomcat embedded server, we can create a new run configuration at this point to test everything worked.

Select the Run, Edit Configuration option from the toolbar or Run | Edit Configuration and click + and add a Tomcat Server | Local.

Mine’s set with the URL http://localhost:8080/. Give the configuration a name and don’t forget to select the Deployment tab, press the + and then click Aritifact… I selected MyWebApp:war.

Press OK and OK again to finish the configuration and now you can run Tomcat locally and deploy the war.

Don’t forget to execute mvn install to build you war file.

Object mapping with TinyMapper

I’ve written a few posts on AutoMapper in the past.

AutoMapper and the subject of this post, TinyMapper, are object mappers which basically means translating/mapping one object to another object. That is to say you have (for example) a DTO (data transfer object) used to transfer data via REST or SOAP services. This object is in a format best suited to the transfer process, or is simply a format that doesn’t match with how your application domain models might use the data. This is not the only scenario for using object mapping, our objects might match, but types differ in some subtle (or not so subtle) ways or we want to simple using an object mapper to clone/copy objects that are of the same type.

Let’s look at a simple example

Let’s take the simplest of examples whereby we have two objects which look almost exactly alike, differing only in one of the types used

public class PersonDto
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string DateOfBirth { get; set; }
}

public class Person
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public DateTime DateOfBirth { get; set; }
}

As you can see, the property names are the same, but the DateOfBirth string needs to map to a System.DateTime.

Let’s use TinyMapper to map our objects

AutoMapper is an excellent tool for object mapping, but sometimes it might feel like using a “sledgehammer to crack a nut”. If you are not using it’s full set of capabilities or more importantly find that it’s a little slow for your needs, TinyMapper is an option.

TinyMapper purports to be quite a bit faster than AutoMapper, but obviously at the expense of all the AutoMapper capabilities. If you don’t need everything AutoMapper offers then TinyMapper makes a lot of sense.

Add the nuget package TinyMapper to your project and here’s some code

var dto = new PersonDto
{
   FirstName = "Scooby",
   LastName = "Doo",
   DateOfBirth = "13 September 1969"
};

TinyMapper.Bind<Person, PersonDto>();

var person = TinyMapper.Map<Person>(dto);

Note: Scooby do was no born on 13th September 1969, this was when the show debuted on CBS.

The line TinyMapper.Bind<Person, PersonDto>(); sets up TinyMapper so that it knows it’s meant to map these two types together when required to using the Map method. The first generic argument is the source and the second the target type. However it really just means which two types do you want to bind together, so the order seems unimportant.

The final line then tells TinyMapper to Map the PersonDto to a new instance of a Person.

As you might expect, because the property names are the same, this is all we need for the mapping to succeed, the DateOfBirth type difference doesn’t matter as these are convertible from one type to the other. The names, however do matter.

What if my property names don’t match

If your property names differ, then we simply need to tell TinyMapper what should map to what. Let’s change our Dto DateOfBirth property to DOB. If you run the previous code you’ll get (as you probably expect) a null or default value for missing mappings, so in this case the Person DateOfBirth property will equal DateTime.MinValue.

If we change our Bind method to the following

TinyMapper.Bind<PersonDto, Person>(cfg =>
{
   cfg.Bind(o => o.DOB, o => o.DateOfBirth);
});

All other properties remain mapped, based upon the property names, but now DOB is mapped to DateOfBirth.

Ignoring properties

Sometimes we want to not map a property, i.e. ignoring it. This can be handled via the configuration of the Bind method like this

TinyMapper.Bind<PersonDto, Person>(cfg =>
{
   cfg.Ignore(o => o.LastName);
}

Summary

TinyMapper is very simple to use and might has minimal configuration options, but it’s also very fast, so depending upon your needs, TinyMapper might suit better as the object mapper
for your project.

Starting out with web components

Introduction

Web components are analogous to self-contained controls or components as you’ll have seen in Windows Forms, WPF etc. They allow developers to package up style, scripts and HTML into a single file which also is used to create a custom element, i.e. not part of the standard HTML.

Sadly, still not yet available in all browsers (although I believe Polyfills exist for the main ones). Hence, for this post you’ll either need a polyfill or the latest version of Google Chrome (which I’m using to test this with).

What does it look like using web components?

To start with we create an HTML file for our web component, we then define the template for the component – this is basically the combination of style and the control’s HTML. Finally we write scripts to effect the shadow DOM by interacting with our web component.

In this post we’re going to create a simple little web component which will flash it’s text. Before we look at the component code let’s see the component in use within the following index.html file

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Web Component Test</title>
    <link rel="import" href="flashtext.html">
</head>
<body>
   <flash-text data-text="Hello World"></flash-text>
</body>
</html>

The key points here are that we include the web component using the line

<link rel="import" href="flashtext.html">

and then we use the custom element flash-text with it’s custom attributes and we use the component like this

<flash-text data-text="Hello World"></flash-text>

It’s important to note the closing element, a self closing element is not correctly handled (at least at this time using Google Chrome) and whilst the first usage of a web component might be displayed, if we had several flashing text components in the index.html it’s possible only one would be displayed and no obvious error or reason for the other components not displaying.

Creating our web component

We already decided our component will be stored in flashtext.html (as that’s what we linked to in the previous section).

To start off with, create the following

<template id="flashTextComponent">
</template>
<script>
</script>

We’ve created a new template with the id flashTextComponent. This id will be used within our script, it’s not what the pages using the component uses. To create a new custom element by adding the following to the script

var flashText = document.registerElement('flash-text', {
});

But we’re getting a little ahead of ourselves. Let’s instead create some styling and the HTML for our component. Within the template element, place the following

<style>
   .flashText {
      float: left;
      width: 152px;
      background-color: red;
      margin-bottom: 20px;
   }

   .flashText > .text {
      color: #fff;
      font-size: 15px;
      width: 150px;
      text-align: center;
   }

</style>
<div class="flashText">
   <div class="text"></div>
</div>

The style section simply defines the CSS for both the flashText div and the text div within it. The div elements create our layout template. Obviously if you created something like a menu component, the HTML for this would go here with custom attributes which we’ll define next, mapping to the template HTML.

Next up we need to create the code and custom attributes to map the data to our template. Before we do this let’s make sure the browser supports web components by writing

var template = document.createElement('template');
if('content' in template) {
   // supports web components
}
else {
   // does not support web components
}

If no content exists on the template, Google Chrome will report the error in the dev tools stating content is null (or similar wording).

Within the // supports web components section place the following

var ownerDocument = document.currentScript.ownerDocument;
var component = ownerDocument.querySelector('#flashTextComponent');

var templatePrototype = Object.create(HTMLElement.prototype);

templatePrototype.createdCallback = function () {
   var root = this.createShadowRoot();
   root.appendChild(document.importNode(component.content, true));

   var name = root.querySelector('.text');
   name.textContent = this.getAttribute('data-text');

   setInterval(function(){
      name.style.visibility = (name.style.visibility == 'hidden' ? '' : 'hidden');
   }, 1000);
};

var flashText = document.registerElement('flash-text', {
    prototype: templatePrototype
});

Let’s look at what we’ve done here. First we get at the ownerDocument and then locate our template via it’s id flashTextComponent. Now were going to create an HTMLElement prototype which will (in essence) replace our usage of the web component. When the HTMLElement is created we interact with the shadow DOM placing our component HTML into it and then interacting with parts of the template HTML, i.e. in this case placing data from the data-text custom attribute, into the text content of the div text.

As we want this text to flash we implement the script for this and attached to the visibility style of the text.

Finally, as mentioned previously, we register our custom element and “map” it to the previously created prototype.

Using in ASP.NET

ASP.NET can handle static pages easily enough, we just need to add the following to the RouteConfig

routes.IgnoreRoute("{filename}.html");

Now, inside _Layout.cshtml put in the head section

and within the Index.cshtml (or wherever you want it) place your custom elements, i.e.

<flash-text data-text="Hello World"></flash-text>

References

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
https://developers.google.com/web/fundamentals/web-components/customelements
https://www.webcomponents.org/
https://stackoverflow.com/questions/45418556/whats-the-reason-behind-not-allowing-self-closing-tag-in-custom-element-in-spec

Code for this post

https://github.com/putridparrot/blog-projects/tree/master/webcomponent

fuslogvw, how could I forget you?

This is one of those reminder’s to myself…

Don’t forget that you can use fuslogvw to find problems loading assemblies.

Why do I need reminding?

I had an issue whereby a release build of an application I was working on had been configured for live/prod for the first time and somebody went to test the application which simply failed at start-up – just displaying a Windows dialog asking whether I wanted to close or debug the application.

Ofcourse, the application worked perfectly on my machine and oddly the non-prod versions also worked fine on the other user’s machine. However the live/prod release had one change to the previous build. A new feature had been removed which wasn’t ready to go live and unbeknown to me, the removal of it’s project caused the build to deploy older versions of a couple of DLL’s as part of the new live/prod build.

On my machine this wasn’t an issue as .NET located the newer versions of the DLL’s, on the other user’s machines these could not be located.

This is fine, it was all part of pre-release testing cycle but a little confusing as all the non-prod configurations worked fine on other user’s machines.

When do we get to fuslogvw?

I haven’t had the need to use fuslogvw for ages, but really should probably use it a lot more. To be honest, it makes sense to have it on all the time to catch such potential issues.

What fuslogvw can do is list any failures during start-up of a .NET application. Running fuxlogvw.exe from the Windows SDK folder (for example C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.1 Tools) will result in the fuslog UI being displayed. As you might have guessed from the name the fus-log-vw is a viewer for the fusion log files.

Note: You must run fuslogvw as Admin if you want to change the settings, i.e. set the path of the log files, change the level of loggin, i.e. I just wanted to log all the bind failures, etc.

Leave fuslogvw open and then run the .NET application you’re wanting to take a look at. Now you’ll need to press the Refresh button on fuslogvw to see the logs after the application has started and depending on what you are logging you may see either a list of assemblies that were loaded (for example when just logging everything) or see failures etc.

We can now see what assembly and what version of that assembly the application (we’re monitoring) tried to load and we can inspect the log itself for more information on the failure (via the log file or the fuslogvw).

It’s as simple as that.

Triangular numbers

Introduction

I was listening to a podcast on Gauss where they talked about a story that, when as a youngster Gauss (and his classmates) were asked to calculate the sum of 1 to 100, Gauss came back with the following solution.

If we take the first and last number and add them, we get 101, if we take the 2nd and 2nd last number (2 + 99) we also get the result 101, hence if we do continue this process for half of the numbers we’ll get the result of the sum of 1 to 100

Please note, I’m not attributing the discovery of this formula to Gauss, so much as it just inspired me to look into this a little further.

So he we have [1, 100] and what we want to do is 1 + 2 + 3 + 4 … + 100, using Gauss’s observation, we can actually calculate this as (1 + n) * n / 2, i.e. the last number added to the first gives us 101 then multiply by 50 (i.e. 100/2) which gives us the result 5050.

The formula for this summation process is usually given as

n(n + 1)/2

which is obviously the same as we’ve listed, just rearranged.

Triangular numbers

What n(n + 1)/2 gives us, is a list of, what’s known as, triangular numbers.

If we think about having marbles and with those, start from 1 marble creating equilateral triangles, we will need three marbles to create the next equilateral triangle, then 6 marbles then 10 and so on, this pattern can be calculated by 1, 1 + 2, 1 + 2 + 3, 1 + 2 + 3 + 4… and hence is the same as n(n + 1)/2.

Is x a triangular number

To determine if a number is triangular we need to rearrange our equation as follows

n(n + 1) / 2 = x
(n^2 + n) / 2 = x
n^2 + n = 2x

This can be rewritten as

n = (sqrt(8x + 1) – 1)/2

Now if n is a perfect square (i.e. no decimal points) then the number is triangular. We can use the following in code

var n = (Math.Sqrt(8 * x + 1) - 1) / 2;
return Math.Floor(n) == n;

to test if the result is a perfect square.

A perfect square can be seen to be a squared number, i.e. 0^2, 1^2, 2^2 etc. are squared integers, hence if we square root a value we expect a non decimal number for it to be a perfect square.

References

http://mathforum.org/library/drmath/view/57162.html
http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/runsums/triNbProof.html

Creating a Custom Layout for Xamarin Forms

First off, there’s an excellent post on this subject at Creating a Custom Layout. My intention with this post is to just go through my experience creating a layout to organise controls into a grid of squares, i.e. the intention was to layout buttons in a 3×3 matrix for a simple TicTacToe game board.

Getting Started

Your class needs to be derived from Layout or Layout and override the OnMeasure method and the LayoutChildren method.

My class will be named Squared grid and it will require the number of rows and columns that we want to display our View in, so for example here’s the XAML I want to use (excluding namespace and view bindings)

<SquareGrid Rows="3" Columns="3">
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
   <Button />
</SquareGrid>

So my expectation would be that we will have three rows and three columns of equal width/height and display buttons in each “cell” of the grid.

Note: I’m not going to handle situations where the number of controls doesn’t match the expected rows/columns etc.

Here’s the code (excluding OnMeasure and LayoutChildren for now)

public class SquareGrid : Layout<View>
{
   public static readonly BindableProperty RowsProperty =
      BindableProperty.Create("Rows",
         typeof(int),
         typeof(SquareGrid),
         0,
         propertyChanged: (bindable, oldValue, newValue) =>
            ((SquareGrid)bindable).NativeSizeChanged(),
         validateValue: Validate);

   public static readonly BindableProperty ColumnsProperty = 
      BindableProperty.Create("Columns", 
         typeof(int), 
         typeof(SquareGrid),
         0,
         propertyChanged: (bindable, oldValue, newValue) => 
            ((SquareGrid)bindable).NativeSizeChanged(),
            validateValue: Validate);

   public int Rows
   {
      get => (int)GetValue(RowsProperty);
      set => SetValue(RowsProperty, value);
   }

   public int Columns
   {
      get => (int)GetValue(ColumnsProperty);
      set => SetValue(ColumnsProperty, value);
   }

   private static bool Validate(BindableObject bindable, object value)
   {
      return (int)value >= 0;
   }
}

If you’re used to WPF then, whilst the name BindableProperty don’t exist in WPF (they’re DependencyProperty instead) you’ll probably understand what’s going on, for others reading this – basically we’ve created two properties that are available in XAML, Rows and Columns. We’ve created BindableProperty static’s to handle the storage, binding etc. to these properties. The default values for both Rows and Columns is set to 0 and in both cases, if there’s a change to the property then we call the NativeSizeChanged to alert the layout of changes and also ensure, using validation methods, that the Validate method to check the supplied Rows and Columns properties are within certain bounds (in this case that they’re greater than or equal to 0).

Children

The controls/views (from the example above, the Button objects) become the Children of the layout. In some (maybe most cases) we will want to ask the children for their measurements, i.e. their minimum size requirements and hence would use code like this

foreach (var child in Children)
{
   var childSizeRequest = child.Measure(widthConstraint, heightConstraint);
   // handle any childSizeRequest
}

In our SquareLayout we’re actually going to ignore what the control/view requires and instead force it into the available space, but this gives you an example of how you might handle measurements from the children during the next section, the OnMeasure method.

OnMeasure

Here’s the method signature

protected override SizeRequest OnMeasure(
   double widthConstraint, double heightConstraint)
{
}

The OnMeasure method may be called, depending upon where our SquareGrid is placed and depending upon constraints of any outer layout. For example, if the SquareGrid is within a Grid.Row and that Row height is “*” then OnMeasure is not called. OnMeasure is called when the outer layout is asking “how much space do you require?”. In the case of “*”, we can think of it more like “this is how much space you’ve got”.

In cases where OnMeasure is called, the widthConstraint or heightConstaint might be set to infinity. For example, if the SquareGrid is within a StackLayout, the StackLayout, in portrait orientation, will have not constrain height, hence heightConstraint will by set to infinity. Likewise with a landscape orientation the widthConstraint will be set to infinity. Therefore, when you are calculating the SizeRequest to return from OnMeasure, you will need to handle infinity situations.

This SquareLayout will ignore the child controls measurement requirements and instead will take all the available width or height to create a square of layout space. Hence in a scenario where this layout in within a GridLayout with “Auto” sizing, the SquareGrid will just say it requires an equal width and height based upon the minimum of the two.

Here’s the code

protected override SizeRequest OnMeasure(
   double widthConstraint, 
   double heightConstraint)
{
   var w = double.IsInfinity(widthConstraint) ? 
      double.MaxValue : widthConstraint;
   var h = double.IsInfinity(heightConstraint) ? 
      double.MaxValue : heightConstraint;

   var square = Math.Min(w, h);
   return new SizeRequest(new Size(square, square));
}

LayoutChildren

So OnMeasure is called when the parent wants to ask how much space do you require, and the LayoutChildren (as the name suggests) is when the Layout control is asked to layout it’s children given the x, y, width and height as the bounding rectangle where it should layout it’s children. Here’s a simple example of the code I’m using

protected override void LayoutChildren(
   double x, double y, 
   double width, double height)
{
   var square = Math.Min(width / Columns, height / Rows);

   var startX = x + (width - square * Columns) / 2;
   var startY = y;

   var rect = new Rectangle(startX, startY, square, square);
   var c = 0;
   foreach (var child in Children)
   {
      LayoutChildIntoBoundingRegion(child, rect);

      if (child.IsVisible)
      {
         rect.X += square;
         if (++c >= Columns)
         {
            rect.Y += rect.Height;
            rect.X = startX;
            c = 0;
         }
      }
   }
}

Notice we use the LayoutChildIntoBoundingRegion which ultimately calls the child.Layout but applies margins etc. for us.