Exemplo n.º 1
0
        /// <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);
            }
        }
Exemplo n.º 2
0
        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();
            }
        }
Exemplo n.º 3
0
        /// <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);
                }
            }
        }
Exemplo n.º 4
0
        /// <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();
            }
        }