21

I have the following XAML Xamarin.Forms.Button

<Button Text="Cancel" BackgroundColor="#3079a8" TextColor="White" />

I tried to add padding to it via the Padding property but that didn't work. After checking the forums and the docs, I realised there is no padding property in the documentation for Xamarin.Forms.Button (link to the docs) , is there some other type of quick fix to add just a little bit more padding to a button? A code example would be greatly appreciated.

sgarcia.dev
  • 5,671
  • 14
  • 46
  • 80

6 Answers6

44

usage:

<controls:EnhancedButton Padding="1,2,3,4"/>

advantages:

  • no nasty sideeffects
  • no problem with alignments
  • no viewtree uglyness
  • no view depth incrementation

ios:

[assembly: ExportRenderer(typeof(EnhancedButton), typeof(EnhancedButtonRenderer))]
namespace YOURNAMESPACE.iOS
{
    public class EnhancedButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);
            UpdatePadding();
        }

        private void UpdatePadding()
        {
            var element = this.Element as EnhancedButton;
            if (element != null)
            {
                this.Control.ContentEdgeInsets = new UIEdgeInsets(

                    (int)element.Padding.Top,
                    (int)element.Padding.Left,
                    (int)element.Padding.Bottom,
                    (int)element.Padding.Right
                );
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == nameof(EnhancedButton.Padding))
            {
                UpdatePadding();
            }
        }
    }
}

android:

[assembly: ExportRenderer(typeof(EnhancedButton), typeof(EnhancedButtonRenderer))]
namespace YOURNAMESPACE.Droid
{
    public class EnhancedButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);
            UpdatePadding();
        }

        private void UpdatePadding()
        {
            var element = this.Element as EnhancedButton;
            if (element != null)
            {
                this.Control.SetPadding(
                    (int)element.Padding.Left,
                    (int)element.Padding.Top,
                    (int)element.Padding.Right, 
                    (int)element.Padding.Bottom
                );
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == nameof(EnhancedButton.Padding))
            {
                UpdatePadding();
            }
        }
    }
}

pcl:

public class EnhancedButton : Button
{
    #region Padding    

    public static BindableProperty PaddingProperty = BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(EnhancedButton), default(Thickness), defaultBindingMode:BindingMode.OneWay);

    public Thickness Padding
    {
        get { return (Thickness) GetValue(PaddingProperty); }
        set { SetValue(PaddingProperty, value); }
    }

    #endregion Padding
}

Solution using effects instead of renderers, to allow easy usage for more than one control:

XAML:

<Label Text="Welcome to Xamarin.Forms!" BackgroundColor="Red">
    <Label.Effects>
        <xamTest:PaddingEffect Padding="20,40,20,40"></xamTest:PaddingEffect>
    </Label.Effects>
</Label>

PCL:

[assembly: ResolutionGroupName("ComponentName")]
namespace XamTest
{
    public class PaddingEffect : RoutingEffect
    {
        /// <inheritdoc />
        protected PaddingEffect(string effectId) : base($"ComponentName.{nameof(PaddingEffect)}")
        {
        }

        public Thickness Padding { get; set; }
    }
}

Android:

[assembly: ResolutionGroupName("ComponentName")]
[assembly: ExportEffect(typeof(XamTest.Droid.PaddingEffect), "PaddingEffect")]
namespace XamTest.Droid
{
    public class PaddingEffect : PlatformEffect
    {
        /// <inheritdoc />
        protected override void OnAttached()
        {
            if (this.Control != null)
            {
                var firstMatch = this.Element.Effects.FirstOrDefault(d => d is XamTest.PaddingEffect);
                if (firstMatch is XamTest.PaddingEffect effect)
                {
                    this.Control.SetPadding((int)effect.Padding.Left, (int)effect.Padding.Top, (int)effect.Padding.Right, (int)effect.Padding.Bottom);
                }
            }
        }

        /// <inheritdoc />
        protected override void OnDetached()
        {
        }
    }
}
Adam
  • 16,089
  • 6
  • 66
  • 109
