зеркало из https://github.com/DeGsoft/maui-linux.git
[Xaml] Create value from positional argument text of markup extension (#8980)
The behavior introduced with this change conforms to the description of [MS-XAML-2009] page 80 and 81. fixes #6905
This commit is contained in:
Родитель
65c6860fc3
Коммит
fee5faaca8
|
@ -143,11 +143,11 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
}
|
||||
else
|
||||
{
|
||||
char next;
|
||||
string piece;
|
||||
while ((piece = GetNextPiece(serviceProvider, ref remaining, out next)) != null)
|
||||
Property parsed;
|
||||
do
|
||||
{
|
||||
var parsed = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
|
||||
parsed = ParseProperty(serviceProvider, ref remaining);
|
||||
|
||||
XmlName childname;
|
||||
|
||||
if (parsed.name == null)
|
||||
|
@ -177,6 +177,7 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
childnodes.Add((childname, childnode));
|
||||
}
|
||||
}
|
||||
while (!parsed.last);
|
||||
}
|
||||
|
||||
//The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
{
|
||||
IXamlTypeResolver typeResolver;
|
||||
|
||||
public static readonly string Foo = "Foo";
|
||||
|
||||
class MockElementNode : IElementNode, IValueNode, IXmlLineInfo
|
||||
{
|
||||
public bool HasLineInfo () { return false; }
|
||||
|
@ -93,11 +95,10 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
Assert.AreEqual (Binding.SelfPath, ((Binding)binding).Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindingWithImplicitPath ()
|
||||
[TestCase("{Binding Foo}")]
|
||||
[TestCase("{Binding {x:Static local:MarkupExpressionParserTests.Foo}}")]
|
||||
public void BindingWithImplicitPath (string bindingString)
|
||||
{
|
||||
var bindingString = "{Binding Foo}";
|
||||
|
||||
var binding = (new MarkupExtensionParser ()).ParseExpression (ref bindingString, new Internals.XamlServiceProvider (null, null) {
|
||||
IXamlTypeResolver = typeResolver,
|
||||
});
|
||||
|
@ -377,6 +378,8 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
|
||||
[TestCase("{Binding")]
|
||||
[TestCase("{Binding 'Foo}")]
|
||||
[TestCase("{Binding Foo, Converter={StaticResource Bar}")]
|
||||
[TestCase("{Binding Foo, Converter={StaticResource Bar}?}")]
|
||||
public void InvalidExpressions (string expression)
|
||||
{
|
||||
var serviceProvider = new Internals.XamlServiceProvider (null, null);
|
||||
|
|
|
@ -131,10 +131,9 @@ namespace Xamarin.Forms.Xaml
|
|||
remaining = remaining.Substring(1);
|
||||
}
|
||||
else {
|
||||
char next;
|
||||
string piece;
|
||||
while ((piece = GetNextPiece(serviceProvider, ref remaining, out next)) != null) {
|
||||
var parsed = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
|
||||
Property parsed;
|
||||
do {
|
||||
parsed = ParseProperty(serviceProvider, ref remaining);
|
||||
XmlName childname;
|
||||
|
||||
if (parsed.name == null) {
|
||||
|
@ -160,6 +159,7 @@ namespace Xamarin.Forms.Xaml
|
|||
childnodes.Add((childname, childnode));
|
||||
}
|
||||
}
|
||||
while (!parsed.last);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
protected struct Property
|
||||
{
|
||||
public bool last;
|
||||
public string name;
|
||||
public string strValue;
|
||||
public object value;
|
||||
|
@ -119,50 +120,56 @@ namespace Xamarin.Forms.Xaml
|
|||
return true;
|
||||
}
|
||||
|
||||
protected Property ParseProperty(string prop, IServiceProvider serviceProvider, ref string remaining, bool isImplicit)
|
||||
protected Property ParseProperty(IServiceProvider serviceProvider, ref string remaining)
|
||||
{
|
||||
char next;
|
||||
object value = null;
|
||||
string str_value;
|
||||
string name;
|
||||
|
||||
if (isImplicit)
|
||||
{
|
||||
return new Property { name = null, strValue = prop, value = null };
|
||||
}
|
||||
remaining = remaining.TrimStart();
|
||||
if (remaining.StartsWith("{", StringComparison.Ordinal))
|
||||
{
|
||||
value = ParseExpression(ref remaining, serviceProvider);
|
||||
if (remaining[0] == '{')
|
||||
return ParsePropertyExpression(null, serviceProvider, ref remaining);
|
||||
|
||||
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
|
||||
if (next == '=') {
|
||||
remaining = remaining.TrimStart();
|
||||
if (remaining[0] == '{')
|
||||
return ParsePropertyExpression(str_value, serviceProvider, ref remaining);
|
||||
|
||||
if (remaining.Length > 0 && remaining[0] == ',')
|
||||
remaining = remaining.Substring(1);
|
||||
else if (remaining.Length > 0 && remaining[0] == '}')
|
||||
remaining = remaining.Substring(1);
|
||||
|
||||
str_value = value as string;
|
||||
name = str_value;
|
||||
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
|
||||
}
|
||||
else {
|
||||
str_value = GetNextPiece(serviceProvider, ref remaining, out next);
|
||||
if (str_value == null) {
|
||||
throw new XamlParseException($"No value found for property '{prop}' in markup expression", serviceProvider);
|
||||
}
|
||||
name = null;
|
||||
}
|
||||
|
||||
return new Property { name = prop, strValue = str_value, value = value };
|
||||
return new Property { last = next == '}', name = name, strValue = str_value, value = value };
|
||||
}
|
||||
|
||||
protected string GetNextPiece(IServiceProvider serviceProvider, ref string remaining, out char next)
|
||||
private Property ParsePropertyExpression(string prop, IServiceProvider serviceProvider, ref string remaining)
|
||||
{
|
||||
bool last;
|
||||
var value = ParseExpression(ref remaining, serviceProvider);
|
||||
remaining = remaining.TrimStart();
|
||||
if (remaining.Length <= 0)
|
||||
throw new XamlParseException("Unexpected end of markup expression", serviceProvider);
|
||||
if (remaining[0] == ',')
|
||||
last = false;
|
||||
else if (remaining[0] == '}')
|
||||
last = true;
|
||||
else
|
||||
throw new XamlParseException("Unexpected character following value string", serviceProvider);
|
||||
|
||||
remaining = remaining.Substring(1);
|
||||
return new Property { last = last, name = prop, strValue = value as string, value = value };
|
||||
}
|
||||
|
||||
private string GetNextPiece(IServiceProvider serviceProvider, ref string remaining, out char next)
|
||||
{
|
||||
bool inString = false;
|
||||
int end = 0;
|
||||
char stringTerminator = '\0';
|
||||
remaining = remaining.TrimStart();
|
||||
if (remaining.Length == 0)
|
||||
{
|
||||
next = Char.MaxValue;
|
||||
return null;
|
||||
}
|
||||
|
||||
var piece = new StringBuilder();
|
||||
// If we're inside a quoted string we append all chars to our piece until we hit the ending quote.
|
||||
|
@ -203,14 +210,8 @@ namespace Xamarin.Forms.Xaml
|
|||
if (inString && end == remaining.Length)
|
||||
throw new XamlParseException("Unterminated quoted string", serviceProvider);
|
||||
|
||||
if (end == remaining.Length && !remaining.EndsWith("}", StringComparison.Ordinal))
|
||||
throw new XamlParseException("Expression did not end with '}'", serviceProvider);
|
||||
|
||||
if (end == 0)
|
||||
{
|
||||
next = Char.MaxValue;
|
||||
return null;
|
||||
}
|
||||
throw new XamlParseException("Empty value string in markup expression", serviceProvider);
|
||||
|
||||
next = remaining[end];
|
||||
remaining = remaining.Substring(end + 1);
|
||||
|
|
|
@ -42,11 +42,12 @@ namespace Xamarin.Forms.Xaml
|
|||
if (remaining == "}")
|
||||
return markupExtension.ProvideValue(serviceProvider);
|
||||
|
||||
string piece;
|
||||
while ((piece = GetNextPiece(serviceProvider, ref remaining, out char next)) != null) {
|
||||
var value = ParseProperty(piece, serviceProvider, ref remaining, next != '=');
|
||||
Property value;
|
||||
do {
|
||||
value = ParseProperty(serviceProvider, ref remaining);
|
||||
SetPropertyValue(value.name, value.strValue, value.value, serviceProvider);
|
||||
}
|
||||
while (!value.last);
|
||||
|
||||
return markupExtension.ProvideValue(serviceProvider);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче