Xamarin.Forms – Implementing ItemsSource for Custom Control

Here’s some code for ItemsSource:

        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
           nameof(ItemsSource),
           typeof(IEnumerable),
           typeof(MyControl),
           null,
           BindingMode.OneWay,
           propertyChanged: (bindable, oldValue, newValue) => OnItemsSourceChanged(bindable, oldValue, newValue));

        static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            void newValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                var wraplayout = bindable as WrapLayout;

                if (e.OldItems != null)
                    foreach (var item in e.OldItems)
                        wraplayout.Children.Remove(item as View);

                if (e.NewItems != null)
                    foreach (var item in e.NewItems)
                        wraplayout.Children.Add(item as View);
            }

            var oldValueINotifyCollectionChanged = oldValue as INotifyCollectionChanged;

            if (null != oldValueINotifyCollectionChanged)
            {
                oldValueINotifyCollectionChanged.CollectionChanged -= newValueINotifyCollectionChanged_CollectionChanged;
            }

            var newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;

            if (null != newValueINotifyCollectionChanged)
            {
                newValueINotifyCollectionChanged.CollectionChanged += newValueINotifyCollectionChanged_CollectionChanged;
            }
        }
        
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }
Advertisements

Xamarin.Forms Responsive Layout from Device Rotation

Intro

This is a brain dump on implementing a responsive layout based on a device being in Portrait or Landscape mode.

In summary, I used a ContentView’s ControlTemplate to host a layout that depends on a device being in Portrait or Landscape mode. In addition, I then overrode the OnSizeAllocated method on the ContentPage.

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:XForms"
             x:Class="XForms.RegistrationPage1">

    <ContentPage.Resources>
        <ResourceDictionary>
            <ControlTemplate x:Key="PortraitTemplate">
                <StackLayout>
                    <StackLayout VerticalOptions="CenterAndExpand">
                        <Entry Style="{StaticResource RegistrationEntry}" Text="{Binding Email}"    Placeholder="email"  />
                        <Entry Style="{StaticResource RegistrationEntry}" Text="{Binding Password}" Placeholder="password" IsPassword="True" Margin="0,15,0,0"  />
                    </StackLayout>

                    <Button Style="{StaticResource RegistrationButton}" Text="Next" FontAttributes="Bold" FontSize="Small" VerticalOptions="Fill" />
                </StackLayout>
            </ControlTemplate>

            <ControlTemplate x:Key="LandscapeTemplate">
                <Label Text="Landscape" />
            </ControlTemplate>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentView ControlTemplate="{StaticResource PortraitTemplate}" />

</ContentPage>

Code Behind

Override OnSizeAllocated method of a ContentPage and compare page width to height:

    public partial class RegistrationPage1 : ContentPage
	{
		public RegistrationPage1() => InitializeComponent();

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

            ControlTemplate = GetTemplate(width, height);
        }

        ControlTemplate GetTemplate(double width, double height) =>
            (height > width) ? TemplateFrom("PortraitTemplate") : TemplateFrom("LandscapeTemplate");

        ControlTemplate TemplateFrom(string templateName) =>
            Resources[templateName] as ControlTemplate;
    }

Conclusion

Brain dump completed.

F#: ViewModel (Dependency Injection)

 

Intro

In this post, I attempt to demonstrate how cool I am at writing proven code for a user’s registration form. Please note that I use the term proven and testable interchangeably.

 

The viewmodel is the root of the testable codebase for business logic.
As a result, I injected the function (aka: implementation) that handles the submit request for a registration form inside my viewmodel.

Here’s code that shows a workflow accepting the implementation of a submit request inside my viewmodel:

type ViewModel(implementation:Try.SubmitFn) as x =
...
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function
                         | Some form ->
                                form |> Command.Execute
                                     |> In.SubmitRegistration.workflow implementation
                                     |> publishEvents
                         | None -> ()

Here’s the entire viewmodel:

namespace Nikeza.Mobile.UILogic.Registration

open System.Windows.Input
open Nikeza.Mobile.UILogic
open Nikeza.Mobile.Profile.Commands.Registration
open Nikeza.Mobile.Profile.Events
open Adapter
open In

type Form = Registration.Types.Form

module Updates =
    let statusOf formValidated events = 
        events |> List.exists formValidated