Dbl
  • 5,634
  • 3
  • 41
  • 66
  • 1
    Thanks for this. I also needed to add `xmlns:controls="clr-namespace:YOURNAMESPACE;assembly=YOURNAMESPACE"` to the root xaml element. – Glenn May 29 '16 at 03:38
  • The generic BindableProperty.Create have been depreciated in Xamarin Forms 2.0. Use the non-generic BindableProperty.Create instead: https://forums.xamarin.com/discussion/comment/177726/#Comment_177726 – Chris C Jun 29 '16 at 14:26
  • @ChrisC that is indeed correct - changed accordingly – Dbl Jun 29 '16 at 17:25
  • This doesn't work for me for some reason. Using Material theme if that makes any difference. – kspearrin Aug 17 '16 at 02:07
  • @kspearrin what exactly isn't working? It works for many people so far – Dbl Aug 17 '16 at 09:35
  • See implementation here https://github.com/bitwarden/mobile/blob/master/src/Android/Controls/ExtendedButtonRenderer.cs and https://github.com/bitwarden/mobile/blob/master/src/App/Controls/ExtendedButton.cs. Being applied here https://github.com/bitwarden/mobile/blob/master/src/App/Controls/LabeledValueCell.cs. Not sure why but the button padding on android never changes, even if I set it at 0. – kspearrin Aug 18 '16 at 02:08
  • @kspearrin this is just a wild hunch, but there is a bug in https://github.com/bitwarden/mobile/blob/master/src/App/Controls/ExtendedButton.cs where you duplicate the property name "Padding" on your Uppercase BP. Maybe that messes things up – Dbl Aug 18 '16 at 09:32
  • Whoops. I actually just added that new property last night, after I even posted that comment. Definitely a bug, but the problem still occurred before I had added that. Will fix though. – kspearrin Aug 18 '16 at 10:44
  • @kspearrin too bad it's not that then - however i experienced the same issue at some point too. it was resolved after cleaning + updating packages though. – Dbl Aug 18 '16 at 11:40
  • What's weird is that I can even see (via breakpoint) the button's padding change as it comes into the renderer from 30 top, bottom and 36 left, right to 0s all around yet when it displays it looks like it back to the default 30, 36. I feel like something is overriding it after the fact, but I cannot see where. – kspearrin Aug 18 '16 at 12:42
  • @kspearrin odd - as said for me i also had the issue, but it was resolved after doing manual solution cleans and redownloading the packages. you could try the same. – Dbl Aug 18 '16 at 14:17
  • Thanks it worked! This should marked as the answered! – PLOW May 04 '17 at 20:55
  • Solid answer - I needed to add `using System.ComponentModel;` – jbyrd Jul 28 '17 at 18:49
  • 1
    Ridiculous that you have to create an "enhanced" button to adding *padding* to a freakin' button in Xamarin Forms. It's the most anemic, buggy framework I've ever used. It's as if its authors have never used it for anything, ever. There's no way, as a framework developer, you could test a button, see the complete lack of whitespace, and think it's OK to ship that. – Mud Aug 22 '17 at 04:33
  • @Mud Yeah. Kind of agree with that one. Personally i wonder why they didn't make their own rendering engines on each platform and mimiced wpf on each platform. WPFs rendering is so much better than ios/android still – Dbl Aug 22 '17 at 16:36
  • Sadly I am facing the same problem as @kspearrin and have no idea why. I see the same result via breakpoint but somehow it defaults back to original values. :( – Ghost Nov 30 '17 at 13:02
  • @Ghost did you modify the code at all? I would suggest cleaning+rebuilding the app. I really don't trust the debugger when working with xamarin anymore. – Dbl Nov 30 '17 at 14:24
  • @Dbl Not at all but it seems to have an effect on the button after all when using HeightRequest. When I used HeightRequest before - like when set to "20" - I would see the text disappear because the padding top and bottom was kept to default, but with your solution it seems to re-calculate the padding top and bottom to make the text fit. So it wasn't completely pointless. :) – Ghost Nov 30 '17 at 14:51
  • @Ghost You can post a github link of a stripped sample if you want and i can try to reproduce it? The thing is that i used it myself just like i posted here and it's working. So there has to be something that's different – Dbl Nov 30 '17 at 15:07
15

Update:

Padding has been added to the XF Button control in Xamarin.Forms v3.2+

<Button Padding="10,20,10,20" />

Old:

The best way to do it would be to increase the size of the button.

Then align the text as you see fit. Unfortunately it is about the best you can do. It works well if you have your text center aligned. Not so much if its left or right aligned.

Adam
  • 16,089
  • 6
  • 66
  • 109
  • Just to clarify, the author requested a quick fix for padding. You can of course use a renderer to go deeper. Also padding on a button is at least being discussed with the XF team. Unsure on whether it will be implemented though. – Adam Jan 13 '18 at 01:11
4

you can wrap your button in a StackLayout and add padding to the StackLayout

 <StackLayout Padding="10,10,10,10">
     <Button Text="Cancel" BackgroundColor="#3079a8" TextColor="White" />
  </StackLayout>
IdoT
  • 2,831
  • 1
  • 24
  • 35
3

In iOS, you can use a simple custom renderer to add a bit of padding to the button:

[assembly: ExportRenderer(typeof(Button), typeof(iOSButtonRenderer))]
namespace My.App.iOS.Renderers.Button
{
    /// <summary>
    /// A custom renderer that adds a bit of padding to either side of a button
    /// </summary>
    public class iOSButtonRenderer : ButtonRenderer
    {
        public iOSButtonRenderer() { }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
            Control.ContentEdgeInsets = new UIEdgeInsets(Control.ContentEdgeInsets.Top, Control.ContentEdgeInsets.Left + 10, Control.ContentEdgeInsets.Bottom, Control.ContentEdgeInsets.Right + 10);
        }
    }
}
Martin Rhodes
  • 1,118
  • 9
  • 13
1

It doesn't work for me in android. Looking at SetPadding function, I see that the Control has a minimum height of 132 and a minimum width of 242. I changed the SetPadding function in:

 private void SetPadding()
    {
        var element = Element as ExtendedButton;
        if (element != null)
        {
            Control.SetMinHeight(-1);
            Control.SetMinimumHeight(-1);
            Control.SetMinWidth(-1);
            Control.SetMinimumWidth(-1);
            Control.SetPadding(
                (int)element.Padding.Left,
                (int)element.Padding.Top - 6,
                (int)element.Padding.Right,
                (int)element.Padding.Bottom - 6);
        }
    }
1

The simplest you can do is

enter image description here

<StackLayout  Margin="0,20,0,0" Orientation="Horizontal" Spacing="20"> 
        <Switch VerticalOptions="CenterAndExpand"  ></Switch>
        <Label VerticalOptions="CenterAndExpand" TextColor="White">Remember Me</Label>
        <StackLayout  HorizontalOptions="FillAndExpand"><Button VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand" Text="Sign In" TextColor="White" BackgroundColor="#5cb85c"  ></Button>
        </StackLayout>
</StackLayout>

[such a big headache working with xamarin forms :'(]

Itzdsp
  • 872
  • 11
  • 22