Component with children

Any component deriving from RxComponent can render anything in its Render() overload.

Sometimes is useful a component that arranges children.

Say we want for example to create a component that arranges its children within a customizable grid, like this:

To start let's create a component that build our page:

public class PageComponent : RxComponent
{
    public override VisualNode Render()
    {
        return new RxNavigationPage()
        {
            new RxContentPage()
            {

            }
            .Title("Component With Children")
        };
    }
}

This should show a page with a title; let's now create a component for our grid (call it WrapGrid)

public class WrapGrid : RxComponent
{
    public override VisualNode Render()
    {
    }
}

Every RxComponent can access its children using Children() method (like {this.props.children} in react)

public class WrapGrid : RxComponent
{
    public override VisualNode Render()
    {
        return new RxGrid()
        {
            Children().ToArray()
        };
    }
}

Now let's add a ColumnCount property and a simple logic to arrange and wraps any children passed

public class WrapGrid : RxComponent
{
    private int _columnCount = 4;
    public WrapGrid ColumnCount(int columnCount)
    {
        _columnCount = columnCount;
        return this;
    }

    public override VisualNode Render()
    {
        int rowIndex = 0, colIndex = 0;

        int rowCount = Math.DivRem(Children().Count, _columnCount, out var divRes);
        if (divRes > 0)
            rowCount++;

        return new RxGrid(
            Enumerable.Range(1, rowCount).Select(_ => new RowDefinition() { Height = GridLength.Auto }),
            Enumerable.Range(1, _columnCount).Select(_ => new ColumnDefinition()))
        {
            Children().Select(child =>
            {
                child.GridRow(rowIndex);
                child.GridColumn(colIndex);
                
                colIndex++;
                if (colIndex == _columnCount)
                {
                    colIndex = 0;
                    rowIndex++;
                }

                return child;
            }).ToArray()
        };
    }
}

Finally we just need to create the component from the page:

public class PageState : IState
{
    public int ColumnCount { get; set; } = 1;

    public int ItemCount { get; set; } = 3;
}

public class PageComponent : RxComponent<PageState>
{
    public override VisualNode Render()
    {
        return new RxNavigationPage()
        {
            new RxContentPage()
            {
                new RxStackLayout()
                { 
                    new RxLabel($"Columns {State.ColumnCount}")
                        .FontSize(Xamarin.Forms.NamedSize.Large),
                    new RxStepper()
                        .Minimum(1)
                        .Maximum(10)
                        .Increment(1)
                        .Value(State.ColumnCount)
                        .OnValueChanged(_=> SetState(s => s.ColumnCount = (int)_.NewValue)),
                    new RxLabel($"Items {State.ItemCount}")
                        .FontSize(Xamarin.Forms.NamedSize.Large),
                    new RxStepper()
                        .Minimum(1)
                        .Maximum(20)
                        .Increment(1)
                        .Value(State.ItemCount)
                        .OnValueChanged(_=> SetState(s => s.ItemCount = (int)_.NewValue)),

                    new WrapGrid()
                    { 
                        Enumerable.Range(1, State.ItemCount)
                            .Select(_=> new RxButton($"Item {_}"))
                            .ToArray()
                    }
                    .ColumnCount(State.ColumnCount)                            
                }
                .Padding(10)
                .WithVerticalOrientation()
            }
            .Title("Component With Children")
        };
    }
}

Last updated