Search code examples
c#wpfattached-properties

WPF Child defined attached property initial value


I would like to use an attached property to set properties of multiple child elements. Only during initialisation the VisualTreeHelper.GetChildrenCount(parent) returns 0 children.

When the binding MyText is changed after the view is rendered, all works as expected.

How can I itterate the children before rendering?

XAML:

<StackPanel local:SetTextService.Text="{Binding MyText}">
    <TextBox />
    <TextBox />
    <TextBox />
</StackPanel>

C#:

public class SetTextService : DependencyObject
{
    public static readonly DependencyProperty TextProperty =
                           DependencyProperty.RegisterAttached("Text", typeof(string), typeof(SetTextService),
                           new FrameworkPropertyMetadata("", new PropertyChangedCallback(TextPropertyChanged)));

    public static void SetText(UIElement element, string value)
    {
        element.SetValue(TextProperty, value);
    }

    public static string GetText(UIElement element)
    {
        return (string)element.GetValue(TextProperty);
    }

    private static void TextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SetChildControlText(d, (string)e.NewValue);
    }

    private static void SetChildControlText(DependencyObject parent, string text)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            PropertyInfo propInfo = child.GetType().GetProperty("Text");
            if (propInfo != null) propInfo.SetValue(child, text);
            SetChildControlText(child, text);
        }
    }
}

Solution

  • Your attached property is set before the TextBoxes are added. You could either handle the Loaded event of the StackPanel and do the processing in this event handler instead of doing it in the property changed callback, or you can set your attached property after the TextBoxes have been added using the following element syntax:

    <StackPanel>
        <TextBox />
        <TextBox />
        <TextBox />
        <local:SetTextService.Text>
            <Binding Path="MyText" />
        </local:SetTextService.Text>
    </StackPanel>
    

    The order in which the properties are set matters.