type ViewModel(implementation:Try.SubmitFn) as x =

    let mutable validatedForm = None

    let eventOccurred = new Event<_>()

    let validate() =
        let isValidated = function
            | FormValidated form -> validatedForm <- Some form; true
            | _ -> false

        { Form.Email=    x.Email
          Form.Password= x.Password
          Form.Confirm=  x.Confirm
        } 
          |> ofUnvalidated
          |> Validate.Execute 
          |> In.ValidateRegistration.workflow
          |> Updates.statusOf isValidated
               
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function 
                         | Some form -> 
                                form |> Command.Execute 
                                     |> In.SubmitRegistration.workflow implementation
                                     |> publishEvents
                         | None -> ()

    let validateCommand = DelegateCommand( (fun _ -> x.IsValidated <- validate()) , fun _ -> true)

    let submitCommand =   DelegateCommand( (fun _ -> submit() |> ignore), 
                                            fun _ -> x.IsValidated <- validate(); x.IsValidated )

    let mutable email =    ""
    let mutable password = ""
    let mutable confirm =  ""
    let mutable isValidated = false

    member x.Validate = validateCommand :> ICommand
    member x.Submit =   submitCommand   :> ICommand

    [<CLIEvent>]
    member x.EventOccured = eventOccurred.Publish

    member x.Email
             with get() =      email 
             and  set(value) = email <- value

    member x.Password
             with get() =      password
             and  set(value) = password <- value

    member x.Confirm
        with get() =      confirm
        and  set(value) = confirm <- value

    member x.IsValidated
             with get() =      isValidated
             and  set(value) = isValidated <- value

Test

An automated test targeting business logic should provide its own implementation for submitting a registration request.

My test for submitting a registration form is as follows:

[<Test>]
let ``Registration submitted after being validated`` () =
    
    // Setup
    let mutable registrationSucceeded = false

    let registration = ViewModel(mockSubmit)

    registration.FillIn()
                .EventOccured
                .Add(fun event -> 
                         event |> function
                                  | RegistrationSucceeded _ -> registrationSucceeded <- true
                                  | RegistrationFailed    _ -> registrationSucceeded <- false)

    registration.Validate.Execute()

    // Test
    registration.Submit.Execute()

    // Verify
    registrationSucceeded |> should equal true

Test API

As I stated earlier, I try to be cool when I write code. Thus, to validate my coolness, I used a type extension to initialize the registration viewmodel.

Here’s my test API:

module TestAPI

open Nikeza.Mobile.UILogic.Registration
open Try

let someEmail =    "scott@abc.com"
let somePassword = "some_password"

let mockSubmit : SubmitFn =
    fun _ -> Ok { Id =        ""
                  FirstName = ""
                  LastName =  ""
                  Email =     ""
                  Bio =       ""
                  ImageUrl =  ""
                  Sources =   []
                }

type ViewModel with

    member x.FillIn () =
           x.Email    <- someEmail
           x.Password <- somePassword
           x.Confirm  <- somePassword
           x

Usage

The workflow below relies on a SubmitFn which was originally injected into my viewmodel.

The workflow that receives the implementation of this dependency is as follows:

module SubmitRegistration =
    type private SubmitWorkflow = SubmitFn -> Command -> RegistrationSubmissionEvent list

    let workflow : SubmitWorkflow =
        fun submitFn command -> command |> function
            Command.Execute form ->
                            form |> submitFn
                                 |> ResultOf.Submit.Executed
                                 |> Are.Registration.Submission.events

Wiring a ViewModel to a DDD-Workflow

Intro

I recently connected a viewmodel to a workflow that I was inspired to write as a result of reading the book: Domain Driven Design Made Functional. All of my source code can be found on GitHub/Nikeza.

ViewModel

In the viewmodel, I handle validating and submitting a form. The interesting part of this exercise is that I receive domain events as a response to requesting validation and submission.

Here’s the logic for validating and submitting a form:

type ViewModel() as x =

    let mutable validatedForm = None

    let eventOccurred = new Event<_>()

    let validate() =
        let isValidated = function
            | FormValidated form -> validatedForm <- Some form; true
            | _ -> false

        { Form.Email=    x.Email
          Form.Password= x.Password
          Form.Confirm=  x.Confirm
        } 
        |> ofUnvalidated
        |> Validate.Execute 
        |> In.ValidateRegistration.workflow
        |> Updates.statusOf isValidated
               
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function 
                         | Some form -> 
                                form |> Submit.Execute 
                                     |> In.SubmitRegistration.workflow
                                     |> publishEvents
                         | None -> ()

    let validateCommand = DelegateCommand( (fun _ -> x.IsValidated <- validate()) , fun _ -> true)

    let submitCommand =   DelegateCommand( (fun _ -> submit() |> ignore), 
                                            fun _ -> x.IsValidated <- validate(); x.IsValidated )

    let mutable email =    ""
    let mutable password = ""
    let mutable confirm =  ""
    let mutable isValidated = false

    member x.Validate = validateCommand :> ICommand
    member x.Submit =   submitCommand   :> ICommand

    [<CLIEvent>]
    member x.EventOccured = eventOccurred.Publish

