From 21f12bc3e814baa479f0c6a6d68a9ab2ff6ecc2f Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Wed, 28 Sep 2022 13:56:52 -0400 Subject: [PATCH] feat(xbind): Add pathless casting support --- .../features/windows-ui-xaml-xbind.md | 22 +++++++++ .../Given_XBindRewriter.cs | 5 ++ .../Utils/XBindExpressionParser.cs | 47 +++++++++++++++++-- .../Controls/xBind_PathLessCasting.xaml | 17 +++++++ .../Controls/xBind_PathLessCasting.xaml.cs | 28 +++++++++++ .../xBind_PathLessCasting_Template.xaml | 26 ++++++++++ .../xBind_PathLessCasting_Template.xaml.cs | 33 +++++++++++++ .../xBindTests/Given_xBind_Binding.cs | 25 ++++++++++ 8 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml create mode 100644 src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml.cs create mode 100644 src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml create mode 100644 src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml.cs diff --git a/doc/articles/features/windows-ui-xaml-xbind.md b/doc/articles/features/windows-ui-xaml-xbind.md index 77558ed5bb3c..8a91209459c7 100644 --- a/doc/articles/features/windows-ui-xaml-xbind.md +++ b/doc/articles/features/windows-ui-xaml-xbind.md @@ -87,6 +87,28 @@ Uno supports the [`x:Bind`](https://docs.microsoft.com/en-us/windows/uwp/xaml-pl ``` +- [Pathless casting](https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension#pathless-casting) + ```xml + + + + + + + + + + + + + ``` + - `x:Load` binding ```xml diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests.Shared/Given_XBindRewriter.cs b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests.Shared/Given_XBindRewriter.cs index 34d97fb007b0..f35c996e31f5 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators.Tests.Shared/Given_XBindRewriter.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators.Tests.Shared/Given_XBindRewriter.cs @@ -30,6 +30,11 @@ public class Given_XBindRewriter [DataRow("ctx", "(FontFamily)a.Value", "(FontFamily)ctx.a.Value")] [DataRow("ctx", "(global::System.Int32)a.Value", "(global::System.Int32)ctx.a.Value")] + // Pathless casting https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension#pathless-casting + [DataRow("ctx", "MyFunction((global::System.String))", "ctx.MyFunction(((global::System.String)ctx))")] + [DataRow("ctx", "MyFunction2((global::System.String),(global::System.String))", "ctx.MyFunction2(((global::System.String)ctx),((global::System.String)ctx))")] + [DataRow("ctx", "(global::System.String)", "((global::System.String)ctx)")] + // Not supported https://github.com/unoplatform/uno/issues/5061 // [DataRow("ctx", "MyFunction((global::System.Int32)MyProperty)", "ctx.MyFunction((global::System.Int32)ctx.MyProperty)")] diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/Utils/XBindExpressionParser.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/Utils/XBindExpressionParser.cs index ea95ae7aaa14..3432a2fe3247 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/Utils/XBindExpressionParser.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/Utils/XBindExpressionParser.cs @@ -104,15 +104,27 @@ private string ContextBuilder var e = base.VisitMemberAccessExpression(node); var isValidParent = !Helpers.IsInsideMethod(node).result && !Helpers.IsInsideMemberAccessExpression(node).result; var isParentMemberStatic = node.Expression is MemberAccessExpressionSyntax m && _isStaticMember(m.ToFullString()); + var isPathLessCast = Helpers.IsPathLessCast(node); if (e!= null && isValidParent && !_isStaticMember(node.Expression.ToFullString()) && !isParentMemberStatic) { - var expression = e.ToFullString(); - var contextBuilder = _isStaticMember(expression) ? "" : ContextBuilder; - var output = ParseCompilationUnit($"class __Temp {{ private Func __prop => {contextBuilder}{expression}; }}"); + if (isPathLessCast.result) + { + var expression = e.ToFullString(); + var output = ParseCompilationUnit($"class __Temp {{ private Func __prop => ({isPathLessCast.expression?.Expression}){_contextName}; }}"); - var o2 = output.DescendantNodes().OfType().First().Expression; - return o2; + var newSyntax = output.DescendantNodes().OfType().First().Expression; + return newSyntax; + } + else + { + var expression = e.ToFullString(); + var contextBuilder = _isStaticMember(expression) ? "" : ContextBuilder; + var output = ParseCompilationUnit($"class __Temp {{ private Func __prop => {contextBuilder}{expression}; }}"); + + var newSyntax = output.DescendantNodes().OfType().First().Expression; + return newSyntax; + } } else { @@ -259,6 +271,31 @@ internal static (bool result, CastExpressionSyntax? expression) IsInsideCast(Syn return (false, null); } + + internal static (bool result, ParenthesizedExpressionSyntax? expression) IsPathLessCast(SyntaxNode node) + { + var currentNode = node.Parent; + + do + { + if (currentNode is ArgumentSyntax arg + && arg.Expression is ParenthesizedExpressionSyntax expressionSyntax) + { + return (true, expressionSyntax); + } + + if (currentNode is ArrowExpressionClauseSyntax arrow + && arrow.Expression is ParenthesizedExpressionSyntax expressionSyntax2) + { + return (true, expressionSyntax2); + } + + currentNode = currentNode?.Parent; + } + while (currentNode != null); + + return (false, null); + } } } } diff --git a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml new file mode 100644 index 000000000000..583cbf3875df --- /dev/null +++ b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml.cs b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml.cs new file mode 100644 index 000000000000..e666eeb281fb --- /dev/null +++ b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests.Controls +{ + public sealed partial class xBind_PathLessCasting : UserControl + { + public xBind_PathLessCasting() + { + this.InitializeComponent(); + } + } +} diff --git a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml new file mode 100644 index 000000000000..f64b115bab9e --- /dev/null +++ b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml.cs b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml.cs new file mode 100644 index 000000000000..1d28cb31b86f --- /dev/null +++ b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Controls/xBind_PathLessCasting_Template.xaml.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 + +namespace Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests.Controls +{ + public sealed partial class xBind_PathLessCasting_Template : UserControl + { + public xBind_PathLessCasting_Template() + { + this.InitializeComponent(); + } + } + + public class xBind_PathLessCasting_Template_Model + { + + } +} diff --git a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Given_xBind_Binding.cs b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Given_xBind_Binding.cs index f3f0ead15fba..3667368cf093 100644 --- a/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Given_xBind_Binding.cs +++ b/src/Uno.UI.Tests/Windows_UI_Xaml_Data/xBindTests/Given_xBind_Binding.cs @@ -1264,6 +1264,31 @@ public async Task When_NullableRecordStruct() Assert.AreEqual("42", SUT.tb1.Text); } + [TestMethod] + public async Task When_PathLessCasting() + { + var SUT = new xBind_PathLessCasting(); + + SUT.ForceLoaded(); + + Assert.AreEqual(SUT, SUT.tb1.Tag); + } + + [TestMethod] + public async Task When_PathLessCasting_Template() + { + var SUT = new xBind_PathLessCasting_Template(); + + SUT.ForceLoaded(); + + var rootData = new xBind_PathLessCasting_Template_Model(); + SUT.root.Content = rootData; + + var myObject = SUT.FindName("tb1") as TextBlock; + + Assert.AreEqual(rootData, myObject.Tag); + } + private async Task AssertIsNullAsync(Func getter, TimeSpan? timeout = null) where T:class { timeout ??= TimeSpan.FromSeconds(1);