tags:

views:

1078

answers:

6

I can set margins individually in code but how do I do it in XAML, e.g. how do I do this:

PSEUDO-CODE:

<StackPanel Margin.Top="{Binding TopMargin}">
+1  A: 

You can't define just the Top margin with a binding, because Margin is of type Thickness which isn't a dependency object. However you could use a MultiValueConverter that would take 4 margin values to make 1 Thickness objects

Converter :

public class ThicknessMultiConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double left = System.Convert.ToDouble(values[0]);
        double top = System.Convert.ToDouble(values[1]);
        double right = System.Convert.ToDouble(values[2]);
        double bottom = System.Convert.ToDouble(values[3]);
        return new Thickness(left, top, right, bottom);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        Thickness thickness = (Thickness)value;
        return new object[]
        {
            thickness.Left,
            thickness.Top,
            thickness.Right,
            thickness.Bottom
        };
    }

    #endregion
}

XAML :

<StackPanel>
    <StackPanel.Margin>
        <MultiBinding Converter="{StaticResource myThicknessConverter}">
            <Binding Path="LeftMargin"/>
            <Binding Path="TopMargin"/>
            <Binding Path="RightMargin"/>
            <Binding Path="BottomMargin"/>
        </MultiBinding>
    </StackPanel.Margin>
</StackPanel>
Thomas Levesque
How would this work considering he wants to set a single part of the margin and leave the other existing values intact?
Ben M
Well, all properties will have be set at initialisation, but afterwards you just have to change one of the bound properties...
Thomas Levesque
BTW, your solution has exactly the same limitation ;)
Thomas Levesque
I know, but mine's less code. :-) OTOH, yours at least would rebind when any of the values changes...
Ben M
You could add a ConverterParameter to the MultiBinding to specify which values you want to set: for example ConverterParameter="Top,Right" would only require two bindings and return a thickness with only the top and right margin set.
Bubblewrap
Yes, but not setting the other ones is equivalent to set them to 0 (see Kent's answer)
Thomas Levesque
A: 

Use a converter, the sample code below will convert the double you are binding to to a thickness. It will set the "Top" of the thickness to the bound field. You could optionally use a ConverterParameter to determine if you are binding to left, top, right, or bottom.

<StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyThicknessConverter}">

.

public class ThicknessSingleValueConverter : IValueConverter
{
    override Convert(...)
    {
         return new Thickness(0, (double)object, 0, 0);
    }

    //etc...
NotDan
+5  A: 

The key is to realize that setting it in code like this:

sp2.Margin = new System.Windows.Thickness{ Left = 5 };

is equivalent to:

sp2.Margin = new System.Windows.Thickness{ Left = 5, Top = 0, Right = 0, Bottom = 0 };

You can't set just a single value in a Thickness instance through either code or XAML. If you don't set some of the values, they will be implicitly zero. Therefore, you can just do this to convert the accepted code sample in your other question to a XAML equivalent:

<StackPanel Margin="{Binding TopMargin, Converter={StaticResource MyConverter}}"/>

where MyConverter just returns a Thickness that sets only the Top and leaves all other values as zero.

Of course, you could write your own control that does expose these individual values as dependency properties to make your code a little cleaner:

<CustomBorder TopMargin="{Binding TopMargin}">
</CustomBorder>

HTH, Kent

Kent Boogaart
+1  A: 

Isn't this what you're looking for?

<StackPanel Margin="0,10,0,0" />

The first value is Left margin, then Top, then Right, and last but not least Bottom.

I'm not sure if you want to bind it to something, but if not, that'll work.

Carlo
A: 

I thought You could use the property syntax, from MSDN:

      <object.Margin>
        <Thickness Top="{Binding Top}"/>
      </object.Margin>

Than you won't need any converter

But the Top is not DependancyProperty - back to converter

Amittai Shapira
A: 

What would be nice is to be able to do this by specifying something like the code example below.

<StackPanel Margin=",10,,">

Unfortunately this capability doesn't seem to exist by default in WPF and it's a shame because it requires developers to hard code known default values in a way that later makes it more difficult to skin or theme an application.

The best solution that I can think of at this point is using a converter, but the amount of extra code you have to produce to introduce this is not ideal.

jpierson