public bool Evaluate(IDataDescriptor source, out IDataDescriptor result) { result = null; Type type; object obj; MemberInfo mi; if (!ExtractWorkingData(source, _memberName + "Property", out type, out obj, out mi)) { if (!ExtractWorkingData(source, _memberName, out type, out obj, out mi)) { return(false); } } if (mi is FieldInfo) { // Field access result = new FieldDataDescriptor(obj, (FieldInfo)mi); return(true); } if (mi is PropertyInfo) { // Property access PropertyInfo pi = (PropertyInfo)mi; // Handle indexed property object[] convertedIndices = null; // Check property indexer bool indicesOnProperty = _indices != null && _indices.Length > 0 && ReflectionHelper.ConsumeParameters(_indices, pi.GetIndexParameters(), false, out convertedIndices); if (!indicesOnProperty) { convertedIndices = null; } if (pi.PropertyType == typeof(AbstractProperty)) { // Property value -> request value and return DependencyPropertyDataDescriptor object val = pi.GetValue(obj, convertedIndices); if (val == null) { return(false); } result = new DependencyPropertyDataDescriptor(obj, _memberName, (AbstractProperty)val); } else { // Simple property result = new SimplePropertyDataDescriptor(obj, pi); if (convertedIndices != null && convertedIndices.Length > 0) { ((SimplePropertyDataDescriptor)result).Indices = convertedIndices; } } if (_indices != null && _indices.Length > 0 && !indicesOnProperty) { // Item or collection index -> handle index expression per IndexerPathSegment return(new IndexerPathSegment(_indices).Evaluate(result, out result)); } return(true); } if (mi is MethodInfo) { // Method invocation is not supported in evaluation return(false); } // Unsupported member type return(false); }
/// <summary> /// Handles nodes for member or event assignment to the currently processed /// element in both attribute syntax and member element syntax. /// This method ignores attributes from the <c>x:</c> namespace, which /// will be handled in method <see cref="CheckNameOrKey"/>. /// </summary> /// <param name="memberDeclarationNode">Node containing a member or /// event assignment for the current element. This node can either /// be an <see cref="XmlAttribute"/> node (for attribute syntax) or an /// <see cref="XmlElement"/> node (for member element syntax). /// Nodes in the <c>x:</c> namespace will be ignored.</param> protected void HandleMemberOrEventAssignment(XmlNode memberDeclarationNode) { ElementContextInfo elementContext = _elementContextStack.CurrentElementContext; string memberName = memberDeclarationNode.LocalName; int i = memberName.IndexOf('.'); string explicitTargetQualifier = i == -1 ? string.Empty : memberName.Substring(0, i); if (explicitTargetQualifier.Length > 0) { memberName = memberName.Substring(explicitTargetQualifier.Length + 1); // Cut away target element name } // Check if we have an attached property bool isAttached; if (explicitTargetQualifier == string.Empty) { isAttached = false; } else if (explicitTargetQualifier == elementContext.Element.LocalName) { // Qualifier == element name, maybe an attached property string namespaceURI = (memberDeclarationNode.NamespaceURI == string.Empty && memberDeclarationNode is XmlAttribute) ? _elementContextStack.GetNamespaceOfPrefix(string.Empty) : memberDeclarationNode.NamespaceURI; isAttached = GetNamespaceHandler(namespaceURI).HasAttachedProperty( explicitTargetQualifier, memberName, elementContext.Instance); } else { isAttached = true; } if (isAttached) { // Attached property or event attribute // For unprefixed attributes, adopt the namespace from the parent node string namespaceURI = (memberDeclarationNode.NamespaceURI == string.Empty && memberDeclarationNode is XmlAttribute) ? _elementContextStack.GetNamespaceOfPrefix(string.Empty) : memberDeclarationNode.NamespaceURI; DefaultAttachedPropertyDataDescriptor dapdd; if (!DefaultAttachedPropertyDataDescriptor.CreateAttachedPropertyDataDescriptor(GetNamespaceHandler(namespaceURI), elementContext.Instance, explicitTargetQualifier, memberName, out dapdd)) { throw new InvalidOperationException(string.Format("Attached property '{0}.{1}' is not available on new target object '{2}'", explicitTargetQualifier, memberName, elementContext.Instance)); } object value = ParseValue(memberDeclarationNode); HandleMemberAssignment(dapdd, value); } else { // Local member // We have to check if <c>memberDeclarationNode.Prefix == string.Empty</c>, because // unprefixed names normally match the default's namespace URI, // but here the namespace URI of those unprefixed nodes implicitly // belong to the current element. if (memberDeclarationNode.Prefix == string.Empty || memberDeclarationNode.NamespaceURI == elementContext.Element.NamespaceURI) { // Member or event attribute located in the same namespace as the element object value = ParseValue(memberDeclarationNode); Type t = elementContext.Instance.GetType(); // Search Member data descriptor IDataDescriptor dd; if (ReflectionHelper.FindMemberDescriptor(elementContext.Instance, memberName, out dd)) { // Member assignment if (value != null) { HandleMemberAssignment(dd, value); } return; } EventInfo evt = t.GetEvent(memberName); if (evt != null) { // Event assignment HandleEventAssignment(elementContext.Instance, evt, (string)Convert(value, typeof(string))); return; } throw new XamlBindingException("XAML parser: Member '{0}' was not found on type '{1}'", memberName, t.Name); } else if (memberDeclarationNode.NamespaceURI == XamlNamespaceHandler.XAML_NS_URI) { // XAML attributes ("x:Attr") will be ignored here - they are evaluated // in the code processing the parent instance } else { throw new XamlParserException("Attribute '{0}' is not defined in namespace '{1}'", memberDeclarationNode.LocalName, memberDeclarationNode.NamespaceURI); } } }
protected object ParseValue(string str) { if (str.StartsWith("{}")) // {} = escape sequence { return(str.Substring(2)); } if (!str.StartsWith("{")) { // Normal value, no markup extension return(str); } // Object/markup extension instantiation in attribute value syntax if (!str.EndsWith("}")) { throw new XamlParserException("Object instantiation expression '{0}' must be terminated by a '}}' character", str); } string expr = str.Substring(1, str.Length - 2).Trim(); // Extract the expression ElementContextInfo elementContext = _elementContextStack.PushElementContext(expr); try { // Step 1: Process namespace declarations (doesn't apply here) string extensionName; IList <KeyValuePair <string, string> > parameters; bool namedParams; AttributeValueInstantiationParser.ParseInstantiationExpression( expr, out extensionName, out parameters, out namedParams); string namespaceURI; LookupNamespace(extensionName, out extensionName, out namespaceURI); IInitializable initializable = null; if (namedParams) { // Parameters given in a Name=Value syntax // Step 2: Instantiate the element elementContext.Instance = GetNamespaceHandler(namespaceURI).InstantiateElement( this, extensionName, new List <object>()); // Invoke default constructor initializable = elementContext.Instance as IInitializable; if (initializable != null) { initializable.StartInitialization(this); } // Step 4: Members and events in attribute syntax // We only process the given parameters and assign their values to the // target members. Property value inheritance, for example the // inheritance of a "Context" member for bindings, has to be // implemented on the visual's element class hierarchy. foreach (KeyValuePair <string, string> parameter in parameters) // Assign value to each specified member { string memberName = parameter.Key; IDataDescriptor dd; if (ReflectionHelper.FindMemberDescriptor(elementContext.Instance, memberName, out dd)) { object paramVal = ParseValue(parameter.Value); HandleMemberAssignment(dd, paramVal); // Step 4: Name registration if (memberName == "Name") { string value = Convert(paramVal, typeof(string)) as string; RegisterName(value, elementContext.Instance); } } else { throw new XamlBindingException( "XAML parser: Member '{0}' was not found on element type '{1}'", memberName, elementContext.Instance.GetType().Name); } } } else { // Parameters given as constructor parameters // Step 2: Instantiate the element IList <object> flatParams = new List <object>(); foreach (string param in AttributeValueInstantiationParser.ExtractParameterValues(parameters)) { object value = ParseValue(param); IEvaluableMarkupExtension evaluableMarkupExtension = value as IEvaluableMarkupExtension; if (evaluableMarkupExtension != null) { evaluableMarkupExtension.Initialize(this); if (!evaluableMarkupExtension.Evaluate(out value)) { throw new XamlParserException("Could not evaluate markup extension '{0}'", evaluableMarkupExtension); } } flatParams.Add(value); } elementContext.Instance = GetNamespaceHandler(namespaceURI).InstantiateElement(this, extensionName, flatParams); initializable = elementContext.Instance as IInitializable; if (initializable != null) { initializable.StartInitialization(this); } // Step 4: Members and events in attribute syntax (doesn't apply here) } // Step 5: Member values in member element syntax (doesn't apply here) // Step 6: Handle child elements (doesn't apply here) // Step 7: Initialize if (initializable != null) { initializable.FinishInitialization(this); } return(elementContext.Instance); } finally { _elementContextStack.PopElementContext(); } }
/// <summary> /// Instantiates the specified XML element located under the current element context /// in the <see cref="_elementContextStack"/> and sets the resulting element up. /// </summary> /// <remarks> /// Before calling this, the parent element, which is the current top element /// on the element context stack, has to be instantiated and its namespaces /// have to be imported. In the current implementation, its attributes will /// also already be processed. /// When this method returns, the returned visual element is completely set up, /// this means, it was instantiated, its members and events were processed /// and its content member was assigned. The processing of the complete /// structure under <paramref name="currentElement"/> has finished. /// </remarks> /// <param name="currentElement">The XML element under the /// current element on the element's context stack, which has to be /// instantiated and set up.</param> /// <param name="key">Will be set if an <c>x:Key</c> attribute was set on the /// element to parse.</param> /// <returns>Instantiated XAML visual element corresponding to the /// specified <paramref name="currentElement"/>.</returns> protected object Instantiate(XmlElement currentElement, out object key) { ElementContextInfo elementContext = _elementContextStack.PushElementContext(currentElement); try { string name = null; key = null; IList <XmlAttribute> remainingAttributes = new List <XmlAttribute>(currentElement.Attributes.Count); // Step 1: Process namespace declarations (to be done first) foreach (XmlAttribute attr in currentElement.Attributes) { // We have to sort out namespace declarations. // For both declarations xmlns="..." and xmlns:xx="...", the parser returns the // namespace URI "http://www.w3.org/2000/xmlns/" (static constant // <see cref="XMLNS_NS_URI"/>) for the attribute, so we check for this // namespace URI. The value of the attribute (for example // "http://schemas.microsoft.com/winfx/2006/xaml") is the URI // of the namespace to be imported. if (attr.NamespaceURI == XMLNS_NS_URI) { string importNamespaceURI = attr.Value; INamespaceHandler handler; if (importNamespaceURI == XamlNamespaceHandler.XAML_NS_URI) { // Implicit namespace: Use default x:-handler handler = new XamlNamespaceHandler(); } else if (importNamespaceURI.StartsWith("clr-namespace:")) { handler = ImportClrNamespace(importNamespaceURI); } else { handler = _importCustomNamespace(this, importNamespaceURI); } _elementContextStack.RegisterNamespaceHandler(importNamespaceURI, handler); } else { // Store other attributes so we don't need to sort out the namespace declarations in step 3 remainingAttributes.Add(attr); } } // Step 2: Instantiate the element elementContext.Instance = GetNamespaceHandler(currentElement.NamespaceURI). InstantiateElement(this, currentElement.LocalName, new List <object>()); IInitializable initializable = elementContext.Instance as IInitializable; if (initializable != null) { initializable.StartInitialization(this); } // Step 3: Name registration and check for x:Key (to be done before child objects are built) foreach (XmlAttribute attr in remainingAttributes) { CheckNameOrKey(attr, ref name, ref key); } foreach (XmlNode node in currentElement.ChildNodes) { CheckNameOrKey(node, ref name, ref key); } if (name != null) { RegisterName(name, elementContext.Instance); } // Step 4: Members and events in attribute syntax foreach (XmlAttribute attr in remainingAttributes) { HandleMemberOrEventAssignment(attr); } // Step 5: Member values in member element syntax foreach (XmlNode node in currentElement.ChildNodes) { // Hint: We do not enforce, that object elements, which are used to fill the // XAML content member within an object element, must be contiguous if (node is XmlElement && node.LocalName.IndexOf('.') != -1) // "." is used as indicator that we have a member declaration { // Check XML attributes on member element - only x:Uid is permitted foreach (XmlAttribute attr in node.Attributes) { if (attr.NamespaceURI != XamlNamespaceHandler.XAML_NS_URI || attr.LocalName != "Uid") { throw new XamlParserException("No attributes are allowed on member elements except x:Uid"); } // TODO: Handle x:Uid markup extension } HandleMemberOrEventAssignment(node); } } // Step 6: Handle child elements if (elementContext.Instance is INativeXamlObject) { // Implementors of INativeXamlObject will handle their XAML child elements theirselves ((INativeXamlObject)elementContext.Instance).HandleChildren(this, currentElement); } else { // Content member value/Children handled by this XAML parser object value = ParseValue(currentElement); if (value != null) { IDataDescriptor dd; if (elementContext.Instance is IContentEnabled && ((IContentEnabled)elementContext.Instance).FindContentProperty(out dd)) { HandleMemberAssignment(dd, value); } else if (ReflectionHelper.CheckHandleCollectionAssignment(elementContext.Instance, value)) { } else { throw new XamlBindingException("Visual element '{0}' doesn't support adding children", currentElement.Name); } } } // Step 7: Initialize if (initializable != null) { initializable.FinishInitialization(this); } return(elementContext.Instance); } finally { _elementContextStack.PopElementContext(); } }