diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs index 8e5631e198..7df53f430f 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs @@ -46,6 +46,10 @@ namespace Avalonia.Markup.Parsers state = ParseIndexer(ref r, nodes); break; + case State.TypeCast: + state = ParseTypeCast(ref r, nodes); + break; + case State.ElementName: state = ParseElementName(ref r, nodes); mode = SourceMode.Control; @@ -124,6 +128,10 @@ namespace Avalonia.Markup.Parsers { return State.Indexer; } + else if (ParseOpenBrace(ref r)) + { + return State.TypeCast; + } return State.End; } @@ -186,6 +194,27 @@ namespace Avalonia.Markup.Parsers return State.AfterMember; } + private static State ParseTypeCast(ref CharacterReader r, List nodes) + { + var (ns, typeName) = ParseTypeName(ref r); + + nodes.Add(new TypeCastNode { Namespace = ns, TypeName = typeName }); + + if (ParseMemberAccessor(ref r)) + { + var identifier = r.ParseIdentifier(); + + nodes.Add(new PropertyNameNode { PropertyName = identifier.ToString() }); + } + + if (r.End || !r.TakeIf(')')) + { + throw new ExpressionParseException(r.Position, "Expected ')'."); + } + + return State.AfterMember; + } + private static State ParseElementName(ref CharacterReader r, List nodes) { var name = r.ParseIdentifier(); @@ -322,6 +351,7 @@ namespace Avalonia.Markup.Parsers BeforeMember, AttachedProperty, Indexer, + TypeCast, End, } @@ -383,5 +413,11 @@ namespace Avalonia.Markup.Parsers public string TypeName { get; set; } public int Level { get; set; } } + + public class TypeCastNode : INode + { + public string Namespace { get; set; } + public string TypeName { get; set; } + } } } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs index 1048148c1f..558130e23f 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs @@ -59,6 +59,9 @@ namespace Avalonia.Markup.Parsers case BindingExpressionGrammar.NameNode elementName: nextNode = new ElementNameNode(_nameScope, elementName.Name); break; + case BindingExpressionGrammar.TypeCastNode typeCast: + nextNode = ParseTypeCastNode(typeCast); + break; } if (rootNode is null) { @@ -92,6 +95,22 @@ namespace Avalonia.Markup.Parsers return new FindAncestorNode(ancestorType, ancestorLevel); } + private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node) + { + Type castType = null; + if (!(node.Namespace is null) && !(node.TypeName is null)) + { + if (_typeResolver == null) + { + throw new InvalidOperationException("Cannot parse a binding path with a typed Cast without a type resolver. Maybe you can use a LINQ Expression binding path instead?"); + } + + castType = _typeResolver(node.Namespace, node.TypeName); + } + + return new TypeCastNode(castType); + } + private AvaloniaPropertyAccessorNode ParseAttachedProperty(BindingExpressionGrammar.AttachedPropertyNameNode node) { if (_typeResolver == null)