diff --git a/src/System.Xaml/System/Xaml/Context/XamlParserContext.cs b/src/System.Xaml/System/Xaml/Context/XamlParserContext.cs
index ef20b24da..6c90bc10c 100644
--- a/src/System.Xaml/System/Xaml/Context/XamlParserContext.cs
+++ b/src/System.Xaml/System/Xaml/Context/XamlParserContext.cs
@@ -289,6 +289,12 @@ public XamlType CurrentPreviousChildType
set { _stack.CurrentFrame.PreviousChildType = value; }
}
+ public bool CurrentContentPropertyAssigned
+ {
+ get { return _stack.CurrentFrame.ContentPropertyAssigned; }
+ set { _stack.CurrentFrame.ContentPropertyAssigned = value; }
+ }
+
public bool CurrentMemberIsWriteVisible()
{
Type allowProtectedForType = null;
diff --git a/src/System.Xaml/System/Xaml/Context/XamlParserFrame.cs b/src/System.Xaml/System/Xaml/Context/XamlParserFrame.cs
index 6b50ddca0..831b58d6a 100644
--- a/src/System.Xaml/System/Xaml/Context/XamlParserFrame.cs
+++ b/src/System.Xaml/System/Xaml/Context/XamlParserFrame.cs
@@ -20,13 +20,15 @@ public override void Reset()
InCollectionFromMember = false;
InImplicitArray = false;
InContainerDirective = false;
+ ContentPropertyAssigned = false;
TypeNamespace = null;
LongestConstructorOfCurrentMarkupExtensionType = null;
EscapeCharacterMapForMarkupExtension = null;
BracketModeParseParameters = null;
}
-
+
public XamlType PreviousChildType { get; set; }
+ public bool ContentPropertyAssigned { get; set; }
public int CtorArgCount { get; set; }
public bool ForcedToUseConstructor { get; set; }
public bool InCollectionFromMember { get; set; }
diff --git a/src/System.Xaml/System/Xaml/Parser/XamlPullParser.cs b/src/System.Xaml/System/Xaml/Parser/XamlPullParser.cs
index 187c7de0c..64a01683b 100644
--- a/src/System.Xaml/System/Xaml/Parser/XamlPullParser.cs
+++ b/src/System.Xaml/System/Xaml/Parser/XamlPullParser.cs
@@ -856,6 +856,7 @@ private XamlNode Logic_StartContentProperty(XamlMember property)
property = XamlLanguage.UnknownContent;
}
_context.CurrentMember = property;
+ _context.CurrentContentPropertyAssigned = true;
var startProperty = new XamlNode(XamlNodeType.StartMember, property);
// SetLineInfo(startProperty); // No line number info for objects from members.
return startProperty;
@@ -1131,7 +1132,6 @@ private bool Logic_IsDiscardableWhitespace(XamlText text)
// Theoretically we'd also like to support all type-convertible CPs.
// However, for non-string CPs, 3.0 only surfaced whitespace as text if
// the CP hadn't already been set. For string, it surfaced it in all cases.
- // So to avoid a breaking change, we only surface string right now.
if (prop.Type == XamlLanguage.String)
{
return false;
@@ -1140,6 +1140,12 @@ private bool Logic_IsDiscardableWhitespace(XamlText text)
{
return false;
}
+ if (!_context.CurrentContentPropertyAssigned
+ && _context.CurrentType.TypeConverter != null
+ && !_context.CurrentForcedToUseConstructor)
+ {
+ return false;
+ }
}
// ...it's in a type-convertible element
else if (_context.CurrentType.TypeConverter != null && !_context.CurrentForcedToUseConstructor)
diff --git a/src/Test/TestCases.Workflows/XamlTests.cs b/src/Test/TestCases.Workflows/XamlTests.cs
index 4a7e4c8a6..8c55cf225 100644
--- a/src/Test/TestCases.Workflows/XamlTests.cs
+++ b/src/Test/TestCases.Workflows/XamlTests.cs
@@ -149,6 +149,55 @@ public void XamlWorkflowWithInputsOutputs()
outputs["myOutput"].ShouldBe(1);
}
+#if !WINDOWS
+ // The net6.0-windows build uses the WPF System.Xaml, which drops
+ // whitespace-only argument values; only the local System.Xaml keeps them.
+ [Theory]
+ [InlineData(" ")]
+ [InlineData(" ")]
+ [InlineData("\t")]
+ public void WhitespaceOnlyArgumentValueIsPreserved(string value)
+ {
+ var xamlString = $@"
+
+
+
+
+
+
+ [myOutput]
+
+
+ {value}
+
+
+ ";
+ var outputs = InvokeWorkflow(xamlString);
+ outputs["myOutput"].ShouldBe(value);
+ }
+#endif
+
+ [Fact]
+ public void WhitespaceAroundExpressionElementIsIgnoredWhenSpaceIsPreserved()
+ {
+ var xamlString = @"
+
+
+
+
+ ""constant""
+
+
+
+ ";
+ Load(xamlString).ShouldNotBeNull();
+ }
+
[Fact]
public void XamlWorkflowWithInputObject()
{