wpf icon indicating copy to clipboard operation
wpf copied to clipboard

Implicit style lookup not working correctly when control is part of a template

Open batzen opened this issue 1 year ago • 7 comments
trafficstars

Description

Implicit style lookup not working correctly when control is part of a template. That's because FindImplicitStyleResource sets a boundaryElement which causes implicit style lookup to bubble up to the ContentPresenter and immediately skip to App- and Theme-Resources.

There is a comment in the code stating:

// For non-controls the implicit StyleResource lookup must stop at // the templated parent. Look at task 25606 for further details.

I have no clue what's inside task 25606, but if that code there is really required the details should be added to the comment.

Reproduction Steps

See repro StyleLookup.zip

Expected behavior

Both TextBlock elements should have a green foreground.

Actual behavior

The TextBlock generated by the ContentPresenter has a red foreground. Regular TextBlock has a green foreground.

Regression?

No

Known Workarounds

A workaround will be shown in the commits fixing https://github.com/snoopwpf/snoopwpf/issues/397 But such workaround is not suitable for regular applications i think.

Impact

No response

Configuration

No response

Other information

No response

batzen avatar Mar 02 '24 06:03 batzen

@batzen I think this is a WPF bug. But as you says, we do not know the task 25606, and maybe this behavior is to fix another bug.

lindexi avatar Mar 03 '24 01:03 lindexi

I always thought this was by design, since the ad-hoc TextBlock created by the ContentPresenter has no real connection to the surrounding MainWindow. Likewise the text within a ToolTip would also become green, what would be probably fairly surprising for many devs.

I usually work around this by either defining resources as application resources within the App.xaml or use something I would call resource nesting.

Either directly on the control in question:

<ContentPresenter Content="Foo">
    <ContentPresenter.Resources>
        <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}" />
    </ContentPresenter.Resources>
</ContentPresenter>

Or by deeper nesting in the resources:

<Window.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="Foreground" Value="Green" />
    </Style>

    <Style TargetType="ContentPresenter">
        <Style.Resources>
            <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}" />
        </Style.Resources>
    </Style>
</Window.Resources>

MichaeIDietrich avatar Mar 04 '24 18:03 MichaeIDietrich

Just for completeness, in case anyone is wondering, depending on the structure of the elements to style, the nesting can become fairly deep, so to apply the styling to text within a ToolTip, it would be necessary to write something like:

<Window.Resources>
    <Style Type="ToolTip">
        <Style.Resources>
            <Style TargetType="ContentPresenter">
                <Style.Resources>
                    <Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}" />
                </Style.Resources>
            </Style>
        </Style.Resources>
    </Style>
</Window.Resources>

MichaeIDietrich avatar Mar 04 '24 18:03 MichaeIDietrich

@batzen, I think what @MichaeIDietrich is saying here is right.

I went through some specs and some other forums, and this is a known decision taken by the team.

If the element being implicitly styled is not a subclass of Control, the implicit style lookup will not look beyond the templated parent’s template. (This is so users do not accidentally style controls they don’t even realize exist – such as a DockPanel buried deep inside the template for Button)

dipeshmsft avatar Apr 02 '24 16:04 dipeshmsft

If the element being implicitly styled is not a subclass of Control, the implicit style lookup will not look beyond the templated parent’s template. (This is so users do not accidentally style controls they don’t even realize exist – such as a DockPanel buried deep inside the template for Button)

Well users can accidentally do this by defining it at the app level. So your explanation seems counter intuitive.

If i define one style for TextBlock at the app level and one at the window level, for example, i expect the window defined style to be preferred. But instead the style from the app level is chosen. That's just unexpected.

batzen avatar Apr 02 '24 17:04 batzen

I think letting it check App.xaml is a way to let people override this behaviour if they really want it. There are other ways we can bypass this - https://stackoverflow.com/a/70357158/14917248

Also, if I am not wrong, implicit-style lookup does not go and look into theme dictionary. I can be wrong here, haven't checked it and the docs are too old ( the specs may have changed a bit over time )

dipeshmsft avatar Apr 03 '24 03:04 dipeshmsft

@dipeshmsft And the next user being confused about this restriction. See #9029

Can't we just add a lookup for the current Window also? Implicit style lookup, in this case, jumps to app and then to the theme dictionary. If this issue isn't fixed it will causes issues with window level Fluent themes also.

batzen avatar Apr 19 '24 18:04 batzen