WPF use attached property before running app

144 Views Asked by At

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

1

There are 1 best solutions below

3
hiddenUser On

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);
                }
            }
        }
    }
}