-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[X] Allow using x:Type and type literals interchangeably in certain s…
…cenarios (#20915) * Add tests * Add new visitor to simplify type extension * Use the new visitor before SetPropertiesVisitor and ApplyPropertiesVisitor * Remove unnecessary code
- Loading branch information
1 parent
2317119
commit 4306566
Showing
7 changed files
with
176 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
#nullable disable | ||
|
||
namespace Microsoft.Maui.Controls.Xaml | ||
{ | ||
class SimplifyTypeExtensionVisitor : IXamlNodeVisitor | ||
{ | ||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp; | ||
public bool StopOnDataTemplate => false; | ||
public bool VisitNodeOnDataTemplate => true; | ||
public bool StopOnResourceDictionary => false; | ||
public bool IsResourceDictionary(ElementNode node) => false; | ||
public bool SkipChildren(INode node, INode parentNode) => false; | ||
|
||
public void Visit(ValueNode node, INode parentNode) | ||
{ | ||
} | ||
|
||
public void Visit(MarkupNode node, INode parentNode) | ||
{ | ||
//markup was already expanded to element | ||
} | ||
|
||
public void Visit(ElementNode node, INode parentNode) | ||
{ | ||
// Only simplify property setter of TypeExtension and x:Type | ||
// TargetType="{x:Type typeNameValue}" -> TargetType="typeNameValue" | ||
// x:DataType="{x:Type typeNameValue}" -> x:DataType="typeNameValue" | ||
|
||
if (IsValueOfXDataTypeOrTargetType(node, parentNode, out XmlName propertyName) | ||
&& IsTypeExtension(node, out ValueNode typeNameValueNode)) | ||
{ | ||
(parentNode as IElementNode).Properties[propertyName] = typeNameValueNode; | ||
} | ||
|
||
static bool IsValueOfXDataTypeOrTargetType(ElementNode node, INode parentNode, out XmlName propertyName) | ||
=> ApplyPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName) | ||
&& (IsXDataType(propertyName) || IsTargetTypePropertyOfMauiType(parentNode, propertyName)); | ||
|
||
static bool IsTargetTypePropertyOfMauiType(INode parentNode, XmlName propertyName) | ||
=> propertyName == new XmlName("", "TargetType") | ||
&& parentNode is ElementNode { XmlType: var parentType } | ||
&& (IsStyle(parentType) | ||
|| IsTrigger(parentType) | ||
|| IsDataTrigger(parentType) | ||
|| IsMultiTrigger(parentType)); | ||
|
||
static bool IsXDataType(XmlName name) => name == XmlName.xDataType; | ||
static bool IsStyle(XmlType type) => type.Name == nameof(Style) && type.NamespaceUri == XamlParser.MauiUri; | ||
static bool IsTrigger(XmlType type) => type.Name == nameof(Trigger) && type.NamespaceUri == XamlParser.MauiUri; | ||
static bool IsDataTrigger(XmlType type) => type.Name == nameof(DataTrigger) && type.NamespaceUri == XamlParser.MauiUri; | ||
static bool IsMultiTrigger(XmlType type) => type.Name == nameof(MultiTrigger) && type.NamespaceUri == XamlParser.MauiUri; | ||
|
||
static bool IsTypeExtension(ElementNode node, out ValueNode typeNameValueNode) | ||
{ | ||
XmlName typeNameXmlName = new("", "TypeName"); | ||
|
||
if (node.XmlType.Name == nameof(TypeExtension) | ||
&& node.XmlType.NamespaceUri == XamlParser.X2009Uri | ||
&& node.Properties.ContainsKey(typeNameXmlName) | ||
&& node.Properties[typeNameXmlName] is ValueNode valueNode | ||
&& valueNode.Value is string) | ||
{ | ||
typeNameValueNode = valueNode; | ||
return true; | ||
} | ||
|
||
typeNameValueNode = null; | ||
return false; | ||
} | ||
|
||
} | ||
|
||
public void Visit(RootNode node, INode parentNode) | ||
{ | ||
} | ||
|
||
public void Visit(ListNode node, INode parentNode) | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using System.Linq; | ||
using Microsoft.Maui.ApplicationModel; | ||
using Microsoft.Maui.Controls.Core.UnitTests; | ||
using Microsoft.Maui.Controls.Shapes; | ||
using Microsoft.Maui.Devices; | ||
using Microsoft.Maui.Dispatching; | ||
|
||
using Microsoft.Maui.Graphics; | ||
using Microsoft.Maui.UnitTests; | ||
using NUnit.Framework; | ||
|
||
namespace Microsoft.Maui.Controls.Xaml.UnitTests; | ||
|
||
public partial class Maui20818 | ||
{ | ||
public Maui20818() | ||
{ | ||
InitializeComponent(); | ||
} | ||
|
||
public Maui20818(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 TypeLiteralAndXTypeCanBeUsedInterchangeably([Values(false, true)] bool useCompiledXaml) | ||
{ | ||
var page = new Maui20818(useCompiledXaml); | ||
|
||
Assert.That((page.Resources["A"] as Style).TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That((page.Resources["B"] as Style).TargetType, Is.EqualTo(typeof(Label))); | ||
|
||
Assert.That(page.TriggerC.TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That(page.TriggerD.TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That(page.TriggerE.TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That(page.TriggerF.TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That(page.TriggerG.TargetType, Is.EqualTo(typeof(Label))); | ||
Assert.That(page.TriggerH.TargetType, Is.EqualTo(typeof(Label))); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?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" | ||
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui20818"> | ||
<ContentPage.Resources> | ||
<Style x:Key="A" TargetType="Label" /> | ||
<Style x:Key="B" TargetType="{x:Type Label}" /> | ||
</ContentPage.Resources> | ||
|
||
<Label> | ||
<Label.Triggers> | ||
<Trigger x:Name="TriggerC" TargetType="Label" Property="BackgroundColor" Value="Red" /> | ||
<DataTrigger x:Name="TriggerD" TargetType="Label" /> | ||
<MultiTrigger x:Name="TriggerE" TargetType="Label" /> | ||
</Label.Triggers> | ||
</Label> | ||
|
||
<Label> | ||
<Label.Triggers> | ||
<Trigger x:Name="TriggerF" TargetType="{x:Type Label}" Property="BackgroundColor" Value="Red" /> | ||
<DataTrigger x:Name="TriggerG" TargetType="{x:Type Label}" /> | ||
<MultiTrigger x:Name="TriggerH" TargetType="{x:Type Label}" /> | ||
</Label.Triggers> | ||
</Label> | ||
</ContentPage> |