The remaining viewmodel is the following:

type ViewModel() as x =

    ...
    member x.Email
             with get() =      email
             and  set(value) = email <- value

    member x.Password
             with get() =      password
             and  set(value) = password <- value

    member x.Confirm
        with get() =      confirm
        and  set(value) = confirm <- value

    member x.IsValidated
             with get() =      isValidated
             and  set(value) = isValidated <- value

Here’s some viewmodel helpers:

type UIForm = Registration.Types.Form
type DomainForm = Nikeza.Mobile.Profile.Registration.Form

module That =
    let generatesPage events =
        let eventToPage = function
        | RegistrationSucceeded _ -> () // displayPortal()
        | RegistrationFailed    _ -> () // displayError()

        events |> List.iter eventToPage

module Updates =
    let statusOf formValidated events =
        events |> List.exists formValidated

Registration Workflow

Here’s the Registration workflow:

module In

open Nikeza.Mobile.Profile.Commands
open Nikeza.Mobile.Profile.Commands.Registration
open Nikeza.Mobile.Profile.Events
open Logic

module SubmitRegistration =
    type private SubmitWorkflow = Submit -> RegistrationSubmissionEvent list

    let workflow : SubmitWorkflow =
        fun command -> command |> function
            Submit.Execute form ->
                form |> Try.submit
                     |> ResultOf.Submit.Executed
                     |> Are.Registration.Submission.events

module ValidateRegistration =
    type private ValidateWorkflow = Validate -> RegistrationValidationEvent list

    let workflow : ValidateWorkflow =
        fun command -> command |> function
            Validate.Execute form ->
                             form |> Registration.validate
                                  |> ResultOf.Validation.Executed
                                  |> Are.Registration.Validation.events

Domain Events

The domain events are structured as follows:

module Nikeza.Mobile.Profile.Events

open Nikeza.Common
open Nikeza.DataTransfer

type UnvalidatedForm = Nikeza.Mobile.Profile.Registration.UnvalidatedForm
type ValidatedForm =   Nikeza.Mobile.Profile.Registration.ValidatedForm

type RegistrationValidationEvent =
    | FormValidated         of ValidatedForm
    | FormNotValidated      of UnvalidatedForm

type RegistrationSubmissionEvent =
    | RegistrationSucceeded of Profile
    | RegistrationFailed    of ValidatedForm

Here’s how these events get emitted:

module internal Are.Registration

open Nikeza.Mobile.Profile.Commands
open Nikeza.Mobile.Profile.Events

module Submission =
    type private RegistrationSubmission = ResultOf.Submit -> RegistrationSubmissionEvent list

    let events : RegistrationSubmission =
        fun resultOf -> 
            resultOf |> function
                        ResultOf.Submit.Executed result -> 
                                                 result |> function
                                                         | Ok    profile -> [ RegistrationSucceeded profile]
                                                         | Error form    -> [ RegistrationFailed    form ]

module Validation =
    type private RegistrationValidation = ResultOf.Validation -> RegistrationValidationEvent list

    let events : RegistrationValidation =
        fun resultOf -> 
            resultOf |> function
                        ResultOf.Validation.Executed result -> 
                                                     result |> function
                                                               | Error form -> [FormNotValidated form]
                                                               | Ok    form -> [FormValidated    form]

Tests

Here’s the test that I wrote:

module Tests.Registration

open FsUnit
open NUnit.Framework
open Nikeza.Mobile.UILogic.Registration
open TestAPI
open Nikeza.Mobile.Profile.Events

[<Test>]
let ``Registration validated with email and matching passwords`` () =
    
    // Setup
    let registration = ViewModel()
    registration.Email    <- someEmail
    registration.Password <- somePassword
    registration.Confirm  <- somePassword

    // Test
    registration.Validate.Execute()

    // Verify
    registration.IsValidated |> should equal true

[<Test>]
let ``Registration submitted after being validated`` () =
    
    // Setup
    let mutable registrationSucceeded = false

    let registration = ViewModel()
    Apply.valuesTo registration

    registration.EventOccured.Add(fun event -> 
                                      event |> function
                                               | RegistrationSucceeded _ -> registrationSucceeded <- true
                                               | RegistrationFailed    _ -> registrationSucceeded <- false)
    registration.Validate.Execute()

    // Test
    registration.Submit.Execute()

    // Verify
    registrationSucceeded |> should equal true