Center a grid without spreading it out in Xamarin forms

Viewed 86

I'm making a simple memory game, but for some reason I can't get the cards to not spread out like in the image. I put the cards in a grid that I put in the middle of a 3-column grid like this:

xaml:

<Grid x:Name="mainGrid">
        <Image x:Name="bgImage" Aspect="AspectFill"/>
        <Grid x:Name="innerGrid" >
            <Grid.RowDefinitions>
                 <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
        </Grid>
    </Grid>

cs:

            Grid gr = new Grid
            {
                ColumnSpacing = 0,
                RowSpacing = 0,
            };
            for (var i = 0; i < numRows; i++)
            {
                gr.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
            }
            for (var i = 0; i < numCols; i++)
            {
                gr.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            }
            innerGrid.Children.Add(gr, 1,0);
            var rowCount = 0;
            var colCount = 0;

            for (var i = 0; i < bricks.Count; i++)
            {
                Image img = new Image
                {
                    Source = Globals.mediaUrl + bricks[i].img,
                    ClassId = bricks[i].img,
                    Margin = new Thickness(0, 0, 0, 0)
                };
                gr.Children.Add(img,colCount,rowCount);
                colCount++;
                if (colCount > numCols - 1)
                {
                    colCount = 0;
                    rowCount++;
                }
            }

But for some reason I don't understand the cards spread out anyway like in this picture:

image of problem

Can someone please help me with this

3 Answers

It's just a case of setting the "Aspect" property of your image to "Aspect.AspectFill".

I've taken your .xaml.cs code and added an example using a BowView

Grid gr = new Grid
{
    ColumnSpacing = 0,
    RowSpacing = 0,
};

var numRows = 3;
var numCols = 4;

for (var i = 0; i < numRows; i++)
{
    gr.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
}
for (var i = 0; i < numCols; i++)
{
    gr.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
}

innerGrid.Children.Add(gr, 1,0);
var rowCount = 0;
var colCount = 0;

var randomColors = new Color[]
{
    Color.Aqua,
    Color.Aquamarine,
    Color.Azure, 
    Color.Beige, 
    Color.Bisque, 
    Color.Black,
    Color.Blue, 
    Color.Brown, 
    Color.Chartreuse, 
    Color.Chocolate, 
    Color.Coral, 
    Color.Crimson, 
};

for (var i = 0; i < 12; i++)
{
    BoxView img = new BoxView()
    {
        BackgroundColor = randomColors[new Random().Next(0, randomColors.Length - 1)]
    };

    // Image img = new Image
    // {
    //     Source = ImageSource.FromFile("xamarin.png"),
    //     Aspect = Aspect.AspectFill
    // };
    
    gr.Children.Add(img,colCount,rowCount);
    colCount++;
    if (colCount > numCols - 1)
    {
        colCount = 0;
        rowCount++;
    }
}

The end result, a bunch of colourful squares with no spacing between them:

enter image description here

