Это не очень сложно. Не могу сказать, насколько сложно было в 2009 году, когда был задан вопрос, но это было тогда.
Обратите внимание, что если вы явно задаете поле непосредственно для дочернего элемента сетки при использовании этого решения, это поле будет отображаться в конструкторе, но не во время выполнения.
Это свойство может быть применено к Grid, StackPanel, WrapPanel, UniformGrid или любому другому потомку Panel. Это влияет на непосредственных детей. Предполагается, что дети захотят управлять макетом своего содержимого.
PanelExt.cs
public static class PanelExt
{
public static Thickness? GetChildMargin(Panel obj)
{
return (Thickness?)obj.GetValue(ChildMarginProperty);
}
public static void SetChildMargin(Panel obj, Thickness? value)
{
obj.SetValue(ChildMarginProperty, value);
}
/// <summary>
/// Apply a fixed margin to all direct children of the Panel, overriding all other margins.
/// Panel descendants include Grid, StackPanel, WrapPanel, and UniformGrid
/// </summary>
public static readonly DependencyProperty ChildMarginProperty =
DependencyProperty.RegisterAttached("ChildMargin", typeof(Thickness?), typeof(PanelExt),
new PropertyMetadata(null, ChildMargin_PropertyChanged));
private static void ChildMargin_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as Panel;
target.Loaded += (s, e2) => ApplyChildMargin(target, (Thickness?)e.NewValue);
ApplyChildMargin(target, (Thickness?)e.NewValue);
}
public static void ApplyChildMargin(Panel panel, Thickness? margin)
{
int count = VisualTreeHelper.GetChildrenCount(panel);
object value = margin.HasValue ? margin.Value : DependencyProperty.UnsetValue;
for (var i = 0; i < count; ++i)
{
var child = VisualTreeHelper.GetChild(panel, i) as FrameworkElement;
if (child != null)
{
child.SetValue(FrameworkElement.MarginProperty, value);
}
}
}
}
Демо-версия:
MainWindow.xaml
<Grid
local:PanelExt.ChildMargin="2"
x:Name="MainGrid"
>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Width="100" Height="40" Fill="Red" Grid.Row="0" Grid.Column="0" />
<Rectangle Width="100" Height="40" Fill="Green" Grid.Row="1" Grid.Column="0" />
<Rectangle Width="100" Height="40" Fill="Blue" Grid.Row="1" Grid.Column="1" />
<Button Grid.Row="2" Grid.Column="0" Click="NoMarginClick">No Margin</Button>
<Button Grid.Row="2" Grid.Column="1" Click="BigMarginClick">Big Margin</Button>
<ComboBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" />
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void NoMarginClick(object sender, RoutedEventArgs e)
{
// In real life, if we wanted to change PanelExt.ChildMargin at runtime, we
// would prefer to bind it to something, probably a dependency property of
// the view. But this will do for a demonstration.
PanelExt.SetChildMargin(MainGrid, null);
}
private void BigMarginClick(object sender, RoutedEventArgs e)
{
PanelExt.SetChildMargin(MainGrid, new Thickness(20));
}
}