5

I would like to have four buttons like this:

<Grid x:Name="buttonGrid" Grid.Row="3" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="Center" Padding="20, 0">
    <Button x:Name="zeroButton" Text="0" HeightRequest="60" BackgroundColor="#2D7BF7" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"/>
    <Button x:Name="oneButton" Text="1" HeightRequest="60" BackgroundColor="#2D7BF7" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"/>
    <Button x:Name="twoButton" Text="2" HeightRequest="60" BackgroundColor="#2D7BF7" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"/>
    <Button x:Name="fiveButton" Text="5" HeightRequest="60" BackgroundColor="#2D7BF7" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"/>
</Grid>

On an iPhone or a small mobile I would like to have these fill 90% of the screen width. But on a bigger screen I would like the buttons to only fill 50% of the screen width.

Can anyone suggest to me how I can do this?

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
Alan2
  • 23,493
  • 79
  • 256
  • 450
  • use OnIdiom to specify a different layout for phone and tablet – Jason Aug 01 '17 at 21:37
  • I'm sorry I don't know anything about that. Is that what many people use? – Alan2 Aug 01 '17 at 22:13
  • yes, XF provides OnIdiom for specifically this case: where you want to have different behavior on Phone and Tablet. – Jason Aug 01 '17 at 22:58
  • Jason - Can you provide an example. Btw I will probably open up a bounty for this question in the next 24 hours :-) – Alan2 Aug 03 '17 at 13:08
  • have you attempted to search? There are numerous examples out there, like: https://stackoverflow.com/questions/40111252/is-it-possible-to-mix-onidiom-and-onplatform-in-xaml-xamarin-forms. You can also create separate XAML layouts for each idiom and just select which one to call programatically. – Jason Aug 03 '17 at 13:13

1 Answers1

10

Explanation

If you need to create layouts that reference the ContentPage.Width or ContentPage.Height properties, the best way to do it is by using a RelativeLayout.

If you try referencing the ContentPage.Width or ContentPage.Height properties directly, you will find that Xamarin.Forms returns -1. This is because -1 is the default value that Xamarin.Forms uses when it hasn't instantiated the control yet.

RelativeLayout uses Funcs to dynamically reference the page's Height and Width properties and will return the true value of the Height and Width properties once the ContentPage has been instantiated.

Code

using Xamarin.Forms;

namespace HorizontalButtonSampleApp
{
    public class ButtonPage : ContentPage
    {
        public ButtonPage()
        {
            const int relativeLayoutHorizontalSpacing = 5;
            const int numberOfButtons = 4;

            double screenUsePercentage;
            if (Device.Idiom.Equals(TargetIdiom.Phone))
                screenUsePercentage = 0.9;
            else
                screenUsePercentage = 0.5;

            var zeroButton = new StyledButton { Text = "0" };
            var oneButton = new StyledButton { Text = "1" };
            var twoButton = new StyledButton { Text = "2" };
            var fiveButton = new StyledButton { Text = "5" };

            var relativeLayout = new RelativeLayout();
            relativeLayout.Children.Add(zeroButton,
                                        Constraint.RelativeToParent(parent => parent.Width * (1 - screenUsePercentage) / 2),
                                        Constraint.Constant(0),
                                        Constraint.RelativeToParent(parent => (parent.Width * screenUsePercentage - relativeLayoutHorizontalSpacing * (numberOfButtons - 1)) / numberOfButtons));
            relativeLayout.Children.Add(oneButton,
                                        Constraint.RelativeToView(zeroButton, (parent, view) => view.X + view.Width + relativeLayoutHorizontalSpacing),
                                        Constraint.Constant(0),
                                        Constraint.RelativeToParent(parent => (parent.Width * screenUsePercentage - relativeLayoutHorizontalSpacing * (numberOfButtons - 1)) / numberOfButtons));
            relativeLayout.Children.Add(twoButton,
                                        Constraint.RelativeToView(oneButton, (parent, view) => view.X + view.Width + relativeLayoutHorizontalSpacing),
                                        Constraint.Constant(0),
                                        Constraint.RelativeToParent(parent => (parent.Width * screenUsePercentage - relativeLayoutHorizontalSpacing * (numberOfButtons - 1)) / numberOfButtons));
            relativeLayout.Children.Add(fiveButton,
                                        Constraint.RelativeToView(twoButton, (parent, view) => view.X + view.Width + relativeLayoutHorizontalSpacing),
                                        Constraint.Constant(0),
                                        Constraint.RelativeToParent(parent => (parent.Width * screenUsePercentage - relativeLayoutHorizontalSpacing * (numberOfButtons - 1)) / numberOfButtons));

            Content = relativeLayout;

            Padding = new Thickness(0, 20);

            Title = $"Button Page on a {Device.Idiom}";
        }
    }

    public class App : Application
    {
        public App()
        {
            MainPage = new NavigationPage(new ButtonPage());
        }
    }

    public class StyledButton : Button
    {
        public StyledButton()
        {
            TextColor = Color.Black;
            HeightRequest = 60;
            BackgroundColor = Color.FromHex("2D7BF7");
            HorizontalOptions = LayoutOptions.FillAndExpand;
            VerticalOptions = LayoutOptions.CenterAndExpand;
        }
    }
}

Tablet Screen Shot

enter image description here

Phone Screen Shot

enter image description here

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • Hi Brandon, I am sorry but what I need is them to be spaced horizontally. Sorry for the confusion. If you would like to amend your answer for that then I can accept. – Alan2 Aug 08 '17 at 17:01
  • No problem, @Alan! I edited the code above so that the buttons are spaced horizontally! I also created a new class called `StyledButton` that inherits from `Button`. Because each button has similar properties, we are able to set the similar properties once in `StyledButton` and reuse its code! – Brandon Minnick Aug 09 '17 at 01:46
  • great answer, Given that Xamarin doesn't recommend Relative layout (though I personally unfortunately love it) I would also recommend to check AbsoluteLayout. – Yuri S Aug 09 '17 at 02:26
  • Hi Brandon, My page is already set up with a StackLayout with Grids inside of that. Do you feel that I could achieve the same thing with a relative layout for the other parts of my page and then add your button code to the bottom of that? – Alan2 Aug 10 '17 at 19:54
  • Yea, definitely! You want to avoid nesting StackLayouts anyway. The Grid won’t work in this case simply because we need to reference the Parent’s width. – Brandon Minnick Aug 10 '17 at 20:03
  • 2
    @Alan If you specify what you already have and to what place you want to insert those buttons it will be easier to give you more detailed answer. If you want to insert them to grid then the size of the whole grid should change. – Yuri S Aug 10 '17 at 23:45
  • Thanks Yuri, I will specify what I have now in another question and then open that up with another bounty in about 48 hours. I'm using XAML so would be interested to see if you could come up with any suggestions as to how the XAML could be improved. – Alan2 Aug 11 '17 at 17:06
  • https://stackoverflow.com/questions/45640548/how-can-i-change-a-stacklayout-grid-screen-to-use-relativelayout. The question is quite complicated so you might want to wait until I open a bounty for it in a couple of days. Thanks – Alan2 Aug 11 '17 at 17:16