Swapping out to an image (https://www.google.com/search?q=xamarin&tbm=isch&ved=2ahUKEwiIs5_o1pX2AhWKuSoKHYh9Ay8Q2-cCegQIABAA&oq=xamarin&gs_lcp=CgNpbWcQAzIECAAQQzIFCAAQgAQyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgUIABCABDoLCAAQgAQQsQMQgwE6CAgAEIAEELEDOgcIABCxAxBDUJgFWNoJYIYMaABwAHgAgAE-iAHJA5IBATiYAQCgAQGqAQtnd3Mtd2l6LWltZ8ABAQ&sclient=img&ei=rRQWYsioLIrzqgGI-434Ag&bih=764&biw=1477#imgrc=1Hifvt0AhVMSlM), and setting the aspect should yield the result you want.

enter image description here

Your layout code is fine, the issue is the aspect of the image itself.

This is so strange if I do it like that with a square image I get the result as in picture, so I don't get squares like you.

Grid gr = new Grid
            {
                ColumnSpacing = 2,
                RowSpacing = 2,
            };
            for (var i = 0; i < numRows; i++)
            {
                gr.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
            }
            for (var i = 0; i < numCols; i++)
            {
                gr.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            }
            
            var rowCount = 0;
            var colCount = 0;

            for (var i = 0; i < 12; i++)
            {
                Image img = new Image
                {
                    Source = "https://pbs.twimg.com/profile_images/471641515756769282/RDXWoY7W_400x400.png",//Globals.mediaUrl + bricks[i].img,
                    ClassId = bricks[i].img,
                    Margin = new Thickness(0, 0, 0, 0),
                    Aspect = Aspect.AspectFill
                };
                gr.Children.Add(img,colCount,rowCount);
                colCount++;
                if (colCount > numCols - 1)
                {
                    colCount = 0;
                    rowCount++;
                }
            }
            innerGrid.Children.Add(gr, 1, 0);

enter image description here

I use FlexLayout with a binding to an ObservableCollection. This allows me some control over the size and shape of the images and display area, while also letting me take advantage of the flow properties of the FlexLayout.

I realize this is a different approach from what you were doing, but I hope you can take some value from this approach too.

xaml page:

<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="XFGrid_Test.BindablePage">
<ContentPage.Content>
    <StackLayout VerticalOptions="Start">
        <FlexLayout BindableLayout.ItemsSource="{Binding Images}"
            Wrap="Wrap"
            Direction="Row"
            JustifyContent="Center"
            AlignItems="Center"
            AlignContent="Center"
            x:Name="flexLayout">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <Image ClassId="{Binding ImageCode}" Source="{Binding ImageSrc}" WidthRequest="{Binding ImageWidth}" HeightRequest="{Binding ImageHeight}" Margin="2" >
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" NumberOfTapsRequired="1"></TapGestureRecognizer>
                        </Image.GestureRecognizers>
                    </Image>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
            <BindableLayout.EmptyView>
                <Label Text="Nothing here."></Label>
            </BindableLayout.EmptyView>
        </FlexLayout>
    </StackLayout>
</ContentPage.Content>

xaml.cs:

public ObservableCollection<ImageModel> Images { get; set; }

    int HorizontalMargin { get; set; }
    int VerticalMargin { get; set; }
    int VerticalAdjustment { get; set; } //adjusts for navigation and info bar
    int numberOfImages = 25;
    int numColumns = 6;
    int MaxImageWidth;
    int MaxImageHeight;
    int ImageGap = 1;

    public BindablePage()
    {
        InitializeComponent();

        Images = new ObservableCollection<ImageModel>();
        
        //Adjust the vertical display area to account for on-screen controls, etc.
        //This needs refinement - or rethinking.
        VerticalAdjustment = 100;
        
        //Get the device display size (uses Xamarin.Essentials)
        var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
        //Available horizantal screen space - x dimension
        var xWidth = mainDisplayInfo.Width / mainDisplayInfo.Density;
        //Available vertical screen space - y dimension
        var yHeight = (mainDisplayInfo.Height / mainDisplayInfo.Density) - VerticalAdjustment;
        //Determine the MaxImageWidth of the image to fit horizontally
        //based on the number of rows desired and the available horizontal screen space
        var numHorizontalGaps = numColumns * 2 - 1;
        MaxImageWidth = (int)Math.Round(xWidth / numColumns, MidpointRounding.AwayFromZero) - numHorizontalGaps * ImageGap;
        //Determine Number of image rows to fit vertically based on fixed number of columns and number of images
        var nRows = numberOfImages / numColumns;
        if (numberOfImages % numColumns > 0)
            nRows++;
        var numVerticalGaps = nRows * 2 - 1;
        MaxImageHeight = (int)Math.Round(yHeight / nRows, MidpointRounding.AwayFromZero) - numVerticalGaps * ImageGap;
        //Choose the dimension that is smaller (because it must fit).
        var Dimension = Math.Min(MaxImageHeight, MaxImageWidth);
        //Determine the left and right margins
        HorizontalMargin = (int)Math.Round((xWidth - (((numColumns + 1) * Dimension) + (numColumns - 1) * ImageGap)), MidpointRounding.AwayFromZero) / 2;
        //Determine the top and bottom margins - this will center top to bottom (set to zero for top alignment)
        VerticalMargin = (int)Math.Round((yHeight - ((nRows) * Dimension) + (nRows - 1) * ImageGap), MidpointRounding.AwayFromZero) / 2;
        //VerticalMargin = 0;
        //Set the margins of the FlexLayout named "flexLayout"
        flexLayout.Margin = new Thickness(HorizontalMargin, VerticalMargin);

        //Load up the ObservableCollection
        for (var i = 0; i < numberOfImages; i++)
        {
            ImageModel img = new ImageModel
            {
                ImageID = i,
                ImageSrc = "https://pbs.twimg.com/profile_images/471641515756769282/RDXWoY7W_400x400.png",//Globals.mediaUrl + bricks[i].img,
                ImageCode = i.ToString(),
                ImageHeight = Dimension, //Height and Width shouild be equal
                ImageWidth = Dimension,
                ImageName = i.ToString(),
            };
            Images.Add(img);
        }
        BindingContext = this;
    }

    private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
    {
        //Handle the tap
    }

ImageModel class:

public class ImageModel
{
    public int ImageID { get; set; }
    public string ImageName { get; set; }
    public string ImageSrc { get; set; }
    public int ImageWidth { get; set; }
    public int ImageHeight { get; set; }
    public string ImageCode { get; set; }
}

The result with 12 images:

enter image description here

Result with 16 images:

enter image description here

Related