Skip to content

Commit 9371f72

Browse files
committed
fix(xLoad): Fix x:Load nesting and ElementStub strong reference
1 parent 38b35f3 commit 9371f72

File tree

12 files changed

+690
-37
lines changed

12 files changed

+690
-37
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#nullable enable
2+
3+
4+
namespace Uno.UI.SourceGenerators.XamlGenerator
5+
{
6+
/// <summary>
7+
/// Definition for Components used for lazy static resources and x:Load marked objects
8+
/// </summary>
9+
internal class ComponentDefinition
10+
{
11+
public ComponentDefinition(XamlObjectDefinition xamlObject, bool isWeakReference)
12+
{
13+
IsWeakReference = isWeakReference;
14+
XamlObject = xamlObject;
15+
}
16+
17+
public bool IsWeakReference { get; }
18+
public XamlObjectDefinition XamlObject { get; }
19+
}
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#nullable enable
2+
3+
4+
namespace Uno.UI.SourceGenerators.XamlGenerator
5+
{
6+
internal class ComponentEntry
7+
{
8+
public ComponentEntry(string variableName, XamlObjectDefinition objectDefinition)
9+
{
10+
VariableName = variableName;
11+
ObjectDefinition = objectDefinition;
12+
}
13+
14+
public string VariableName { get; }
15+
public XamlObjectDefinition ObjectDefinition { get; }
16+
}
17+
}

src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/NameScope.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public NameScope(string? @namespace, string className)
3636

3737
public Dictionary<string, Subclass> Subclasses { get; } = new Dictionary<string, Subclass>();
3838

39-
public List<XamlObjectDefinition> Components { get; } = new List<XamlObjectDefinition>();
39+
public List<ComponentDefinition> Components { get; } = new List<ComponentDefinition>();
4040

4141
public List<XamlObjectDefinition> XBindExpressions { get; } = new List<XamlObjectDefinition>();
4242

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#nullable enable
2+
3+
using System.Collections.Generic;
4+
5+
namespace Uno.UI.SourceGenerators.XamlGenerator
6+
{
7+
internal class XLoadScope
8+
{
9+
public List<ComponentEntry> Components { get; } = new List<ComponentEntry>();
10+
public int ComponentCount => Components.Count;
11+
12+
/// <summary>
13+
/// List of action handlers for registering x:Bind events
14+
/// </summary>
15+
public List<BackingFieldDefinition> xBindEventsHandlers { get; } = new List<BackingFieldDefinition>();
16+
17+
}
18+
}

src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ internal partial class XamlFileGenerator
5656
/// </summary>
5757
private readonly Dictionary<(string? Theme, string ResourceKey), string> _topLevelQualifiedKeys = new Dictionary<(string?, string), string>();
5858
private readonly Stack<NameScope> _scopeStack = new Stack<NameScope>();
59+
private readonly Stack<XLoadScope> _xLoadScopeStack = new Stack<XLoadScope>();
5960
private readonly Stack<ResourceOwner> _resourceOwnerStack = new Stack<ResourceOwner>();
6061
private readonly XamlFileDefinition _fileDefinition;
6162
private readonly string _targetPath;
@@ -835,7 +836,7 @@ private void BuildCompiledBindingsInitializer(IndentedStringBuilder writer, stri
835836
{
836837
TryAnnotateWithGeneratorSource(writer);
837838
var hasXBindExpressions = CurrentScope.XBindExpressions.Count != 0;
838-
var hasResourceExtensions = CurrentScope.Components.Any(HasMarkupExtensionNeedingComponent);
839+
var hasResourceExtensions = CurrentScope.Components.Any(c => HasMarkupExtensionNeedingComponent(c.XamlObject));
839840
var isFrameworkElement =
840841
IsType(className, _frameworkElementSymbol) // The current type may not have a base type as it is defined in XAML,
841842
|| IsType(controlBaseType, _frameworkElementSymbol); // so look at the control base type extracted from the XAML.
@@ -864,7 +865,7 @@ private void BuildCompiledBindingsInitializer(IndentedStringBuilder writer, stri
864865
private void BuildCompiledBindingsInitializerForTemplate(IIndentedStringBuilder writer)
865866
{
866867
TryAnnotateWithGeneratorSource(writer);
867-
var hasResourceExtensions = CurrentScope.Components.Any(HasMarkupExtensionNeedingComponent);
868+
var hasResourceExtensions = CurrentScope.Components.Any(c => HasMarkupExtensionNeedingComponent(c.XamlObject));
868869

869870
if (hasResourceExtensions)
870871
{
@@ -873,16 +874,16 @@ private void BuildCompiledBindingsInitializerForTemplate(IIndentedStringBuilder
873874
using (writer.BlockInvariant($"__fe.Loading += delegate"))
874875
{
875876
BuildComponentResouceBindingUpdates(writer);
876-
BuildxBindEventHandlerInitializers(writer);
877+
BuildxBindEventHandlerInitializers(writer, CurrentScope.xBindEventsHandlers);
877878
}
878879
writer.AppendLineInvariant(";");
879880
}
880881
}
881882
}
882883

883-
private void BuildxBindEventHandlerInitializers(IIndentedStringBuilder writer, string prefix = "")
884+
private static void BuildxBindEventHandlerInitializers(IIndentedStringBuilder writer, List<BackingFieldDefinition> xBindEventsHandlers, string prefix = "")
884885
{
885-
foreach (var xBindEventHandler in CurrentScope.xBindEventsHandlers)
886+
foreach (var xBindEventHandler in xBindEventsHandlers)
886887
{
887888
writer.AppendLineInvariant($"{prefix}{xBindEventHandler.Name}?.Invoke();");
888889

@@ -897,7 +898,7 @@ private void BuildComponentResouceBindingUpdates(IIndentedStringBuilder writer)
897898
{
898899
var component = CurrentScope.Components[i];
899900

900-
if (HasMarkupExtensionNeedingComponent(component) && IsDependencyObject(component))
901+
if (HasMarkupExtensionNeedingComponent(component.XamlObject) && IsDependencyObject(component.XamlObject))
901902
{
902903
writer.AppendLineInvariant($"_component_{i}.UpdateResourceBindings();");
903904
}
@@ -911,11 +912,12 @@ private void BuildComponentFields(IIndentedStringBuilder writer)
911912
var current = CurrentScope.Components[i];
912913

913914
var componentName = $"_component_{i}";
914-
var typeName = GetType(current.Type).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
915+
var typeName = GetType(current.XamlObject.Type).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
915916

916-
writer.AppendLineInvariant($"private global::Windows.UI.Xaml.Markup.ComponentHolder {componentName}_Holder = new global::Windows.UI.Xaml.Markup.ComponentHolder();");
917+
var isWeak = current.IsWeakReference ? "true" : "false";
918+
writer.AppendLineInvariant($"private global::Windows.UI.Xaml.Markup.ComponentHolder {componentName}_Holder = new global::Windows.UI.Xaml.Markup.ComponentHolder(isWeak: {isWeak});");
917919

918-
using (writer.BlockInvariant($"private {GetType(current.Type).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} _component_{i}"))
920+
using (writer.BlockInvariant($"private {GetType(current.XamlObject.Type).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} _component_{i}"))
919921
{
920922
using (writer.BlockInvariant("get"))
921923
{
@@ -933,7 +935,7 @@ private void BuildComponentFields(IIndentedStringBuilder writer)
933935
private void BuildCompiledBindings(IndentedStringBuilder writer, string className)
934936
{
935937
var hasXBindExpressions = CurrentScope.XBindExpressions.Count != 0;
936-
var hasResourceExtensions = CurrentScope.Components.Any(HasMarkupExtensionNeedingComponent);
938+
var hasResourceExtensions = CurrentScope.Components.Any(c => HasMarkupExtensionNeedingComponent(c.XamlObject));
937939

938940
if (hasXBindExpressions || hasResourceExtensions)
939941
{
@@ -975,16 +977,16 @@ private void BuildCompiledBindings(IndentedStringBuilder writer, string classNam
975977
{
976978
var component = CurrentScope.Components[i];
977979

978-
if (HasXBindMarkupExtension(component))
980+
if (HasXBindMarkupExtension(component.XamlObject))
979981
{
980-
var isDependencyObject = IsDependencyObject(component);
982+
var isDependencyObject = IsDependencyObject(component.XamlObject);
981983

982984
var wrapInstance = isDependencyObject ? "" : ".GetDependencyObjectForXBind()";
983985

984986
writer.AppendLineInvariant($"owner._component_{i}{wrapInstance}.ApplyXBind();");
985987
}
986988

987-
BuildxBindEventHandlerInitializers(writer, "owner.");
989+
BuildxBindEventHandlerInitializers(writer, CurrentScope.xBindEventsHandlers, "owner.");
988990
}
989991
}
990992
using (writer.BlockInvariant($"void {bindingsInterfaceName}.UpdateResources()"))
@@ -995,7 +997,7 @@ private void BuildCompiledBindings(IndentedStringBuilder writer, string classNam
995997
{
996998
var component = CurrentScope.Components[i];
997999

998-
if (HasMarkupExtensionNeedingComponent(component) && IsDependencyObject(component))
1000+
if (HasMarkupExtensionNeedingComponent(component.XamlObject) && IsDependencyObject(component.XamlObject))
9991001
{
10001002
writer.AppendLineInvariant($"owner._component_{i}.UpdateResourceBindings();");
10011003
}
@@ -3282,7 +3284,7 @@ private void BuildExtendedProperties(IIndentedStringBuilder outerwriter, XamlObj
32823284
writer.AppendLineInvariant($"global::Windows.UI.Xaml.NameScope.SetNameScope(this._component_{CurrentScope.ComponentCount}, nameScope);");
32833285
}
32843286

3285-
CurrentScope.Components.Add(objectDefinition);
3287+
AddComponentForCurrentScope(objectDefinition);
32863288
}
32873289
else if (isFrameworkElement && HasMarkupExtensionNeedingComponent(objectDefinition))
32883290
{
@@ -3469,9 +3471,7 @@ IMethodSymbol FindTargetMethodSymbol(INamedTypeSymbol? sourceType)
34693471

34703472
var builderName = $"_{ownerPrefix}_{CurrentScope.xBindEventsHandlers.Count}_{member.Member.Name}_{sanitizedEventTarget}_Builder";
34713473

3472-
CurrentScope.xBindEventsHandlers.Add(
3473-
new BackingFieldDefinition("global::System.Action", builderName, Accessibility.Private)
3474-
);
3474+
AddXBindEventHandlerToScope(builderName);
34753475

34763476
using (writer.BlockInvariant($"{builderName} = () => "))
34773477
{
@@ -5536,6 +5536,8 @@ private IEnumerable<XamlObjectDefinition> EnumerateSubElements(IEnumerable<XamlO
55365536
writer.AppendLine(")");
55375537
});
55385538

5539+
var xLoadScopeDisposable = XLoadScope();
5540+
55395541
return new DisposableAction(() =>
55405542
{
55415543
disposable?.Dispose();
@@ -5615,7 +5617,20 @@ private IEnumerable<XamlObjectDefinition> EnumerateSubElements(IEnumerable<XamlO
56155617
// Refresh the bindings when the ElementStub is unloaded. This assumes that
56165618
// ElementStub will be unloaded **after** the stubbed control has been created
56175619
// in order for the _component_XXX to be filled, and Bindings.Update() to do its work.
5618-
writer.AppendLineInvariant($"({componentName}_update_That.Target as {_className.className})?.Bindings.Update();");
5620+
5621+
using (writer.BlockInvariant($"if ({componentName}_update_That.Target is {_className.className} that)"))
5622+
{
5623+
if (CurrentXLoadScope != null)
5624+
{
5625+
foreach (var component in CurrentXLoadScope.Components)
5626+
{
5627+
writer.AppendLineInvariant($"that.{component.VariableName}.ApplyXBind();");
5628+
writer.AppendLineInvariant($"that.{component.VariableName}.UpdateResourceBindings();");
5629+
}
5630+
5631+
BuildxBindEventHandlerInitializers(writer, CurrentXLoadScope.xBindEventsHandlers, "that.");
5632+
}
5633+
}
56195634
}
56205635

56215636
writer.AppendLineInvariant($"{closureName}.Materializing += {componentName}_materializing;");
@@ -5627,9 +5642,11 @@ private IEnumerable<XamlObjectDefinition> EnumerateSubElements(IEnumerable<XamlO
56275642

56285643
var xamlObjectDef = new XamlObjectDefinition(elementStubType, 0, 0, definition);
56295644
xamlObjectDef.Members.AddRange(members);
5630-
CurrentScope.Components.Add(xamlObjectDef);
5631-
}
56325645

5646+
// Add the element stub as a strong reference, so that the
5647+
// stub can be brought back if the loaded state changes.
5648+
AddComponentForParentScope(xamlObjectDef, isWeak: false);
5649+
}
56335650
}
56345651
else
56355652
{
@@ -5663,6 +5680,8 @@ XamlMemberDefinition GenerateBinding(string name, XamlMemberDefinition? memberDe
56635680
return def;
56645681
}
56655682
}
5683+
5684+
xLoadScopeDisposable?.Dispose();
56665685
}
56675686
);
56685687
}
@@ -5910,18 +5929,56 @@ private bool IsDirectUserControlSubType(XamlObjectDefinition objectDefinition)
59105929
}
59115930

59125931
private NameScope CurrentScope
5932+
=> _scopeStack.Peek();
5933+
5934+
private IDisposable Scope(string? @namespace, string className)
59135935
{
5914-
get
5936+
_scopeStack.Push(new NameScope(@namespace, className));
5937+
5938+
return new DisposableAction(() => _scopeStack.Pop());
5939+
}
5940+
5941+
private void AddComponentForCurrentScope(XamlObjectDefinition objectDefinition)
5942+
{
5943+
CurrentScope.Components.Add(new ComponentDefinition(objectDefinition, true));
5944+
5945+
if (CurrentXLoadScope is { } xLoadScope)
59155946
{
5916-
return _scopeStack.Peek();
5947+
CurrentXLoadScope.Components.Add(new ComponentEntry($"_component_{CurrentScope.ComponentCount - 1}", objectDefinition));
59175948
}
59185949
}
59195950

5920-
private IDisposable Scope(string? @namespace, string className)
5951+
private void AddComponentForParentScope(XamlObjectDefinition objectDefinition, bool isWeak)
59215952
{
5922-
_scopeStack.Push(new NameScope(@namespace, className));
5953+
CurrentScope.Components.Add(new ComponentDefinition(objectDefinition, isWeak));
59235954

5924-
return new DisposableAction(() => _scopeStack.Pop());
5955+
if (_xLoadScopeStack.Count > 1)
5956+
{
5957+
var parent = _xLoadScopeStack.Skip(1).First();
5958+
5959+
parent.Components.Add(new ComponentEntry($"_component_{CurrentScope.ComponentCount - 1}", objectDefinition));
5960+
}
5961+
}
5962+
private void AddXBindEventHandlerToScope(string fieldName)
5963+
{
5964+
var definition = new BackingFieldDefinition("global::System.Action", fieldName, Accessibility.Private);
5965+
5966+
CurrentScope.xBindEventsHandlers.Add(definition);
5967+
5968+
if (CurrentXLoadScope is { } xLoadScope)
5969+
{
5970+
CurrentXLoadScope.xBindEventsHandlers.Add(definition);
5971+
}
5972+
}
5973+
5974+
private XLoadScope? CurrentXLoadScope
5975+
=> _xLoadScopeStack.Count != 0 ? _xLoadScopeStack.Peek() : null;
5976+
5977+
private IDisposable XLoadScope()
5978+
{
5979+
_xLoadScopeStack.Push(new XLoadScope());
5980+
5981+
return new DisposableAction(() => _xLoadScopeStack.Pop());
59255982
}
59265983

59275984
private ResourceOwner? CurrentResourceOwner
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Page x:Class="Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls.Binding_xLoad_Nested"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:local="using:Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml.Controls"
5+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
6+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7+
mc:Ignorable="d">
8+
9+
<StackPanel>
10+
<TextBlock x:Name="tb01" x:FieldModifier="public" Text="tb01" x:Load="{x:Bind TopLevelVisiblity1, Mode=OneWay}" />
11+
<TextBlock x:Name="tb02" x:FieldModifier="public" Text="tb02" x:Load="{x:Bind TopLevelVisiblity1, Mode=OneWay}" />
12+
<StackPanel x:Name="panel01" x:FieldModifier="public" x:Load="{x:Bind TopLevelVisiblity2, Mode=OneWay}">
13+
<TextBlock x:Name="tb03" x:FieldModifier="public" Text="tb03" />
14+
<!--<Border x:Name="border1">-->
15+
<StackPanel x:Name="panel02" x:FieldModifier="public" x:Load="{x:Bind TopLevelVisiblity3, Mode=OneWay}">
16+
<TextBlock x:Name="tb04" x:FieldModifier="public" Text="tb04" />
17+
</StackPanel>
18+
<!--</Border>-->
19+
</StackPanel>
20+
<StackPanel x:Name="panel03" x:FieldModifier="public" x:Load="{x:Bind TopLevelVisiblity2, Mode=OneWay}">
21+
<TextBlock x:Name="tb05" x:FieldModifier="public" Text="tb05" />
22+
<StackPanel x:Name="panel04" x:FieldModifier="public" x:Load="{x:Bind TopLevelVisiblity3, Mode=OneWay}">
23+
<TextBlock x:Name="tb06" x:FieldModifier="public" Text="tb06" />
24+
</StackPanel>
25+
</StackPanel>
26+
</StackPanel>
27+
</Page>

0 commit comments

Comments
 (0)