Implicit styles in Application.Resources vs Window.Resources?
Asked Answered
A

5

37

I was looking at this question and noticed that placing an implicit TextBlock style in Application.Resources applies that style to all TextBlocks, even those inside other controls such as Buttons, ComboBoxes, etc

<Application.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Blue" />
    </Style>
</Application.Resources>

Placing the implicit style in Window.Resources does not cross control template boundaries, so things like Buttons and ComboBoxes maintain their default black text.

<Window.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Blue" />
    </Style>
</Window.Resources>

Furthermore, adding the default style in the Application.Resources makes it so you can't overwrite that style with another implicit style.

<!-- Doesn't work if implicit style with same property is in Application.Resources -->
<ComboBox.Resources>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red" />
    </Style>
</ComboBox.Resources>

My questions are:

  • Why is this?

  • Are there other differences between Application.Resources and Windows.Resources?

  • When should use one over the other?

    I understand that Application.Resources apply to the entire application, while Window.Resources apply to the window only, however I want to know why the styles in Application are treated differently than styles in Window

Adequacy answered 27/1, 2012 at 15:38 Comment(3)
Good question and insight about how implicit styles of App.xaml cross ControlTemplate boundaries. Because of this, I'm importing/merging certain resource dictionaries into each of my windows of the app (I'm really just importing a single master rd file though). Have you found a better way to deal with this issue than how I'm doing it? (It would be nicer to be able to just import it one time - as we do with imported resources of App.xaml.)Halfback
This crossing-ControlTemplate-boundary issue is especially troublesome for cases like an (App.xaml-level) implicit TextBlock style, where a TextBlock gets auto-generated as a child of a ContentPresenter of a Control. Using the Window-level trick that Rachel mentions solved that problem for me.Halfback
@Jason Typically my WPF applications only have a single window, with the content changing as needed. I don't think I've ever done one that's been more than two windows (Login and Application). That said, you could probably use MEF to import/export resource dictionaries, as explained hereAdequacy
I
29

This is really the only special handling added to WPF and it was done by design. The code that implements it can be found in FrameworkElement, in the method FindImplicitStyleResource, which effectively does:

internal static object FindImplicitStyleResource(FrameworkElement fe, object resourceKey, out object source)
{
        // ...
        DependencyObject boundaryElement = null;
        if (!(fe is Control))
            boundaryElement = fe.TemplatedParent;
        // ...
}

So the rule of thumb is that implicit Styles are always applied to controls (i.e. derives from Control). Assuming the implicit Style can be found.

For elements used in a ControlTemplate that do not derive from Control, such as TextBlock, the implicit Style look up will not cross it's templated parent. In your case above, this would be the ComboBox.

I believe this was done so that non-application implicit Styles for TextBlock were not inadvertently applied to TextBlock elements used in control templates, which the developer may or may not have known were there. The implicit Styles would only be applied to TextBlocks actually created by the developer in their own XAML.

The application implicit Styles would still allow global styling, such as increasing font size. But has probably caused more confusion than it's worth.

There is no good answer to say when to use one versus the other, as they each have their function. Obviously if you don't want to affect every TextBlock in your application, you shouldn't put the style in the application resources.

But keep in mind that this affects any non-Control element, such as Shape elements.

Ingles answered 6/2, 2012 at 20:41 Comment(3)
Do you know why an implicit style in Application.Resources overrides any other implicit style defined in the Resources of any other UI Element?Adequacy
@Adequacy - I don't believe that is true. For Controls, an implicit Style defined in Window.Resources would take precedence over ones in Application.Resources. For UIElements that do not derive from Control and that are used in a template, then the Windows.Resources is not queried, so in that case Application.Resources would be used.Ingles
You're right, its only UI elements that don't derive from Control, which don't inherit from implicit styles anyways. Thank you :)Adequacy
P
2

Pretty plain as simple.

If you want Resources to be shared among the ENTIRE application you would use Application.Resources.

If you want Resources to be shared among the ENTIRE Window you would use Window.Resources.

If you want Resources to be shared among a single control you would use (Whatever Control).Resources.

Let's say you have multiple windows, but you only want a default style in one, but not the other then you would use Window.Resources.

Polivy answered 27/1, 2012 at 16:3 Comment(4)
But I am confused why setting an implicit TextBlock style in Application.Resources will go past control boundaries, when the default behavior is for implicit styles not to not cross control templates unless the element the implicit style is applied to is of type Control. And so far all my WPF apps have been contained in a single Window, so I always use Window.ResourcesAdequacy
I personally avoid implicit styles because it confused new developers to the project. Say you have 100k line of code or more with over 20 resource dictionaries merged at the App.xaml and I add an implicit style in one of those. Your going to say to yourself if you never saw the code WHAT THE HECK IS GOING ON!!!! I can see using an implicite style in maybe a custom USerControl where its a test or quiz or something where every TextBlock may have the same margin.Polivy
I frequently use implicit styles since I am lazy and don't like to define named styles for absolutely everything. It's quite common for me to have implicit styles define margin, alignment, or colors of generic controls at the top of a View, although now that I think about it, I don't often apply implicit styles application-wide in Window or Application resources (of course, implicit DataTemplates are another story)Adequacy
I really recommend avoiding them when the Dictionaries get more complex or you join a team that has more than a handful of developers. Because you can be adding implicit styles while another developer is adding more and you 2 just overwrite each other. I REALLY would only use them in a simple UserControl where EVERYTHING looks the samePolivy
C
1

