x:Bind initialization timing conflict when used in ResourceDictionary - NullReferenceException
See original GitHub issueDescribe the bug
I somehow stumbled upon a NullReferenceException
in the generated x:Bind code when the element containing the binding was null
. However, it only occurs in a certain scenario when the resource is not directly referenced from outside the ResourceDictionary. i.e. if the resource is only referenced from within the resource dictionary the binding to it fails; however, when the resource is used within the main visuals below it, the binding works.
However, it seems to only reproduce with specific sets of XAML trees as well. There’s definitely some weird timing/initialization issue going on here.
Steps to reproduce the bug Steps to reproduce the behavior:
- Install the
Microsoft.Toolkit.Uwp.UI
package version 7.1-rc1 or later. - Create a page with the following resources:
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"/>
<Page.Resources>
<ui:AttachedDropShadow
x:Key="CommonShadow"
CastTo="{x:Bind ShadowTarget}"
Offset="4" />
<Style TargetType="Button">
<Setter Property="ui:Effects.Shadow" Value="{StaticResource CommonShadow}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Background" Value="Red" />
</Style>
</Page.Resources>
- This is a base working case for the page content (as expected):
<Grid>
<Grid x:Name="ShadowTarget" />
<Image
Width="100"
Height="100"
ui:Effects.Shadow="{StaticResource CommonShadow}"
Source="ms-appx:///Assets/Photos/Owl.jpg" />
</Grid>
- There are a few different scenarios which can show the problem occurring, this is a minimal (if not practical) version of it which shows the same failure line:
<Grid>
<Grid x:Name="ShadowTarget" />
<!-- Uncomment the button and it works -->
<!--<Button
Margin="0,8,0,0"
VerticalAlignment="Top"
Content="Hello" />-->
<Image
Width="100"
Height="100"
Source="ms-appx:///Assets/Photos/Owl.jpg" />
</Grid>
If you uncomment out the button, then the binding is successful.
- In this more complex tree, it seems to fail even though the button implicitly is still referencing it like the above example. I’m not sure about what this tree causes the difference:
<ScrollViewer>
<Grid>
<Grid x:Name="ShadowTarget" />
<StackPanel VerticalAlignment="Center" Spacing="32">
<Button Content="I Have a Shadow!" />
<Image
Width="100"
Height="100"
Source="ms-appx:///Assets/Photos/Owl.jpg" />
<Rectangle
Width="200"
Height="100"
Fill="#80FF0000"
RadiusX="4"
RadiusY="4" />
<Button Content="I Also have a Shadow!" />
</StackPanel>
</Grid>
</ScrollViewer>
This now fails even though it appears similar to step 4 scenario when the button is uncommented. If you add the ui:Effects.Shadow="{StaticResource CommonShadow}"
property to the blank lines in the first Image and/or the last Rectangle then the sample works fine again! 🤷♂️
- It appears to work if you remove the
ScrollViewer
…?
Expected behavior Binding doesn’t throw null reference exception, still should bind…
Screenshots
Version Info
NuGet package version: None, WUX OS XAML UWP
Windows version | Saw the problem? |
---|---|
Insider Build (xxxxx) | |
May 2021 Update (19043) | Yes |
October 2020 Update (19042) | |
May 2020 Update (19041) | |
November 2019 Update (18363) | |
May 2019 Update (18362) | |
October 2018 Update (17763) | |
April 2018 Update (17134) | |
Fall Creators Update (16299) | |
Creators Update (15063) |
Device form factor | Saw the problem? |
---|---|
Desktop | Yes |
Xbox | |
Surface Hub | |
IoT |
Additional context Discovered in final testing of our Attached Shadow Toolkit PR here: https://github.com/CommunityToolkit/WindowsCommunityToolkit/pull/4179
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:5 (4 by maintainers)
Top GitHub Comments
This is from the framework optimizing incorrectly, and not creating the
CommonShadow
object when it’s actually needed. Xaml defers creatingResourceDictionary
entries until they’re actually referenced/needed (e.g. from aStaticResource
reference or someone accessingResourceDictionary
entries in code-behind).In the examples, Step 3 works because the
Image
has aStaticResource
reference toCommonShadow
object, so the framework will create it. Step 4 doesn’t work when theButton
is commented because nothing in the code actually referencesCommonShadow
. However, when theButton
is uncommented, the implicit style forButton
defined in the dictionary needs to be applied (and therefore instantiated itself) - when the implicit style is made, itsui:Effects.Shadow
setter referencesCommonShadow
, causingCommonShadow
to be created and making everything work again.One workaround is to use
x:Name
forCommonShadow
instead ofx:Key
, which will force the framework to instantiate it. However, the framework should really be force instantiating any objects withx:ConnectionId
on them (which are secretly added by the Xaml compiler before the.xaml
is fed to the parser - in this caseCommonShadow
would have one).x:ConnectionId
indicates some Xaml compiler generated code-behind needs them (e.g.x:Bind
).This feels like a completely different issue from 32092234.