2

I have code like this:

<TableSection>
   <ViewCell Height="200">
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>      
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
      <Button Text="ABCDEFG"></Button>
   </ViewCell>
</TableSection>

What I would like to do is to make the buttons look like a tag cloud and have the ViewCell height expand to accommodate them.

I didn't see any examples of how I could do this and would appreciate some feedback / suggestions.

Thanks

Update 1 - Here is what I tried but it doesn't work

   <ViewCell Height="200">
      <StackLayout Orientation="Horizontal" IsClippedToBounds="false" Spacing="5">
         <Button Text="ABCDEF1" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF2" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF3" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF4" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF5" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF6" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF7" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
         <Button Text="ABCDEF8" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30">
                                </Button>
      </StackLayout>
   </ViewCell>

Unfortunately all I see is the first four buttons and then nothing after that. What I was hoping to see was buttons on the first row and then on the second row another four buttons with the view cell expanding in height to fit.

Alan2
  • 23,493
  • 79
  • 256
  • 450

3 Answers3

2

You can achieve this following the guide provided by the xamarin forms developers site at https://developer.xamarin.com/guides/xamarin-forms/user-interface/layouts/custom/

The problem you described is exactally what the sample code this guide shows. Creating a custom layout you can reuse this solution everywhere you need it.

Succinctly, what you have to do to create your WrapLayout:

  1. Implement the methods to handle the children arrangemeant
  2. Provide bindable properties to personalize the use on each need
  3. Consume it as you wish

You may get results like this:

Sample result

Diego Rafael Souza
  • 5,241
  • 3
  • 23
  • 62
  • I tried this but it only works with buttons that are of the same width. I know the example shows them with all the same number of characters but in the actual code they width varies a lot. – Alan2 Dec 19 '17 at 09:06
2

As Diego suggested , we can custom Layout to achieve the expect style.

Create new subclass which derives from Layout<View> , we just need to

  1. Override OnMeasure to return the size of this layout .

  2. Override LayoutChildren to determine positions and sizes of the children.

  3. Create Bindable Properties to personalize the use on each need.

Custom Layout:

public class WrapLayout : Layout<View>
{
    public static readonly BindableProperty SpacingProperty =
        BindableProperty.Create
        (
            "Spacing",
            typeof(double),
            typeof(WrapLayout),
            10.0,
            propertyChanged: (bindable, oldvalue, newvalue) => ((WrapLayout)bindable).OnSizeChanged()
        );

    public double Spacing
    {
        get { return (double)GetValue(SpacingProperty); }
        set { SetValue(SpacingProperty, value); }
    }

    private void OnSizeChanged()
    {
        this.ForceLayout();
    }

    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        if (WidthRequest > 0)
            widthConstraint = Math.Min(widthConstraint, WidthRequest);
        if (HeightRequest > 0)
            heightConstraint = Math.Min(heightConstraint, HeightRequest);

        double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
        double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);

        return DoHorizontalMeasure(internalWidth, internalHeight);
    }

    private SizeRequest DoHorizontalMeasure(double widthConstraint, double heightConstraint)
    {
        int rowCount = 1;

        double width = 0;
        double height = 0;
        double minWidth = 0;
        double minHeight = 0;
        double widthUsed = 0;

        foreach (var item in Children)
        {
            var size = item.Measure(widthConstraint, heightConstraint);

            height = Math.Max(height, size.Request.Height);

            var newWidth = width + size.Request.Width + Spacing;
            if (newWidth > widthConstraint)
            {
                rowCount++;
                widthUsed = Math.Max(width, widthUsed);
                width = size.Request.Width;
            }
            else
                width = newWidth;

            minHeight = Math.Max(minHeight, size.Minimum.Height);
            minWidth = Math.Max(minWidth, size.Minimum.Width);
        }

        if (rowCount > 1)
        {
            width = Math.Max(width, widthUsed);
            height = (height + Spacing) * rowCount - Spacing; // via MitchMilam 
        }

        return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
    }

    protected override void LayoutChildren(double x, double y, double width, double height)
    {
        double rowHeight = 0;
        double yPos = y, xPos = x;

        foreach (var child in Children.Where(c => c.IsVisible))
        {
            var request = child.Measure(width, height);

            double childWidth = request.Request.Width;
            double childHeight = request.Request.Height;
            rowHeight = Math.Max(rowHeight, childHeight);

            if (xPos + childWidth > width)
            {
                xPos = x;
                yPos += rowHeight + Spacing;
                rowHeight = 0;
            }

            var region = new Rectangle(xPos, yPos, childWidth, childHeight);
            LayoutChildIntoBoundingRegion(child, region);
            xPos += region.Width + Spacing;
        }
    }
}

In Xmal

<local:WrapLayout Spacing="5">
    <Button Text="111111111111111" BackgroundColor="Red"/>
    <Button Text="222" BackgroundColor="Green"/>
    <Button Text="33333333333333333333333333" BackgroundColor="Gray"/>
    <Button Text="444444" BackgroundColor="Blue"/>
    <Button Text="5" BackgroundColor="Orange"/>
    <Button Text="6666666666666666" BackgroundColor="Aqua"/>
    <Button Text="77777777" BackgroundColor="Yellow"/>
    <Button Text="888" BackgroundColor="Pink"/>
    <Button Text="9 9 9 9" BackgroundColor="Purple"/>
    <Button Text="10" BackgroundColor="Brown"/>
</local:WrapLayout>

Test

enter image description here

ColeX
  • 14,062
  • 5
  • 43
  • 240
1

why not use a StackLayout ?

<StackLayout Orientation="Horizontal" IsClippedToBounds="false" Spacing="5"  >
     <Button Text="ABCDEFG" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90" HeightRequest="30" ></Button> 
     <Button Text="ABCDEFG" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90"  HeightRequest="30" ></Button> 
     <Button Text="ABCDEFG" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90"  HeightRequest="30" ></Button> 
     <Button Text="ABCDEFG" TextColor="Blue" BackgroundColor="Transparent" WidthRequest="90"  HeightRequest="30" ></Button>  
</StackLayout>
LeRoy
  • 4,189
  • 2
  • 35
  • 46