public void Visit(ValueNode node, INode parentNode) { XmlName propertyName; if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName)) { if (!IsCollectionItem(node, parentNode)) return; string contentProperty; if (!Context.Variables.ContainsKey((IElementNode)parentNode)) return; var parentVar = Context.Variables[(IElementNode)parentNode]; if ((contentProperty = SetPropertiesVisitor.GetContentProperty(parentVar.VariableType)) != null) propertyName = new XmlName(((IElementNode)parentNode).NamespaceURI, contentProperty); else return; } if (node.SkipPrefix((node.NamespaceResolver ?? parentNode.NamespaceResolver)?.LookupPrefix(propertyName.NamespaceURI))) return; if (propertyName.NamespaceURI == "http://schemas.openxmlformats.org/markup-compatibility/2006" && propertyName.LocalName == "Ignorable") { (parentNode.IgnorablePrefixes ?? (parentNode.IgnorablePrefixes = new List<string>())).AddRange( (node.Value as string).Split(',')); return; } if (propertyName.LocalName != "MergedWith") return; SetPropertiesVisitor.SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node); }
static IList <KeyValuePair <XmlName, INode> > ParseXamlAttributes(XmlReader reader, out IList <KeyValuePair <string, string> > xmlns) { Debug.Assert(reader.NodeType == XmlNodeType.Element); var attributes = new List <KeyValuePair <XmlName, INode> >(); xmlns = new List <KeyValuePair <string, string> >(); for (var i = 0; i < reader.AttributeCount; i++) { reader.MoveToAttribute(i); //skip xmlns if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/") { xmlns.Add(new KeyValuePair <string, string>(reader.LocalName, reader.Value)); continue; } var namespaceUri = reader.NamespaceURI; if (reader.LocalName.Contains(".") && namespaceUri == "") { namespaceUri = ((IXmlNamespaceResolver)reader).LookupNamespace(""); } var propertyName = new XmlName(namespaceUri, reader.LocalName); object value = reader.Value; if (reader.NamespaceURI == X2006Uri) { switch (reader.LocalName) { case "Key": propertyName = XmlName.xKey; break; case "Name": propertyName = XmlName.xName; break; case "Class": case "FieldModifier": continue; default: Debug.WriteLine("Unhandled attribute {0}", reader.Name); continue; } } if (reader.NamespaceURI == X2009Uri) { switch (reader.LocalName) { case "Key": propertyName = XmlName.xKey; break; case "Name": propertyName = XmlName.xName; break; case "TypeArguments": propertyName = XmlName.xTypeArguments; value = TypeArgumentsParser.ParseExpression((string)value, (IXmlNamespaceResolver)reader, (IXmlLineInfo)reader); break; case "DataType": propertyName = XmlName.xDataType; break; case "Class": case "FieldModifier": continue; case "FactoryMethod": propertyName = XmlName.xFactoryMethod; break; case "Arguments": propertyName = XmlName.xArguments; break; default: Debug.WriteLine("Unhandled attribute {0}", reader.Name); continue; } } var propertyNode = GetValueNode(value, reader); attributes.Add(new KeyValuePair <XmlName, INode>(propertyName, propertyNode)); } reader.MoveToElement(); return(attributes); }
static void ParseXamlElementFor(IElementNode node, XmlReader reader) { Debug.Assert(reader.NodeType == XmlNodeType.Element); var elementName = reader.Name; var isEmpty = reader.IsEmptyElement; if (isEmpty) { return; } while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.EndElement: Debug.Assert(reader.Name == elementName); //make sure we close the right element return; case XmlNodeType.Element: // 1. Property Element. if (reader.Name.Contains(".")) { XmlName name; if (reader.Name.StartsWith(elementName + ".", StringComparison.Ordinal)) { name = new XmlName(reader.NamespaceURI, reader.Name.Substring(elementName.Length + 1)); } else //Attached BP { name = new XmlName(reader.NamespaceURI, reader.LocalName); } if (node.Properties.ContainsKey(name)) { throw new XamlParseException($"'{reader.Name}' is a duplicate property name.", (IXmlLineInfo)reader); } INode prop = null; if (reader.IsEmptyElement) { Debug.WriteLine($"Unexpected empty element '<{reader.Name} />'", (IXmlLineInfo)reader); } else { prop = ReadNode(reader); } if (prop != null) { node.Properties.Add(name, prop); } } // 2. Xaml2009 primitives, x:Arguments, ... else if (reader.NamespaceURI == X2009Uri && reader.LocalName == "Arguments") { if (node.Properties.ContainsKey(XmlName.xArguments)) { throw new XamlParseException($"'x:Arguments' is a duplicate directive name.", (IXmlLineInfo)reader); } var prop = ReadNode(reader); if (prop != null) { node.Properties.Add(XmlName.xArguments, prop); } } // 3. DataTemplate (should be handled by 4.) else if (node.XmlType.NamespaceUri == XFUri && (node.XmlType.Name == "DataTemplate" || node.XmlType.Name == "ControlTemplate")) { if (node.Properties.ContainsKey(XmlName._CreateContent)) { throw new XamlParseException($"Multiple child elements in {node.XmlType.Name}", (IXmlLineInfo)reader); } var prop = ReadNode(reader, true); if (prop != null) { node.Properties.Add(XmlName._CreateContent, prop); } } // 4. Implicit content, implicit collection, or collection syntax. Add to CollectionItems, resolve case later. else { var item = ReadNode(reader, true); if (item != null) { node.CollectionItems.Add(item); } } break; case XmlNodeType.Whitespace: break; case XmlNodeType.Text: case XmlNodeType.CDATA: if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode) { ((ValueNode)node.CollectionItems[0]).Value += reader.Value.Trim(); } else { node.CollectionItems.Add(new ValueNode(reader.Value.Trim(), (IXmlNamespaceResolver)reader)); } break; default: Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value); break; } } }
public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydrationContext context, IXmlLineInfo lineInfo) { var localName = propertyName.LocalName; var serviceProvider = new XamlServiceProvider(node, context); Exception xpe = null; var xKey = node is IElementNode && ((IElementNode)node).Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)((IElementNode)node).Properties[XmlName.xKey]).Value as string : null; //If it's an attached BP, update elementType and propertyName var bpOwnerType = xamlelement.GetType(); var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo); var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false); //If the target is an event, connect if (xpe == null && TryConnectEvent(xamlelement, localName, attached, value, rootElement, lineInfo, out xpe)) { return; } //If Value is DynamicResource and it's a BP, SetDynamicResource if (xpe == null && TrySetDynamicResource(xamlelement, property, value, lineInfo, out xpe)) { return; } //If value is BindingBase, SetBinding if (xpe == null && TrySetBinding(xamlelement, property, localName, value, lineInfo, out xpe)) { return; } //If it's a BindableProberty, SetValue if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe)) { VisualDiagnostics.RegisterSourceInfo(value, null, ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition); VisualDiagnostics.SendVisualTreeChanged(xamlelement, value); return; } //If we can assign that value to a normal property, let's do it if (xpe == null && TrySetProperty(xamlelement, localName, value, lineInfo, serviceProvider, context, out xpe)) { VisualDiagnostics.RegisterSourceInfo(value, null, ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition); VisualDiagnostics.SendVisualTreeChanged(xamlelement, value); return; } //If it's an already initialized property, add to it if (xpe == null && TryAddToProperty(xamlelement, propertyName, value, xKey, lineInfo, serviceProvider, context, out xpe)) { VisualDiagnostics.RegisterSourceInfo(value, null, ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition); VisualDiagnostics.SendVisualTreeChanged(xamlelement, value); return; } xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exist, or is not assignable, or mismatching type between value and property", lineInfo); if (context.ExceptionHandler != null) { context.ExceptionHandler(xpe); } else { throw xpe; } }
public void Visit(ElementNode node, INode parentNode) { XmlName propertyName; if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent) { var s0 = Values[parentNode]; if (s0 is ElementTemplate) { SetTemplate(s0 as ElementTemplate, node); return; } } var parentElement = parentNode as IElementNode; propertyName = XmlName.Empty; //Simplify ListNodes with single elements var pList = parentNode as ListNode; if (pList != null && pList.CollectionItems.Count == 1) { propertyName = pList.XmlName; parentNode = parentNode.Parent; parentElement = parentNode as IElementNode; } if (!Values.TryGetValue(node, out var value) && Context.ExceptionHandler != null) { return; } if (propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) { if (Skips.Contains(propertyName)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } if (!Values.TryGetValue(parentNode, out var source) && Context.ExceptionHandler != null) { return; } ProvideValue(ref value, node, source, propertyName); SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node); } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) { if (!Values.TryGetValue(parentNode, out var source) && Context.ExceptionHandler != null) { return; } ProvideValue(ref value, node, source, XmlName.Empty); string contentProperty; Exception xpe = null; var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null; //ResourceDictionary if (xpe == null && TryAddToResourceDictionary(source as ResourceDictionary, value, xKey, node, out xpe)) { return; } // Collection element, implicit content, or implicit collection element. if (xpe == null && typeof(IEnumerable).IsAssignableFrom(Context.Types[parentElement]) && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) { var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1); addMethod.Invoke(source, new[] { value }); return; } if (xpe == null && (contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null) { var name = new XmlName(node.NamespaceURI, contentProperty); if (Skips.Contains(name)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } SetPropertyValue(source, name, value, Context.RootElement, node, Context, node); return; } xpe = xpe ?? new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node); if (Context.ExceptionHandler != null) { Context.ExceptionHandler(xpe); } else { throw xpe; } } else if (IsCollectionItem(node, parentNode) && parentNode is ListNode) { if (!Values.TryGetValue(parentNode.Parent, out var source) && Context.ExceptionHandler != null) { return; } ProvideValue(ref value, node, source, XmlName.Empty); var parentList = (ListNode)parentNode; if (Skips.Contains(parentList.XmlName)) { return; } Exception xpe = null; var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null; var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _, out _) as IEnumerable; if (collection == null) { xpe = new XamlParseException($"Property {parentList.XmlName.LocalName} is null or is not IEnumerable", node); } if (xpe == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, node, out xpe)) { return; } MethodInfo addMethod; if (xpe == null && (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) != null) { addMethod.Invoke(collection, new[] { value }); return; } xpe = xpe ?? new XamlParseException($"Value of {parentList.XmlName.LocalName} does not have a Add() method", node); if (Context.ExceptionHandler != null) { Context.ExceptionHandler(xpe); } else { throw xpe; } } }
static void ParseXamlElementFor(IElementNode node, XmlReader reader) { Debug.Assert(reader.NodeType == XmlNodeType.Element); var elementName = reader.Name; var isEmpty = reader.IsEmptyElement; if (isEmpty) { return; } while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.EndElement: Debug.Assert(reader.Name == elementName); //make sure we close the right element return; case XmlNodeType.Element: // 1. Property Element. if (reader.Name.Contains(".")) { XmlName name; if (reader.Name.StartsWith(elementName + ".", StringComparison.Ordinal)) { name = new XmlName(reader.NamespaceURI, reader.Name.Substring(elementName.Length + 1)); } else //Attached DP { name = new XmlName(reader.NamespaceURI, reader.LocalName); } var prop = ReadNode(reader); if (prop != null) { node.Properties.Add(name, prop); } } // 2. Xaml2009 primitives, x:Arguments, ... else if (reader.NamespaceURI == "http://schemas.microsoft.com/winfx/2009/xaml" && reader.LocalName == "Arguments") { var prop = ReadNode(reader); if (prop != null) { node.Properties.Add(XmlName.xArguments, prop); } } // 3. DataTemplate (should be handled by 4.) else if (node.XmlType.NamespaceUri == "http://xamarin.com/schemas/2014/forms" && (node.XmlType.Name == "DataTemplate" || node.XmlType.Name == "ControlTemplate")) { var prop = ReadNode(reader, true); if (prop != null) { node.Properties.Add(XmlName._CreateContent, prop); } } // 4. Implicit content, implicit collection, or collection syntax. Add to CollectionItems, resolve case later. else { var item = ReadNode(reader, true); if (item != null) { node.CollectionItems.Add(item); } } break; case XmlNodeType.Whitespace: break; case XmlNodeType.Text: case XmlNodeType.CDATA: if (node.CollectionItems.Count == 1 && node.CollectionItems[0] is ValueNode) { ((ValueNode)node.CollectionItems[0]).Value += reader.Value.Trim(); } else { node.CollectionItems.Add(new ValueNode(reader.Value.Trim(), (IXmlNamespaceResolver)reader)); } break; default: Debug.WriteLine("Unhandled node {0} {1} {2}", reader.NodeType, reader.Name, reader.Value); break; } } }
public void Visit(ElementNode node, INode parentNode) { XmlName propertyName; if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent) { var s0 = Values[parentNode]; if (s0 is ElementTemplate) { SetTemplate(s0 as ElementTemplate, node); return; } } var parentElement = parentNode as IElementNode; propertyName = XmlName.Empty; //Simplify ListNodes with single elements var pList = parentNode as ListNode; if (pList != null && pList.CollectionItems.Count == 1) { propertyName = pList.XmlName; parentNode = parentNode.Parent; parentElement = parentNode as IElementNode; } var value = Values[node]; if (propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) { if (Skips.Contains(propertyName)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } var source = Values[parentNode]; ProvideValue(ref value, node, source, propertyName); SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node); } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) { var source = Values[parentNode]; ProvideValue(ref value, node, source, XmlName.Empty); string contentProperty; // Implicit Style Resource in a ResourceDictionary if (typeof(ResourceDictionary).IsAssignableFrom(Context.Types[parentElement]) && value is Style && !node.Properties.ContainsKey(XmlName.xKey)) { ((ResourceDictionary)source).Add(value as Style); } // Resource without a x:Key in a ResourceDictionary else if (typeof(ResourceDictionary).IsAssignableFrom(Context.Types[parentElement]) && !node.Properties.ContainsKey(XmlName.xKey)) { throw new XamlParseException("resources in ResourceDictionary require a x:Key attribute", node); } // Resource in a ResourceDictionary else if (typeof(ResourceDictionary).IsAssignableFrom(Context.Types[parentElement]) && node.Properties.ContainsKey(XmlName.xKey)) { ((ResourceDictionary)source).Add((string)(((ValueNode)node.Properties[XmlName.xKey]).Value), value); } // Collection element, implicit content, or implicit collection element. else if (typeof(IEnumerable).IsAssignableFrom(Context.Types[parentElement]) && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) { if (!(typeof(ResourceDictionary).IsAssignableFrom(Context.Types[parentElement]))) { var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1); addMethod.Invoke(source, new[] { value }); } } else if ((contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null) { var name = new XmlName(node.NamespaceURI, contentProperty); if (Skips.Contains(name)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } SetPropertyValue(source, name, value, Context.RootElement, node, Context, node); } else { throw new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node); } } else if (IsCollectionItem(node, parentNode) && parentNode is ListNode) { var source = Values[parentNode.Parent]; ProvideValue(ref value, node, source, XmlName.Empty); var parentList = (ListNode)parentNode; if (Skips.Contains(parentList.XmlName)) { return; } var elementType = source.GetType(); var localname = parentList.XmlName.LocalName; GetRealNameAndType(ref elementType, parentList.XmlName.NamespaceURI, ref localname, Context, node); PropertyInfo propertyInfo = null; try { propertyInfo = elementType.GetRuntimeProperty(localname); } catch (AmbiguousMatchException) { // Get most derived instance of property foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localname)) { if (propertyInfo == null || propertyInfo.DeclaringType.IsAssignableFrom(property.DeclaringType)) { propertyInfo = property; } } } if (propertyInfo == null) { throw new XamlParseException(Format("Property {0} not found", localname), node); } MethodInfo getter; if (!propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null) { throw new XamlParseException(Format("Property {0} does not have an accessible getter", localname), node); } IEnumerable collection; if ((collection = getter.Invoke(source, new object[] { }) as IEnumerable) == null) { throw new XamlParseException(Format("Property {0} is null or is not IEnumerable", localname), node); } MethodInfo addMethod; if ( (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) == null) { throw new XamlParseException(Format("Value of {0} does not have a Add() method", localname), node); } addMethod.Invoke(collection, new[] { Values[node] }); } }
public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydratationContext context, IXmlLineInfo lineInfo) { var elementType = xamlelement.GetType(); var localname = propertyName.LocalName; var serviceProvider = new XamlServiceProvider(node, context); //If it's an attached BP, update elementType and propertyName var attached = GetRealNameAndType(ref elementType, propertyName.NamespaceURI, ref localname, context, lineInfo); //If the target is an event, connect var eventInfo = elementType.GetRuntimeEvent(localname); if (eventInfo != null && value is string) { var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value); if (methodInfo == null) { var xpe = new XamlParseException(string.Format("No method {0} found on type {1}", value, rootElement.GetType()), lineInfo); if (context.DoNotThrowOnExceptions) { System.Diagnostics.Debug.WriteLine(xpe.Message); return; } else { throw xpe; } } try { eventInfo.AddEventHandler(xamlelement, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement)); } catch (ArgumentException) { var xpe = new XamlParseException(string.Format("Method {0} does not have the correct signature", value), lineInfo); if (context.DoNotThrowOnExceptions) { System.Diagnostics.Debug.WriteLine(xpe.Message); } else { throw xpe; } } return; } var property = GetBindableProperty(elementType, localname, lineInfo, false); //If Value is DynamicResource and it's a BP, SetDynamicResource if (value is DynamicResource && property != null) { if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof(BindableObject))) { throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo); } ((BindableObject)xamlelement).SetDynamicResource(property, ((DynamicResource)value).Key); return; } //If value is BindingBase, and target is a BindableProperty, SetBinding if (value is BindingBase && property != null) { if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof(BindableObject))) { throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo); } ((BindableObject)xamlelement).SetBinding(property, value as BindingBase); return; } //If it's a BindableProberty, SetValue if (property != null) { if (!(xamlelement.GetType()).GetTypeInfo().IsSubclassOf(typeof(BindableObject))) { throw new XamlParseException(string.Format("{0} is not a BindableObject", xamlelement.GetType().Name), lineInfo); } Func <MemberInfo> minforetriever; if (attached) { minforetriever = () => elementType.GetRuntimeMethod("Get" + localname, new[] { typeof(BindableObject) }); } else { minforetriever = () => elementType.GetRuntimeProperty(localname); } var convertedValue = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider); //SetValue doesn't throw on mismatching type, so check before to get a chance to try the property setting or the collection adding var nullable = property.ReturnTypeInfo.IsGenericType && property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable <>); if ((convertedValue == null && (!property.ReturnTypeInfo.IsValueType || nullable)) || (property.ReturnType.IsInstanceOfType(convertedValue))) { ((BindableObject)xamlelement).SetValue(property, convertedValue); return; } } var exception = new XamlParseException( String.Format("No Property of name {0} found", propertyName.LocalName), lineInfo); //If we can assign that value to a normal property, let's do it var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localname); MethodInfo setter; if (propertyInfo != null && propertyInfo.CanWrite && (setter = propertyInfo.SetMethod) != null) { object convertedValue = value.ConvertTo(propertyInfo.PropertyType, () => propertyInfo, serviceProvider); if (convertedValue == null || propertyInfo.PropertyType.IsInstanceOfType(convertedValue)) { try { setter.Invoke(xamlelement, new[] { convertedValue }); return; } catch (ArgumentException) { } } else { exception = new XamlParseException( String.Format("Cannot assign property \"{0}\": type mismatch between \"{1}\" and \"{2}\"", propertyName.LocalName, value.GetType(), propertyInfo.PropertyType), lineInfo); } } //If it's an already initialized property, add to it MethodInfo getter; if (propertyInfo != null && propertyInfo.CanRead && (getter = propertyInfo.GetMethod) != null) { IEnumerable collection; MethodInfo addMethod; if ((collection = getter.Invoke(xamlelement, new object[] { }) as IEnumerable) != null && (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) != null) { addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func <TypeConverter>)null, serviceProvider) }); return; } } if (context.DoNotThrowOnExceptions) { System.Diagnostics.Debug.WriteLine(exception.Message); } else { throw exception; } }
public void Visit(ElementNode node, INode parentNode) { var value = Values[node]; var parentElement = parentNode as IElementNode; var markupExtension = value as IMarkupExtension; var valueProvider = value as IValueProvider; if (markupExtension != null) { var serviceProvider = new XamlServiceProvider(node, Context); value = markupExtension.ProvideValue(serviceProvider); } if (valueProvider != null) { var serviceProvider = new XamlServiceProvider(node, Context); value = valueProvider.ProvideValue(serviceProvider); } XmlName propertyName = XmlName.Empty; //Simplify ListNodes with single elements var pList = parentNode as ListNode; if (pList != null && pList.CollectionItems.Count == 1) { propertyName = pList.XmlName; parentNode = parentNode.Parent; parentElement = parentNode as IElementNode; } if (propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) { if (Skips.Contains(propertyName)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } var source = Values [parentNode]; if (propertyName == XmlName._CreateContent && source is ElementTemplate) { SetTemplate(source as ElementTemplate, node); } else { SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node); } } else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) { // Collection element, implicit content, or implicit collection element. string contentProperty; if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(Context.Types[parentElement].GetTypeInfo())) { var source = Values[parentNode]; if (!(typeof(ResourceDictionary).IsAssignableFrom(Context.Types[parentElement]))) { var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1); addMethod.Invoke(source, new[] { value }); } } else if ((contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null) { var name = new XmlName(node.NamespaceURI, contentProperty); if (Skips.Contains(name)) { return; } if (parentElement.SkipProperties.Contains(propertyName)) { return; } var source = Values[parentNode]; SetPropertyValue(source, name, value, Context.RootElement, node, Context, node); } } else if (IsCollectionItem(node, parentNode) && parentNode is ListNode) { var parentList = (ListNode)parentNode; var source = Values[parentNode.Parent]; if (Skips.Contains(parentList.XmlName)) { return; } var elementType = source.GetType(); var localname = parentList.XmlName.LocalName; GetRealNameAndType(ref elementType, parentList.XmlName.NamespaceURI, ref localname, Context, node); PropertyInfo propertyInfo = null; try { propertyInfo = elementType.GetRuntimeProperty(localname); } catch (AmbiguousMatchException) { // Get most derived instance of property foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localname)) { if (propertyInfo == null || propertyInfo.DeclaringType.IsAssignableFrom(property.DeclaringType)) { propertyInfo = property; } } } if (propertyInfo == null) { throw new XamlParseException(string.Format("Property {0} not found", localname), node); } MethodInfo getter; if (!propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null) { throw new XamlParseException(string.Format("Property {0} does not have an accessible getter", localname), node); } IEnumerable collection; if ((collection = getter.Invoke(source, new object[] { }) as IEnumerable) == null) { throw new XamlParseException(string.Format("Property {0} is null or is not IEnumerable", localname), node); } MethodInfo addMethod; if ( (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) == null) { throw new XamlParseException(string.Format("Value of {0} does not have a Add() method", localname), node); } addMethod.Invoke(collection, new[] { Values[node] }); } }
//Used by HotReload, do not change signature public static bool TrySetPropertyValue(object element, XmlName propertyName, string xKey, object value, object rootElement, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, out Exception xpe) { var localName = propertyName.LocalName; xpe = null; void registerSourceInfo(object target, string path) { var assemblyName = rootElement.GetType().GetTypeInfo().Assembly?.GetName().Name; if (lineInfo != null) { VisualDiagnostics.RegisterSourceInfo(target, new Uri($"{path};assembly={assemblyName}", UriKind.Relative), lineInfo.LineNumber, lineInfo.LinePosition); } } //If it's an attached BP, update elementType and propertyName var bpOwnerType = element.GetType(); var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, rootElement, lineInfo); var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false); //If the target is an event, connect if (xpe == null && TryConnectEvent(element, localName, attached, value, rootElement, lineInfo, out xpe)) { return(true); } //If Value is DynamicResource and it's a BP, SetDynamicResource if (xpe == null && TrySetDynamicResource(element, property, value, lineInfo, out xpe)) { return(true); } //If value is BindingBase, SetBinding if (xpe == null && TrySetBinding(element, property, localName, value, lineInfo, out var binding, out xpe)) { if (binding != null && XamlFilePathAttribute.GetFilePathForObject(rootElement) is string path) { registerSourceInfo(binding, path); } return(true); } //If it's a BindableProberty, SetValue if (xpe == null && TrySetValue(element, property, attached, value, lineInfo, serviceProvider, out xpe)) { if (value != null && !value.GetType().GetTypeInfo().IsValueType&& XamlFilePathAttribute.GetFilePathForObject(rootElement) is string path) { registerSourceInfo(value, path); } return(true); } //If we can assign that value to a normal property, let's do it if (xpe == null && TrySetProperty(element, localName, value, lineInfo, serviceProvider, rootElement, out xpe)) { if (value != null && !value.GetType().GetTypeInfo().IsValueType&& XamlFilePathAttribute.GetFilePathForObject(rootElement) is string path) { registerSourceInfo(value, path); } return(true); } //If it's an already initialized property, add to it if (xpe == null && TryAddToProperty(element, propertyName, value, xKey, lineInfo, serviceProvider, rootElement, out xpe)) { if (value != null && !value.GetType().GetTypeInfo().IsValueType&& XamlFilePathAttribute.GetFilePathForObject(rootElement) is string path) { registerSourceInfo(value, path); } return(true); } xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exist, or is not assignable, or mismatching type between value and property", lineInfo); return(false); }