Skip to content

Commit

Permalink
[XC] Fix x:DataType resolution for BindingContext (dotnet#21454)
Browse files Browse the repository at this point in the history
* Add test

* Add special case for resolving x:DataType for the  binding context property
  • Loading branch information
simonrozsival authored Apr 16, 2024
1 parent e2dd6a4 commit a7aeade
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
41 changes: 36 additions & 5 deletions src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,26 @@ static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext c

INode dataTypeNode = null;
IElementNode n = node;

// Special handling for BindingContext={Binding ...}
// The order of checks is:
// - x:DataType on the binding itself
// - SKIP looking for x:DataType on the parent
// - continue looking for x:DataType on the parent's parent...
IElementNode skipNode = null;
if (IsBindingContextBinding(node))
{
skipNode = GetParent(node);
}

while (n != null)
{
if (n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
if (n != skipNode && n.Properties.TryGetValue(XmlName.xDataType, out dataTypeNode))
{
break;
if (n.Parent is ListNode listNode)
n = listNode.Parent as IElementNode;
else
n = n.Parent as IElementNode;
}

n = GetParent(n);
}

if (dataTypeNode is null)
Expand Down Expand Up @@ -475,6 +487,25 @@ static IEnumerable<Instruction> CompileBindingPath(ElementNode node, ILContext c
yield return Create(Ldnull);
yield return Create(Newobj, module.ImportReference(ctorinforef));
yield return Create(Callvirt, module.ImportPropertySetterReference(context.Cache, bindingExtensionType, propertyName: "TypedBinding"));

static IElementNode GetParent(IElementNode node)
{
return node switch
{
{ Parent: ListNode { Parent: IElementNode parentNode } } => parentNode,
{ Parent: IElementNode parentNode } => parentNode,
_ => null,
};
}

static bool IsBindingContextBinding(ElementNode node)
{
// looking for BindingContext="{Binding ...}"
return GetParent(node) is IElementNode parentNode
&& ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out var propertyName)
&& propertyName.NamespaceURI == ""
&& propertyName.LocalName == nameof(BindableObject.BindingContext);
}
}

static IList<(PropertyDefinition property, TypeReference propDeclTypeRef, string indexArg)> ParsePath(ILContext context, string path, TypeReference tSourceRef, IXmlLineInfo lineInfo, ModuleDefinition module)
Expand Down
54 changes: 54 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui21434.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Dispatching;

using Microsoft.Maui.UnitTests;
using NUnit.Framework;

namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public partial class Maui21434
{
public Maui21434()
{
InitializeComponent();
}

public Maui21434(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}

[TestFixture]
class Test
{
[SetUp]
public void Setup()
{
Application.SetCurrentApplication(new MockApplication());
DispatcherProvider.SetCurrent(new DispatcherProviderStub());
}

[TearDown] public void TearDown() => AppInfo.SetCurrent(null);

[Test]
public void BindingsDoNotResolveStaticProperties([Values(false, true)] bool useCompiledXaml)
{
var page = new Maui21434(useCompiledXaml);
Assert.That(page.ParentTextLabel?.Text, Is.EqualTo("ParentText"));
Assert.That(page.ChildTextLabel?.Text, Is.EqualTo("ChildText"));
}
}
}

public class ParentViewModel21434
{
public string Text => "ParentText";
public ChildViewModel21434 Child { get; } = new();
}

public class ChildViewModel21434
{
public string Text => "ChildText";
}
15 changes: 15 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Maui21434.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui21434"
x:DataType="local:ParentViewModel21434">
<ContentPage.BindingContext>
<local:ParentViewModel21434 />
</ContentPage.BindingContext>

<VerticalStackLayout>
<Label Text="{Binding Text}" x:Name="ParentTextLabel" />
<Label BindingContext="{Binding Child}" Text="{Binding Text}" x:DataType="local:ChildViewModel21434" x:Name="ChildTextLabel" />
</VerticalStackLayout>
</ContentPage>

0 comments on commit a7aeade

Please sign in to comment.