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(); } }