0

This question is the same as How to set a top margin only in XAML? but regarding Xamarin instead of WPF.

How can I set an individual margin on a view from XAML?

The linked Q&A indicates that the default implementation always sets all unspecified Margins to 0, even from code.

lucidbrot
  • 5,378
  • 3
  • 39
  • 68

3 Answers3

3

Unless I'm missing something, it's just a matter of specifying the Margin as follows:

<Label
    Margin = "0,20,0,0"
    Text = "Hello World!" />

The Margin is specified as left, top, right, bottom values.

James Lavery
  • 920
  • 4
  • 25
  • 1
    This only works if you already know all the Margins you want to set. If you would like to overwrite only one of them, this does not work - instead it sets all others to 0 – lucidbrot Apr 16 '19 at 09:37
  • 1
    That's what I was missing! Fair comment. My solution won't work. – James Lavery Apr 16 '19 at 09:50
1

The solution is to create an AttachedProperty:

using System;
using Xamarin.Forms;
namespace Sample.Utils
{
    /// <summary>
    /// Allows setting Top,Left,Right,Bottom Margin independently from each other.
    /// The default implementation does not allow that, even from code. This
    /// attached property remembers the previously set margins and only modifies what you intend to modify.
    /// </summary>
    public static class Margin
    {
        public static readonly BindableProperty LeftProperty = BindableProperty.CreateAttached(
            propertyName: "Left",
            returnType: typeof(double),
            declaringType: typeof(Margin),
            propertyChanged: LeftPropertyChanged,
            defaultValue: 0.0d);

        private static void LeftPropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            SetLeft(bindable, (double)newValue);
        }

        public static void SetLeft(BindableObject element, double value)
        {
            var view = element as View;
            if (view != null)
            {
                Thickness currentMargin = (Xamarin.Forms.Thickness)view.GetValue(View.MarginProperty);

                view.Margin = new Thickness(value, currentMargin.Top, currentMargin.Right, currentMargin.Bottom);
            }
        }

        public static double GetLeft(BindableObject element)
        {
            View view = element as View;
            if (view != null)
            {
                Thickness margin = (Xamarin.Forms.Thickness)view.GetValue(View.MarginProperty);
                return margin.Left;
            }
            return (double)LeftProperty.DefaultValue;
        }

    }
}

Declare this in the same way for Bottom, Top, and Right. Then use it in XAML like this:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
         xmlns:utils="using:Sample.Utils"
         x:Class="Sample.MyContentPage">
<StackLayout
   Orientation="Vertical"
   utils:Margin.Left="{StaticResource InnerPageContentMarginLeft}">
</StackLayout>
</ContentPage>

Sources:
* https://forums.xamarin.com/discussion/66026/use-attached-bindable-property-in-xaml
* https://stackoverflow.com/a/32408461/2550406

lucidbrot
  • 5,378
  • 3
  • 39
  • 68
0

Maybe try a valueConverter where you can send a value to converter through binding and return a Thickness object. Somewhat like

Margin ={{Binding Marginleft, Converter={StaticResource stringToThicknessConverter}}

where you pass a string and get back a thickness object like new thickness(0,marginleft,0,0).

Also you could directly bind to a thickness type object in viewModel but that's a bad practice as it would create a View dependency in ViewModel which defeats the purpose of MVVM

VirtualNomad
  • 83
  • 11