Rachel, I don't think there is anything special to "Styles". Moreover, there isn't an issue of "crossing template boundaries". The reason for this is different and it goes to the different "Trees" in a WPF applicaiton. By your question I recon you are picturing a world with the following hierarchy:
- Application => Window => Control => Elements within the control

There is no such hierarchy. There are different trees in a WPF applicaiton, the most famous are the Logical Tree and the Visual Tree, but there are more (the routing event tree and also the resource lookup tree, with slightly different semantics).

Assume the following XAML:

<Window x:Class="SO.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button x:Name="btn" Click="click">Click Me</Button>
    </Grid>
</Window>

For this XAML, the logical tree will look like:
- Window => Grid => Button => String

The textblock inside the button is not part of the logical tree (it is part of the VisualTree though).

Looking up for resources goes by the LogicalTree, with one difference. After it reaches the top object, the finding resource algorithm will look at the Application resource dictionary, and then at the Theme resource diectionary, and then at the System resource dictionary in this order.

See following articles:

Finnaly, to prove my point, add the following resource to the applicaiton XAML:

<Application x:Class="SO.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:clr="clr-namespace:System;assembly=mscorlib"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <clr:String x:Key="MyResource">Hello Application Resource</clr:String>
    </Application.Resources>
</Application>

and the following code behind:

private void click(object sender, RoutedEventArgs e)
{
    // Logical Children of btn
    Debug.WriteLine("Children of btn:");
    foreach( var x in LogicalTreeHelper.GetChildren(btn) ) {
        Debug.WriteLine("{0} : {1}", x, x.GetType());
    }

    // Walk the visual tree
    Debug.WriteLine("The Visual Tree:");
    WalkVisual(0, this);

    // Find the textblock within the button
    DependencyObject p = btn;
    while (p.GetType() != typeof(TextBlock))
        p = VisualTreeHelper.GetChild(p, 0);
     TextBlock tb = p as TextBlock;

    // Now climp the textblock through the logical tree
    while (p != null)
    {
        Debug.WriteLine("{0}", p.GetType());
        p = LogicalTreeHelper.GetParent(p);
    }

    // Find a resource for the textbox
    string s = tb.FindResource("MyResource") as string;
    Debug.WriteLine("MyResource Content: {0}", s);
}

private void WalkVisual(int indent, DependencyObject p)
{
    string fmt = string.Format("{{0,{0}}}{{1}}", indent * 4);
    Debug.WriteLine(fmt, "", p.GetType());
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(p); ++i)
    {
        WalkVisual(indent+1,VisualTreeHelper.GetChild(p, i));
    }
}

So ... once you understand the first question ('why is that'), the other questions fall apart. The different between application resources and window resources is that application resources can be fount by any DependencyObject in your application, including those defined in other assemblies. You'll use it when this is what you want to acheive :-)

u.

Calipash answered 4/2, 2012 at 6:3 Comment(1)
There is special handling in WPF for Styles whose target type does not derive from Control, such as TextBlock. As Racheal explains, the TextBlock Style is handled differently when placed in the Application.Resources versus Window.Ingles
C
0

The difference lies in the scope of the styles :

  • when placed in Application.Resources, a style will apply to all controls in the application
  • when placed inside Windows.Resources, a style will apply to all controls in the window

the difference is quite subtle there, but what it means is that whatever control we're talking about (included one that's in another control's template) will get the style from application.Resources. but only the controls directly children of the window will get the style from window.Resources. A control inside antoher control's template will not have the style defined in Window.Resources since it is not directly in the window, whereas it will have the style defined in Application.Resources since it is in the application.

as for your second point, it has to do with dependency property precedence I think:

http://msdn.microsoft.com/en-us/library/ms743230.aspx

Centigram answered 31/1, 2012 at 15:3 Comment(1)
That's not true. If you have a Button in the ControlTemplate of another Control (such as TextBox) then any implicit Style for Button in Windows.Resources will still be applied.Ingles
B
0

CodeNaked Answered true. It is made by WPF design. I opened the ticket question here which is a full explanation that it is by design. Also, what are online documents (dependency-property-precedence-list), function (DependencyPropertyHelper.GetValueSource) and desktop tools (snoopwpf) to use to help you navigate through these behaviors. While we are unhappy as WPF users (we expect the same behavior) we can't do anything about it. Options: Explicitly define content like <Label.Content> or quotation " to do considerably more work - custom templates, custom type for the content, new control etc. which I don't see as really necessary."

Bernard answered 7/9, 2022 at 11:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.