public void Visit(ElementNode node, INode parentNode) { if (node.SkipPrefix(node.NamespaceResolver.LookupPrefix(node.NamespaceURI))) return; 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; if (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] }); } }
public void Visit(ElementNode node, INode parentNode) { object value = null; if (node.SkipPrefix(node.NamespaceResolver.LookupPrefix(node.NamespaceURI))) return; XamlParseException xpe; var type = XamlParser.GetElementType(node.XmlType, node, Context.RootElement?.GetType().GetTypeInfo().Assembly, out xpe); if (xpe != null) throw xpe; Context.Types[node] = type; string ctorargname; if (IsXaml2009LanguagePrimitive(node)) value = CreateLanguagePrimitive(type, node); else if (node.Properties.ContainsKey(XmlName.xArguments) || node.Properties.ContainsKey(XmlName.xFactoryMethod)) value = CreateFromFactory(type, node); else if ( type.GetTypeInfo() .DeclaredConstructors.Any( ci => ci.IsPublic && ci.GetParameters().Length != 0 && ci.GetParameters().All(pi => pi.CustomAttributes.Any(attr => attr.AttributeType == typeof (ParameterAttribute)))) && ValidateCtorArguments(type, node, out ctorargname)) value = CreateFromParameterizedConstructor(type, node); else if (!type.GetTypeInfo().DeclaredConstructors.Any(ci => ci.IsPublic && ci.GetParameters().Length == 0) && !ValidateCtorArguments(type, node, out ctorargname)) { throw new XamlParseException( String.Format("The Property {0} is required to create a {1} object.", ctorargname, type.FullName), node); } else { //this is a trick as the DataTemplate parameterless ctor is internal, and we can't CreateInstance(..., false) on WP7 try { if (type == typeof (DataTemplate)) value = new DataTemplate(); if (type == typeof (ControlTemplate)) value = new ControlTemplate(); if (value == null && node.CollectionItems.Any() && node.CollectionItems.First() is ValueNode) { var serviceProvider = new XamlServiceProvider(node, Context); var converted = ((ValueNode)node.CollectionItems.First()).Value.ConvertTo(type, () => type.GetTypeInfo(), serviceProvider); if (converted != null && converted.GetType() == type) value = converted; } if (value == null) value = Activator.CreateInstance(type); } catch (TargetInvocationException e) { if (e.InnerException is XamlParseException || e.InnerException is XmlException) throw e.InnerException; throw; } } Values[node] = value; var typeExtension = value as TypeExtension; if (typeExtension != null) { var serviceProvider = new XamlServiceProvider(node, Context); var visitor = new ApplyPropertiesVisitor(Context); foreach (var cnode in node.Properties.Values.ToList()) cnode.Accept(visitor, node); foreach (var cnode in node.CollectionItems) cnode.Accept(visitor, node); value = typeExtension.ProvideValue(serviceProvider); node.Properties.Clear(); node.CollectionItems.Clear(); Values[node] = value; } if (value is BindableObject) NameScope.SetNameScope(value as BindableObject, node.Namescope); }
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(propertyInfo.PropertyType, (Func<TypeConverter>)null, serviceProvider) }); return; } } if (context.DoNotThrowOnExceptions) System.Diagnostics.Debug.WriteLine (exception.Message); else throw exception; }
public object[] CreateArgumentsArray(IElementNode enode, ConstructorInfo ctorInfo) { var n = ctorInfo.GetParameters().Length; var array = new object[n]; for (var i = 0; i < n; i++) { var parameter = ctorInfo.GetParameters()[i]; var propname = parameter.CustomAttributes.First(attr => attr.AttributeType == typeof (ParameterAttribute)) .ConstructorArguments.First() .Value as string; var name = new XmlName("", propname); INode node; if (!enode.Properties.TryGetValue(name, out node)) { throw new XamlParseException( String.Format("The Property {0} is required to create a {1} object.", propname, ctorInfo.DeclaringType.FullName), enode as IXmlLineInfo); } if (!enode.SkipProperties.Contains(name)) enode.SkipProperties.Add(name); var value = Context.Values[node]; var serviceProvider = new XamlServiceProvider(enode, Context); var convertedValue = value.ConvertTo(parameter.ParameterType, () => parameter, serviceProvider); array[i] = convertedValue; } return array; }