Пример #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);
            }
        }
Пример #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();
            }
        }
Пример #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);
                }
            }
        }