Skip to content

Commit f6adcb5

Browse files
committed
fix(XamlReader): parsing of Rect type property
1 parent 795aa3a commit f6adcb5

File tree

2 files changed

+103
-32
lines changed

2 files changed

+103
-32
lines changed

src/Uno.UI.Tests/Windows_UI_Xaml_Markup/XamlReaderTests/Given_XamlReader.cs

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ public void When_xBind_TwoWay_Back()
12341234
[TestMethod]
12351235
public void When_Collection_Implicit_Add_Item()
12361236
{
1237-
var SUT = LoadXaml<SwipeItems>("""
1237+
var SUT = XamlHelper.LoadXaml<SwipeItems>("""
12381238
<SwipeItems>
12391239
<SwipeItem Text="asd" />
12401240
</SwipeItems>
@@ -1247,7 +1247,7 @@ public void When_Collection_Implicit_Add_Item()
12471247
[TestMethod]
12481248
public void When_Collection_Property_Nest_Collection()
12491249
{
1250-
var SUT = LoadXaml<SwipeControl>("""
1250+
var SUT = XamlHelper.LoadXaml<SwipeControl>("""
12511251
<SwipeControl>
12521252
<SwipeControl.LeftItems>
12531253
<SwipeItems Mode="Execute">
@@ -1266,7 +1266,7 @@ public void When_Collection_Property_Nest_Collection()
12661266
[TestMethod]
12671267
public void When_Collection_Property_Nest_Multiple_Collections()
12681268
{
1269-
var SUT = LoadXaml<SwipeControl>("""
1269+
var SUT = XamlHelper.LoadXaml<SwipeControl>("""
12701270
<SwipeControl>
12711271
<SwipeControl.LeftItems>
12721272
<!-- This is actually allowed, however only the last will be kept -->
@@ -1504,6 +1504,14 @@ public void When_Geometry()
15041504
Assert.AreEqual(FillRule.EvenOdd, geometry.FillRule);
15051505
}
15061506

1507+
[TestMethod]
1508+
public void When_RectangleGeometry()
1509+
{
1510+
var sut = XamlHelper.LoadXaml<RectangleGeometry>("<RectangleGeometry Rect='0 1 2 3' />");
1511+
1512+
Assert.AreEqual(new Windows.Foundation.Rect(0, 1, 2, 3), sut.Rect);
1513+
}
1514+
15071515
[TestMethod]
15081516
public void When_ThemeResource_With_StaticResource()
15091517
{
@@ -1598,35 +1606,6 @@ public void When_Binding_Converter_StaticResource()
15981606
Assert.AreEqual(new CornerRadius(0, 6, 6, 0), rightRadiusGrid.CornerRadius);
15991607
}
16001608

1601-
/// <summary>
1602-
/// XamlReader.Load the xaml and type-check result.
1603-
/// </summary>
1604-
/// <param name="sanitizedXaml">Xaml with single or double quots</param>
1605-
/// <param name="defaultXmlns">The default xmlns to inject; use null to not inject one.</param>
1606-
private T LoadXaml<T>(string sanitizedXaml, string defaultXmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation") where T : class =>
1607-
LoadXaml<T>(sanitizedXaml, new Dictionary<string, string> { [string.Empty] = defaultXmlns });
1608-
1609-
/// <summary>
1610-
/// XamlReader.Load the xaml and type-check result.
1611-
/// </summary>
1612-
/// <param name="sanitizedXaml">Xaml with single or double quots</param>
1613-
/// <param name="xmlnses">Xmlns to inject; use string.Empty for the default xmlns' key</param>
1614-
private T LoadXaml<T>(string xaml, Dictionary<string, string> xmlnses) where T : class
1615-
{
1616-
var injection = " " + string.Join(" ", xmlnses
1617-
.Where(x => x.Value != null)
1618-
.Select(x => $"xmlns{(string.IsNullOrEmpty(x.Key) ? "" : $":{x.Key}")}=\"{x.Value}\"")
1619-
);
1620-
1621-
xaml = new Regex(@"(?=\\?>)").Replace(xaml, injection, 1);
1622-
1623-
var result = Windows.UI.Xaml.Markup.XamlReader.Load(xaml);
1624-
Assert.IsNotNull(result, "XamlReader.Load returned null");
1625-
Assert.IsInstanceOfType(result, typeof(T), "XamlReader.Load did not return the expected type");
1626-
1627-
return (T)result;
1628-
}
1629-
16301609
private string GetContent(string testName)
16311610
{
16321611
var assembly = this.GetType().Assembly;
@@ -1637,5 +1616,76 @@ private string GetContent(string testName)
16371616
return stream.ReadToEnd();
16381617
}
16391618
}
1619+
1620+
private static class XamlHelper
1621+
{
1622+
/// <summary>
1623+
/// Matches right before the &gt; or \&gt; tail of any tag.
1624+
/// </summary>
1625+
/// <remarks>
1626+
/// It will match an opening or closing or self-closing tag.
1627+
/// </remarks>
1628+
private static readonly Regex EndOfTagRegex = new Regex(@"(?=( ?/)?>)");
1629+
1630+
/// <summary>
1631+
/// Matches any tag without xmlns prefix.
1632+
/// </summary>
1633+
private static readonly Regex NonXmlnsTagRegex = new Regex(@"<\w+[ />]");
1634+
1635+
private static readonly IReadOnlyDictionary<string, string> KnownXmlnses = new Dictionary<string, string>
1636+
{
1637+
[string.Empty] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation",
1638+
["x"] = "http://schemas.microsoft.com/winfx/2006/xaml",
1639+
["local"] = "Uno.UI.Tests.Windows_UI_Xaml_Markup.XamlReaderTests",
1640+
["toolkit"] = "using:Uno.UI.Toolkit",
1641+
["muxc"] = "using:Microsoft.UI.Xaml.Controls",
1642+
};
1643+
1644+
/// <summary>
1645+
/// XamlReader.Load the xaml and type-check result.
1646+
/// </summary>
1647+
/// <param name="xaml">Xaml with single or double quotes</param>
1648+
/// <param name="autoInjectXmlns">Toggle automatic detection of xmlns required and inject to the xaml</param>
1649+
public static T LoadXaml<T>(string xaml, bool autoInjectXmlns = true) where T : class
1650+
{
1651+
var xmlnses = new Dictionary<string, string>();
1652+
1653+
if (autoInjectXmlns)
1654+
{
1655+
foreach (var xmlns in KnownXmlnses)
1656+
{
1657+
var match = xmlns.Key == string.Empty
1658+
? NonXmlnsTagRegex.IsMatch(xaml)
1659+
: xaml.Contains($"<{xmlns.Key}:");
1660+
if (match)
1661+
{
1662+
xmlnses.Add(xmlns.Key, xmlns.Value);
1663+
}
1664+
}
1665+
}
1666+
1667+
return LoadXaml<T>(xaml, xmlnses);
1668+
}
1669+
1670+
/// <summary>
1671+
/// XamlReader.Load the xaml and type-check result.
1672+
/// </summary>
1673+
/// <param name="xaml">Xaml with single or double quotes</param>
1674+
/// <param name="xmlnses">Xmlns to inject; use string.Empty for the default xmlns' key</param>
1675+
public static T LoadXaml<T>(string xaml, Dictionary<string, string> xmlnses) where T : class
1676+
{
1677+
var injection = " " + string.Join(" ", xmlnses
1678+
.Select(x => $"xmlns{(string.IsNullOrEmpty(x.Key) ? "" : $":{x.Key}")}=\"{x.Value}\"")
1679+
);
1680+
1681+
xaml = EndOfTagRegex.Replace(xaml, injection.TrimEnd(), 1);
1682+
1683+
var result = Windows.UI.Xaml.Markup.XamlReader.Load(xaml);
1684+
Assert.IsNotNull(result, "XamlReader.Load returned null");
1685+
Assert.IsInstanceOfType(result, typeof(T), "XamlReader.Load did not return the expected type");
1686+
1687+
return (T)result;
1688+
}
1689+
}
16401690
}
16411691
}

src/Uno.UI/DataBinding/BindingPropertyHelper.FastConvert.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ private static bool FastStringConvert(Type outputType, string input, ref object
278278
return true;
279279
}
280280

281+
if (FastStringToRect(outputType, input, ref output))
282+
{
283+
return true;
284+
}
285+
281286
if (FastStringToMatrix(outputType, input, ref output))
282287
{
283288
return true;
@@ -592,6 +597,22 @@ private static bool FastStringToPoint(Type outputType, string input, ref object
592597
return false;
593598
}
594599

600+
private static bool FastStringToRect(Type outputType, string input, ref object output)
601+
{
602+
if (outputType == typeof(Windows.Foundation.Rect))
603+
{
604+
var fields = GetDoubleValues(input);
605+
606+
if (fields.Count == 4)
607+
{
608+
output = new Windows.Foundation.Rect(fields[0], fields[1], fields[2], fields[3]);
609+
return true;
610+
}
611+
}
612+
613+
return false;
614+
}
615+
595616
private static bool FastStringToBrushConvert(Type outputType, string input, ref object output)
596617
{
597618
if (outputType == typeof(Brush))

0 commit comments

Comments
 (0)