2 * Copyright(c) 2022 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections;
20 using System.Collections.Generic;
22 using System.Reflection;
24 using Tizen.NUI.Binding.Internals;
25 using Tizen.NUI.Binding;
27 using static System.String;
29 namespace Tizen.NUI.Xaml
31 internal class ApplyPropertiesVisitor : IXamlNodeVisitor
33 public static readonly IList<XmlName> Skips = new List<XmlName> {
35 XmlName.xTypeArguments,
37 XmlName.xFactoryMethod,
42 public ApplyPropertiesVisitor(HydrationContext context, bool stopOnResourceDictionary = false)
45 StopOnResourceDictionary = stopOnResourceDictionary;
48 Dictionary<INode, object> Values => Context.Values;
49 HydrationContext Context { get; }
51 public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
52 public bool StopOnDataTemplate => true;
53 public bool StopOnResourceDictionary { get; }
54 public bool VisitNodeOnDataTemplate => true;
55 public bool SkipChildren(INode node, INode parentNode) => false;
56 public bool IsResourceDictionary(ElementNode node) => Context.Types.TryGetValue(node, out var type) && typeof(ResourceDictionary).IsAssignableFrom(type);
58 public void Visit(ValueNode node, INode parentNode)
60 var parentElement = parentNode as IElementNode;
61 var value = Values[node];
62 if (!Values.TryGetValue(parentNode, out var source) && Context.ExceptionHandler != null)
66 if (TryGetPropertyName(node, parentNode, out propertyName))
68 if (TrySetRuntimeName(propertyName, source, value, node))
70 if (Skips.Contains(propertyName))
72 if (parentElement.SkipProperties.Contains(propertyName))
74 if (propertyName.Equals(XamlParser.McUri, "Ignorable"))
76 SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
78 else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
80 // Collection element, implicit content, or implicit collection element.
81 var contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo());
82 if (contentProperty != null)
84 var name = new XmlName(((ElementNode)parentNode).NamespaceURI, contentProperty);
85 if (Skips.Contains(name))
87 if (parentElement.SkipProperties.Contains(propertyName))
89 SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
94 public void Visit(MarkupNode node, INode parentNode)
98 public void Visit(ElementNode node, INode parentNode)
100 XmlName propertyName;
101 if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent)
103 var s0 = Values[parentNode];
104 if (s0 is ElementTemplate)
106 SetTemplate(s0 as ElementTemplate, node);
111 var parentElement = parentNode as IElementNode;
112 propertyName = XmlName.Empty;
114 //Simplify ListNodes with single elements
115 var pList = parentNode as ListNode;
116 if (pList != null && pList.CollectionItems.Count == 1)
118 propertyName = pList.XmlName;
119 parentNode = parentNode.Parent;
120 parentElement = parentNode as IElementNode;
123 if (!Values.TryGetValue(node, out var value) && Context.ExceptionHandler != null)
126 if (propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName))
128 if (Skips.Contains(propertyName))
130 if (parentElement == null)
132 if (parentElement.SkipProperties.Contains(propertyName))
135 if (!Values.TryGetValue(parentNode, out var source) && Context.ExceptionHandler != null)
137 ProvideValue(ref value, node, source, propertyName);
138 SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
140 else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
142 if (!Values.TryGetValue(parentNode, out var source) && Context.ExceptionHandler != null)
144 ProvideValue(ref value, node, source, XmlName.Empty);
145 string contentProperty;
146 Exception xpe = null;
147 var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
150 if (xpe == null && TryAddToResourceDictionary(source as ResourceDictionary, value, xKey, node, out xpe))
153 // Dictionary with string key
154 if (xpe == null && xKey != null)
156 var indexer = GetIndexer(source, typeof(string), value.GetType());
159 indexer.SetValue(source, value, new[] { xKey });
164 // Collection element, implicit content, or implicit collection element.
165 if (xpe == null && typeof(IEnumerable).IsAssignableFrom(Context.Types[parentElement]) && Context.Types[parentElement].GetRuntimeMethods().Any(mi => mi.Name == "Add" && mi.GetParameters().Length == 1))
168 Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
170 addMethod?.Invoke(source, new[] { value });
173 if (xpe == null && Context.Types[parentElement].GetRuntimeMethods().Any
174 (mi => mi.Name == "Add" && mi.GetParameters().Length == 1 && mi.GetParameters()[0].ParameterType.IsAssignableFrom(value.GetType())))
176 //if there are similar parameters in the function, this will exist issue.
177 var addMethod = Context.Types[parentElement].GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
178 if (addMethod != null) addMethod.Invoke(source, new[] { value });
181 if (xpe == null && (contentProperty = GetContentPropertyName(Context.Types[parentElement].GetTypeInfo())) != null)
183 var name = new XmlName(node.NamespaceURI, contentProperty);
184 if (Skips.Contains(name))
186 if (parentElement.SkipProperties.Contains(propertyName))
189 SetPropertyValue(source, name, value, Context.RootElement, node, Context, node);
193 xpe = xpe ?? new XamlParseException($"Can not set the content of {((IElementNode)parentNode).XmlType.Name} as it doesn't have a ContentPropertyAttribute", node);
194 if (Context.ExceptionHandler != null)
195 Context.ExceptionHandler(xpe);
199 else if (IsCollectionItem(node, parentNode) && parentNode is ListNode)
201 if (!Values.TryGetValue(parentNode.Parent, out var source) && Context.ExceptionHandler != null)
203 ProvideValue(ref value, node, source, XmlName.Empty);
204 var parentList = (ListNode)parentNode;
205 if (Skips.Contains(parentList.XmlName))
207 Exception xpe = null;
208 var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
211 var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _) as IEnumerable;
212 if (collection == null)
213 xpe = new XamlParseException($"Property {parentList.XmlName.LocalName} is null or is not IEnumerable", node);
215 if (xpe == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, node, out xpe))
218 MethodInfo addMethod;
219 if (xpe == null && (addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1)) != null)
221 addMethod.Invoke(collection, new[] { value });
224 xpe = xpe ?? new XamlParseException($"Value of {parentList.XmlName.LocalName} does not have a Add() method", node);
225 if (Context.ExceptionHandler != null)
226 Context.ExceptionHandler(xpe);
234 public void Visit(RootNode node, INode parentNode)
238 public void Visit(ListNode node, INode parentNode)
242 public static bool TryGetPropertyName(INode node, INode parentNode, out XmlName name)
244 name = default(XmlName);
245 var parentElement = parentNode as IElementNode;
246 if (parentElement == null)
248 foreach (var kvp in parentElement.Properties)
250 if (kvp.Value != node)
258 internal static bool IsCollectionItem(INode node, INode parentNode)
260 var parentList = parentNode as IListNode;
261 if (parentList == null)
263 return parentList.CollectionItems.Contains(node);
266 internal static string GetContentPropertyName(System.Reflection.TypeInfo typeInfo)
268 while (typeInfo != null)
270 var propName = GetContentPropertyName(typeInfo.CustomAttributes);
271 if (propName != null)
273 typeInfo = typeInfo?.BaseType?.GetTypeInfo();
278 void ProvideValue(ref object value, ElementNode node, object source, XmlName propertyName)
280 var markupExtension = value as IMarkupExtension;
281 var valueProvider = value as IValueProvider;
283 if (markupExtension == null && valueProvider == null)
286 XamlServiceProvider serviceProvider = null;
287 if (value.GetType().GetTypeInfo().GetCustomAttribute<AcceptEmptyServiceProviderAttribute>() == null)
288 serviceProvider = new XamlServiceProvider(node, Context);
290 if (serviceProvider != null && serviceProvider.IProvideValueTarget is XamlValueTargetProvider && propertyName != XmlName.Empty)
292 (serviceProvider.IProvideValueTarget as XamlValueTargetProvider).TargetProperty = GetTargetProperty(source, propertyName, Context, node);
295 if (markupExtension != null)
296 value = markupExtension.ProvideValue(serviceProvider);
297 else if (valueProvider != null)
298 value = valueProvider.ProvideValue(serviceProvider);
299 } catch (Exception e) {
300 if (Context.ExceptionHandler != null)
301 Context.ExceptionHandler(e);
307 static string GetContentPropertyName(IEnumerable<CustomAttributeData> attributes)
309 var contentAttribute =
310 attributes.FirstOrDefault(cad => ContentPropertyAttribute.ContentPropertyTypes.Contains(cad.AttributeType.FullName));
311 if (contentAttribute == null || contentAttribute.ConstructorArguments.Count != 1)
313 if (contentAttribute.ConstructorArguments[0].ArgumentType == typeof(string))
314 return (string)contentAttribute.ConstructorArguments[0].Value;
318 static bool GetRealNameAndType(ref Type elementType, string namespaceURI, ref string localname,
319 HydrationContext context, IXmlLineInfo lineInfo)
321 var dotIdx = localname.IndexOf('.');
324 var typename = localname.Substring(0, dotIdx);
325 localname = localname.Substring(dotIdx + 1);
326 XamlParseException xpe;
327 elementType = XamlParser.GetElementType(new XmlType(namespaceURI, typename, null), lineInfo,
328 context.RootElement.GetType().GetTypeInfo().Assembly, out xpe);
337 static BindableProperty GetBindableProperty(Type elementType, string localName, IXmlLineInfo lineInfo,
338 bool throwOnError = false)
341 var bindableFieldInfo = elementType.GetFields().FirstOrDefault(fi => fi.Name == localName + "Property");
343 var bindableFieldInfo = elementType.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).FirstOrDefault(fi => fi.Name == localName + "Property");
345 if (null == bindableFieldInfo)
347 bindableFieldInfo = elementType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy).FirstOrDefault(fi => fi.Name == localName + "Property");
350 Exception exception = null;
351 if (exception == null && bindableFieldInfo == null)
354 new XamlParseException(
355 Format("BindableProperty {0} not found on {1}", localName + "Property", elementType.Name), lineInfo);
358 if (exception == null)
359 return bindableFieldInfo.GetValue(null) as BindableProperty;
365 static object GetTargetProperty(object xamlelement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo)
367 var localName = propertyName.LocalName;
368 //If it's an attached BP, update elementType and propertyName
369 var bpOwnerType = xamlelement.GetType();
370 GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
371 var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
373 if (property != null)
376 var elementType = xamlelement.GetType();
377 var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
381 public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydrationContext context, IXmlLineInfo lineInfo)
383 var localName = propertyName.LocalName;
384 var serviceProvider = new XamlServiceProvider(node, context);
385 Exception xpe = null;
386 var xKey = node is IElementNode && ((IElementNode)node).Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)((IElementNode)node).Properties[XmlName.xKey]).Value as string : null;
388 //If it's an attached BP, update elementType and propertyName
389 var bpOwnerType = xamlelement.GetType();
390 var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
392 var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
394 //If the target is an event, connect
395 if (xpe == null && TryConnectEvent(xamlelement, localName, attached, value, rootElement, lineInfo, out xpe))
398 //If Value is DynamicResource and it's a BP, SetDynamicResource
399 if (xpe == null && TrySetDynamicResource(xamlelement, property, value, lineInfo, out xpe))
402 //If value is BindingBase, SetBinding
403 if (xpe == null && TrySetBinding(xamlelement, property, value, lineInfo, out xpe))
406 //Call TrySetProperty first and then TrySetValue to keep the code logic consistent whether it is through xaml or code.
407 //If we can assign that value to a normal property, let's do it
408 if (xpe == null && TrySetProperty(xamlelement, property, localName, value, serviceProvider, context, out xpe))
411 //If it's a BindableProberty, SetValue
412 if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe))
415 //If it's an already initialized property, add to it
416 if (xpe == null && TryAddToProperty(xamlelement, propertyName, value, xKey, lineInfo, serviceProvider, context, out xpe))
419 xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exist, or is not assignable, or mismatching type between value and property", lineInfo);
420 if (context.ExceptionHandler != null)
421 context.ExceptionHandler(xpe);
426 public static object GetPropertyValue(object xamlElement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo, out object targetProperty)
428 var localName = propertyName.LocalName;
429 Exception xpe = null;
431 targetProperty = null;
433 //If it's an attached BP, update elementType and propertyName
434 var bpOwnerType = xamlElement.GetType();
435 var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
436 var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
438 //If it's a BindableProberty, GetValue
439 if (xpe == null && TryGetValue(xamlElement, property, out value, out xpe, out targetProperty))
442 //If it's a normal property, get it
443 if (xpe == null && TryGetProperty(xamlElement, localName, out value, context, out xpe, out targetProperty))
446 xpe = xpe ?? new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
447 if (context.ExceptionHandler != null)
448 context.ExceptionHandler(xpe);
455 static bool TryConnectEvent(object element, string localName, bool attached, object value, object rootElement, IXmlLineInfo lineInfo, out Exception exception)
462 var elementType = element.GetType();
463 var eventInfo = elementType.GetRuntimeEvent(localName);
464 var stringValue = value as string;
466 if (eventInfo == null || IsNullOrEmpty(stringValue))
469 var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value);
470 if (methodInfo == null)
472 exception = new XamlParseException($"No method {value} found on type {rootElement.GetType()}", lineInfo);
478 eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
481 catch (ArgumentException ae)
483 exception = new XamlParseException($"Method {stringValue} does not have the correct signature", lineInfo, ae);
488 static bool TrySetDynamicResource(object element, BindableProperty property, object value, IXmlLineInfo lineInfo, out Exception exception)
492 var elementType = element.GetType();
493 var dynamicResource = value as DynamicResource;
494 var bindable = element as BindableObject;
496 if (dynamicResource == null || property == null)
499 if (bindable == null)
501 exception = new XamlParseException($"{elementType.Name} is not a BindableObject", lineInfo);
505 bindable.SetDynamicResource(property, dynamicResource.Key);
509 static bool TrySetBinding(object element, BindableProperty property, object value, IXmlLineInfo lineInfo, out Exception exception)
513 var elementType = element.GetType();
514 var binding = value.ConvertTo(typeof(BindingBase), pinfoRetriever: null, serviceProvider: null) as BindingBase;
515 var bindable = element as BindableObject;
520 if (bindable != null && property != null)
522 bindable.SetBinding(property, binding);
526 if (property != null)
527 exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support native bindings", lineInfo);
532 static bool TrySetValue(object element, BindableProperty property, bool attached, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
536 var elementType = element.GetType();
537 var bindable = element as BindableObject;
539 if (property == null)
542 if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
543 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = property;
545 Func<MemberInfo> minforetriever;
547 minforetriever = () => property.DeclaringType.GetRuntimeMethod("Get" + property.PropertyName, new[] { typeof(BindableObject) });
550 minforetriever = () => property.DeclaringType.GetRuntimeProperties().LastOrDefault(p => p.Name == property.PropertyName);
552 //minforetriever = () => property.DeclaringType.GetRuntimeProperty(property.PropertyName);
553 var convertedValue = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider);
555 if (bindable != null)
557 //SetValue doesn't throw on mismatching type, so check before to get a chance to try the property setting or the collection adding
558 var nullable = property.ReturnTypeInfo.IsGenericType &&
559 property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
560 if ((convertedValue == null && (!property.ReturnTypeInfo.IsValueType || nullable)) ||
561 (property.ReturnType.IsInstanceOfType(convertedValue)))
563 bindable.SetValue(property, convertedValue);
567 // This might be a collection; see if we can add to it
568 return TryAddValue(bindable, property, value, serviceProvider);
571 exception = new XamlParseException($"{elementType.Name} is not a BindableObject or does not support setting native BindableProperties", lineInfo);
575 static bool TryGetValue(object element, BindableProperty property, out object value, out Exception exception, out object targetProperty)
579 targetProperty = property;
580 var elementType = element.GetType();
581 var bindable = element as BindableObject;
583 if (property == null)
586 if (bindable == null)
589 value = bindable.GetValue(property);
593 static bool TrySetProperty(object element, BindableProperty property, string localName, object value, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
597 var elementType = element.GetType();
598 var propertyInfo = elementType.GetRuntimeProperties().FirstOrDefault(p => p.Name == localName);
600 if (propertyInfo == null || !propertyInfo.CanWrite || (setter = propertyInfo.SetMethod) == null)
603 if (!IsVisibleFrom(setter, context.RootElement))
606 if (property != null && propertyInfo.PropertyType != property.ReturnType)
609 if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
610 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = propertyInfo;
612 object convertedValue = GetConvertedValue(propertyInfo.PropertyType, value, () => propertyInfo, serviceProvider);
614 if (null == convertedValue)
616 var methods = propertyInfo.PropertyType.GetMethods().Where(a => a.Name == "op_Implicit");
618 foreach (var method in methods)
620 var paramType = method.GetParameters()[0].ParameterType;
621 convertedValue = GetConvertedValue(paramType, value, () => propertyInfo, serviceProvider);
623 if (null != convertedValue)
625 var realValue = Activator.CreateInstance(propertyInfo.PropertyType);
626 convertedValue = method.Invoke(realValue, new object[] { convertedValue });
628 if (null != convertedValue)
636 if (null == convertedValue)
641 setter.Invoke(element, new object[] { convertedValue });
645 static private object GetConvertedValue(Type valueType, object value, Func<MemberInfo> minfoRetriever, XamlServiceProvider serviceProvider)
647 object convertedValue = value.ConvertTo(valueType, minfoRetriever, serviceProvider);
649 if (convertedValue != null && !valueType.IsInstanceOfType(convertedValue))
651 convertedValue = null;
654 return convertedValue;
657 static bool TryGetProperty(object element, string localName, out object value, HydrationContext context, out Exception exception, out object targetProperty)
661 var elementType = element.GetType();
662 PropertyInfo propertyInfo = null;
665 propertyInfo = elementType.GetRuntimeProperty(localName);
667 catch (AmbiguousMatchException)
669 // Get most derived instance of property
670 foreach (var property in elementType.GetRuntimeProperties().Where(prop => prop.Name == localName))
672 if (propertyInfo == null || propertyInfo.DeclaringType.IsAssignableFrom(property.DeclaringType))
673 propertyInfo = property;
677 targetProperty = propertyInfo;
678 if (propertyInfo == null || !propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null)
681 if (!IsVisibleFrom(getter, context.RootElement))
684 value = getter.Invoke(element, System.Array.Empty<object>());
688 static bool IsVisibleFrom(MethodInfo method, object rootElement)
692 if (method.IsPrivate && method.DeclaringType == rootElement.GetType())
694 if ((method.IsAssembly || method.IsFamilyOrAssembly) && method.DeclaringType.AssemblyQualifiedName == rootElement.GetType().AssemblyQualifiedName)
696 if (method.IsFamily && method.DeclaringType.IsAssignableFrom(rootElement.GetType()))
701 static bool TryAddToProperty(object element, XmlName propertyName, object value, string xKey, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
705 object targetProperty;
706 var collection = GetPropertyValue(element, propertyName, context, lineInfo, out targetProperty) as IEnumerable;
708 if (collection == null)
711 if (exception == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, lineInfo, out exception))
714 if (exception != null)
717 var addMethod = collection.GetType().GetRuntimeMethods().First(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
718 if (addMethod == null)
721 if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
722 ((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = targetProperty;
724 addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
728 static bool TryAddToResourceDictionary(ResourceDictionary resourceDictionary, object value, string xKey, IXmlLineInfo lineInfo, out Exception exception)
732 if (resourceDictionary == null)
736 resourceDictionary.Add(xKey, value);
737 else if (value is XamlStyle)
738 resourceDictionary.Add((XamlStyle)value);
739 else if (value is ResourceDictionary)
740 resourceDictionary.Add((ResourceDictionary)value);
743 exception = new XamlParseException("resources in ResourceDictionary require a x:Key attribute", lineInfo);
749 void SetTemplate(ElementTemplate dt, INode node)
751 #pragma warning disable 0612
752 ((IDataTemplate)dt).LoadTemplate = () =>
754 #pragma warning restore 0612
755 var cnode = node.Clone();
756 var context = new HydrationContext { ParentContext = Context, RootElement = Context.RootElement };
757 cnode.Accept(new XamlNodeVisitor((n, parent) => n.Parent = parent), node.Parent); //set parents for {StaticResource}
758 cnode.Accept(new ExpandMarkupsVisitor(context), null);
759 cnode.Accept(new NamescopingVisitor(context), null);
760 cnode.Accept(new CreateValuesVisitor(context), null);
761 cnode.Accept(new RegisterXNamesVisitor(context), null);
762 cnode.Accept(new FillResourceDictionariesVisitor(context), null);
763 cnode.Accept(new ApplyPropertiesVisitor(context, true), null);
764 return context.Values[cnode];
768 static bool TryAddValue(BindableObject bindable, BindableProperty property, object value, XamlServiceProvider serviceProvider)
770 if (property?.ReturnTypeInfo?.GenericTypeArguments == null)
775 if (property.ReturnType == null)
780 if (property.ReturnTypeInfo.GenericTypeArguments.Length != 1 ||
781 !property.ReturnTypeInfo.GenericTypeArguments[0].IsInstanceOfType(value))
784 // This might be a collection we can add to; see if we can find an Add method
785 var addMethod = GetAllRuntimeMethods(property.ReturnType)
786 .FirstOrDefault(mi => mi.Name == "Add" && mi.GetParameters().Length == 1);
787 if (addMethod == null)
790 // If there's an add method, get the collection
791 var collection = bindable.GetValue(property);
793 // And add the new value to it
794 addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider) });
798 static IEnumerable<MethodInfo> GetAllRuntimeMethods(Type type)
800 return type.GetRuntimeMethods()
801 .Concat(type.GetTypeInfo().ImplementedInterfaces.SelectMany(t => t.GetRuntimeMethods()));
804 bool TrySetRuntimeName(XmlName propertyName, object source, object value, ValueNode node)
806 if (propertyName != XmlName.xName)
809 var runTimeName = source.GetType().GetTypeInfo().GetCustomAttribute<RuntimeNamePropertyAttribute>();
810 if (runTimeName == null)
813 SetPropertyValue(source, new XmlName("", runTimeName.Name), value, Context.RootElement, node, Context, node);
817 private PropertyInfo GetIndexer(object source, Type keyType, Type valueType) => source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(p => p.Name == "Item" && p.PropertyType.IsAssignableFrom(valueType) && p.GetIndexParameters().Length != 0 && p.GetIndexParameters()[0].ParameterType == keyType);