/// <summary> /// Checks the existance of a <c>Name</c> and the <c>x:Name</c> and <c>x:Key</c> /// pseudo member nodes in both attribute syntax and member element syntax. /// Other nodes from the <c>x:</c> namespace will be rejected by throwing /// an exception. /// Names given by a <c>x:Name</c> will be automatically assigned to the /// current element's "Name" attribute, if it is present. /// Except <c>Name</c>, this method ignores "normal" members and events, /// which will be handled in method /// <see cref="HandleMemberOrEventAssignment"/>. /// </summary> /// <remarks> /// Implementation node: We separate this method from method /// <see cref="HandleMemberOrEventAssignment"/> /// because both methods handle attributes of different scopes. The <c>x:</c> /// attributes are attributes which affect the outside world, as they associate /// a name or key to the current element in the outer scope. In contrast to /// this, "Normal" members are set on the current element and do not affect /// the outer scope. /// </remarks> /// <param name="memberDeclarationNode">Node containing a <c>x:</c> attribute /// 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). /// This method ignores "normal" member and event attributes.</param> /// <param name="name">Will be set if the node to handle is an <c>x:Name</c>.</param> /// <param name="key">Will be set if the node to handle is an <c>x:Key</c>.</param> protected void CheckNameOrKey(XmlNode memberDeclarationNode, ref string name, ref object key) { ElementContextInfo elementContext = _elementContextStack.CurrentElementContext; // Name if ((memberDeclarationNode.Prefix == string.Empty || memberDeclarationNode.NamespaceURI == elementContext.Element.NamespaceURI) && memberDeclarationNode.LocalName == "Name") { name = Convert(ParseValue(memberDeclarationNode), typeof(string)) as string; return; } if (memberDeclarationNode.NamespaceURI != XamlNamespaceHandler.XAML_NS_URI) { // Ignore other attributes not located in the x: namespace return; } // x: attributes if (memberDeclarationNode.LocalName == "Name") // x:Name { // x:Name if (name == null) { object value = ParseValue(memberDeclarationNode); name = TypeConverter.Convert(value, typeof(string)) as string; // Assign name to "Name" member, if one exists IDataDescriptor dd; if (ReflectionHelper.FindMemberDescriptor( elementContext.Instance, "Name", out dd)) { // Member assignment HandleMemberAssignment(dd, value); } } else { throw new XamlBindingException("Attribute '{0}' was specified multiple times", memberDeclarationNode.Name); } } else if (memberDeclarationNode.LocalName == "Key") { // x:Key object value = ParseValue(memberDeclarationNode); if (key == null) { key = value; } else { throw new XamlBindingException("Attribute '{0}' was specified multiple times", memberDeclarationNode.Name); } } else if (memberDeclarationNode is XmlAttribute) { // TODO: Is it correct to reject all usages of x:Attr attributes except x:Key and x:Name? throw new XamlParserException("Attribute '{0}' cannot be used here", memberDeclarationNode.Name); } }
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> /// 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); } } }