-2

I have a WPF project using .Net Framework 4.7.2 (I have to use this version). The project uses a StackPanel that contains two Buttons. Since there is no Spacing property for the StackPanel I made my own which adjusts the margin of every child element.

Code for the DependencyProperty:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace LobeTruTool.AttachedProperties
{
    public class MarginSetterProperty
    {
        public static readonly DependencyProperty ChildMarginProperty = DependencyProperty.RegisterAttached(
            "ChildMargin",
            typeof(Thickness), //thickness is the same as margin
            typeof(StackPanel),
            new UIPropertyMetadata(new Thickness(), MarginChangedCallback));

        public static void SetChildMargin(UIElement element, Thickness margin)
        {
            element.SetValue(ChildMarginProperty, margin);
            MarginChangedCallback(element, new DependencyPropertyChangedEventArgs());
        }

        public static Thickness GetChildMargin(UIElement element)
        {
            return (Thickness)element.GetValue(ChildMarginProperty);
        }

        private static void MarginChangedCallback(object sender, DependencyPropertyChangedEventArgs e)
        {
            StackPanel stackPanel = sender as StackPanel;

            if (stackPanel != null)
            {
                stackPanel.Loaded += StackPanel_Loaded;
            }
        }

        private static void StackPanel_Loaded(object sender, RoutedEventArgs e)
        {
            StackPanel stackPanel = sender as StackPanel;

            foreach (FrameworkElement child in stackPanel.Children)
            {
                if (child.Margin == default)
                {
                    child.Margin = GetChildMargin(stackPanel);
                }
            }
        }
    }
}

Code where it is used:

<Window x:Class="LobeTruTool.View.Home"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:ap="clr-namespace:LobeTruTool.AttachedProperties"
        ...
        Title="Home" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" ap:MarginSetterProperty.ChildMargin="10,10,10,10">
                <Button Height="40" Width="100"/>
                <Button Height="40" Width="100"/>
            </StackPanel>
        </Grid>
    </Grid>
</Window>

When running the app everything works as expected but it is impractical that I have to start my app to see what it's gonna look like. So I was wondering if it was possible to adjust the margin before running the app.

Right now it looks like this: picture of current design

When running the app it looks like this: picture of design when app is running

hiddenUser
  • 35
  • 7
  • Which IDE are you using ? VS ? If so, o you have the "fx" button on the bottom left corner of the preview panel enabled ? – Irwene Aug 04 '22 at 08:45
  • 1
    Be aware that `typeof(StackPanel)` as third argument to RegisterAttached is wrong. It must be the type that declares the property, i.e. `typeof(MarginSetterProperty)`. As a note, you should make sure that the Loaded event handler is not attached multiple times, since MarginChangedCallback can obviously be called more than once. It does also make no sense to call MarginChangedCallback from the static setter method. It is already called because it is registered as PropertyChangedCallback. – Clemens Aug 04 '22 at 08:46
  • @Sidewinder94 I do have the "fx" button enabled. – hiddenUser Aug 04 '22 at 08:49
  • @Clemens when changing ```typeof(StackPanel)``` to ```typeof(MarginSetterProperty) ``` it worked. Thanks a bunch. – hiddenUser Aug 04 '22 at 08:51

1 Answers1

1

Solved thanks to a comment on the question. All I had to do was changing typeof(StackPanel) to typeof(MarginSetterProperty).

Here is the whole code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace LobeTruTool.AttachedProperties
{
    public class MarginSetterProperty
    {
        public static readonly DependencyProperty ChildMarginProperty = DependencyProperty.RegisterAttached(
            "ChildMargin",
            typeof(Thickness), //thickness is the same as margin
            typeof(MarginSetterProperty),
            new UIPropertyMetadata(new Thickness(), MarginChangedCallback));

        public static void SetChildMargin(UIElement element, Thickness margin)
        {
            element.SetValue(ChildMarginProperty, margin);
        }

        public static Thickness GetChildMargin(UIElement element)
        {
            return (Thickness)element.GetValue(ChildMarginProperty);
        }

        private static void MarginChangedCallback(object sender, DependencyPropertyChangedEventArgs e)
        {
            StackPanel stackPanel = sender as StackPanel;

            if (stackPanel != null && stackPanel.Tag == default)
            {
                stackPanel.Loaded += StackPanel_Loaded;
                stackPanel.Tag = "loaded event subscribed";
            }
        }

        private static void StackPanel_Loaded(object sender, RoutedEventArgs e)
        {
            StackPanel stackPanel = (StackPanel)sender;

            foreach (FrameworkElement child in stackPanel.Children)
            {
                if (child.Margin == default)
                {
                    child.Margin = GetChildMargin(stackPanel);
                }
            }
        }
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
hiddenUser
  • 35
  • 7
  • @Clemens I adjusted the code. Would you mind looking over it one more time? – hiddenUser Aug 04 '22 at 09:33
  • That looks better. You must also check if `sender as StackPanel` did not return null. Never use the `as` operator without checking the result for null. I've added that to your code. – Clemens Aug 04 '22 at 09:37
  • @Clemens thank you so much. You really were a great help. – hiddenUser Aug 04 '22 at 09:47