Search code examples
c#wpfwpf-controlscustom-controlsresourcedictionary

Refresh template custom control after clearing and re-adding MergedDictionaries


The situation

I made a custom control MessageBar that has a style defined in a resource dictionary. This control has a dependency property Message that, like the name says, will contain a message.

public class MessageBar : Control
{
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(MessageBar),
                new FrameworkPropertyMetadata(string.Empty, OnMessageChanged));

        static MessageBar()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MessageBar), new FrameworkPropertyMetadata(typeof(MessageBar)));
        }

        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        private static void OnMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MessageBar messageBar = (MessageBar)d;

            if (e.NewValue != null && !string.IsNullOrWhiteSpace(e.NewValue.ToString()))
            {
                messageBar.Visibility = Visibility.Visible;

                if (messageBar.textBlock == null)
                    messageBar.textBlock = messageBar.GetChildOfType<TextBlock>();

           // Lots of unnecessary code
            }
}

Style

<Style TargetType="{x:Type controls:MessageBar}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:MessageBar}">
                    <Border Background="LightYellow"
                            BorderBrush="Black"
                            BorderThickness="1,0,1,1"
                            CornerRadius="0,0,10,10">
                        <StackPanel VerticalAlignment="Center"
                                    Orientation="Horizontal"
                                    Margin="10,0,10,0">
                            <!-- Actual text -->
                            <TextBlock Padding="4,2,4,2"
                                       Margin="5,0,0,0"
                                       x:Name="tbText"
                                       Text="{TemplateBinding Message}"
                                       FontSize="16"
                                       FontWeight="ExtraBold" />
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

When the application starts, the default style is loaded in the merged dictionaries. After the user logs in, the style that the user chose (this can be different of the default style) is loaded in the merged dictionaries. Reloading the style happens by clearing the merged dictionaries and re-adding the correct resource dictionaries to the merged dictionaries.

Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary()
{
    Source = ...
});
// Adding happens a few times.

The new style is correctly loaded and this is also visible in the UI. The UI changes correctly.

The problem

The problem happens when after clearing and re-adding the merged dictionaries, I try to find the child of type TextBlock in the OnMessageChanged method.

messageBar.textBlock = messageBar.GetChildOfType<TextBlock>();

I am 100% sure that there is nothing wrong with my GetChildOfType<>() method. It works correctly when used somewhere else.

When I execute this after re-adding, there are no child elements of MessageBar. ChildrenCount is 0.

When the merged dictionaries aren't cleared and re-added, there is a child element of type TextBlock found. This is what I want.

My guess is that after clearing and re-adding, the MessageBar doesn't have a correct reference to the style. And because of that, the template isn't applied.

What I already tried

I already tried overriding the ApplyTemplate() and OnStyleChanged() methods of the MessageBar control. But nothing works.

Question

How can I reload the style so that I (the GetChildOfType<TextBlock>() method) can find the TextBlock to set my message in the OnMessageChanged method.

Thanks in advance!

Greetings Loetn.


Solution

  • Try messageBar.ApplyTemplate() before calling GetChildOfType