Is there an easy way to set default space between items inside StackPanel so I\'ll don\'t have to set Margin property on each item?
if all the controls are the same then do as IanR suggested and implement a Style that catches that control. if it's not then you can't create a default style to a base class because it just won't work.
the best way for situations like these is to use a very neat trick - attached properties (aka Behaviors in WPF4)
you can create a class that has an attached property, like so:
public class MarginSetter
{
public static Thickness GetMargin(DependencyObject obj)
{
return (Thickness)obj.GetValue(MarginProperty);
}
public static void SetMargin(DependencyObject obj, Thickness value)
{
obj.SetValue(MarginProperty, value);
}
// Using a DependencyProperty as the backing store for Margin. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MarginProperty =
DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(MarginSetter), new UIPropertyMetadata(new Thickness(), CreateThicknesForChildren));
public static void CreateThicknesForChildren(object sender, DependencyPropertyChangedEventArgs e)
{
var panel = sender as Panel;
if (panel == null) return;
foreach (var child in panel.Children)
{
var fe = child as FrameworkElement;
if (fe == null) continue;
fe.Margin = MarginSetter.GetMargin(panel);
}
}
}
now, to use it, all you need to do is to attach this attached property to any panel you want, like so:
<StackPanel local:MarginSetter.Margin="10">
<Button Content="hello " />
<Button Content="hello " />
<Button Content="hello " />
<Button Content="hello " />
</StackPanel>
Completely reusable of course.
I find that creating a grid inside the stack panel, then adding the desired number of columns (or rows) as follows:
<StackPanel Grid.Row="1" Grid.Column="0" Height="34" Width="698" Margin="10,5,10,10" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Grid Width="698" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="StartButton" Content="Start" Grid.Row="0" Grid.Column="0" Style="{StaticResource 3DButton}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="70" Click="StartButton_Click" />
<Button x:Name="HelpButton" Content="Help" Grid.Row="0" Grid.Column="1" Style="{StaticResource 3DButton}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="70" Click="HelpButton_Click" />
<Button x:Name="ExitButton" Content="Exit" Grid.Row="0" Grid.Column="2" Style="{StaticResource 3DButton}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="70" Click="ExitButton_Click" Foreground="Red" />
</Grid>
</StackPanel>
I use a transparent separator, which works well:
<Separator Opacity="0" Height="20"/>
You can of course use margins but then if you want to change the margins you have to update all of the elements.
The separator can even be styled in a static resource.
An attached property could do it too but I think it's overkill.
The accepted answer doesn't work anymore. But I used that answer and the blog the author of that answer (Elad Katz) to make a working code (tested in .Net Core) that I reproduce here:
public static class EstablecedorMargen {
public static Thickness GetMargen(DependencyObject objeto) => objeto != null ? (Thickness)objeto.GetValue(PropiedadMargen) : new Thickness();
public static void SetMargen(DependencyObject objeto, Thickness value) => objeto?.SetValue(PropiedadMargen, value);
public static readonly DependencyProperty PropiedadMargen
= DependencyProperty.RegisterAttached("Margen", typeof(Thickness), typeof(EstablecedorMargen), new UIPropertyMetadata(new Thickness(), Cambió));
public static void Cambió(object sender, DependencyPropertyChangedEventArgs e) {
if (!(sender is Panel panel)) return;
panel.Loaded += new RoutedEventHandler(EstablecerMargenControlesHijos);
}
public static void EstablecerMargenControlesHijos(object sender, RoutedEventArgs e) {
if (!(sender is Panel panel)) return;
foreach (var hijo in panel.Children) {
if (!(hijo is FrameworkElement feHijo)) continue;
feHijo.Margin = GetMargen(panel);
}
}
}
Then you use:
<StackPanel local:EstablecedorMargen.Margen="10" >
<Button Content="1" />
<Button Content="2" />
<Button Content="3" />
</StackPanel>