예제 #1
0
        public static IAbstractPropertySetter GetPropertyGroupMember(this IAbstractControl control, string prefix, string memberName)
        {
            IAbstractPropertySetter value;

            control.TryGetProperty(control.Metadata.GetPropertyGroupMember(prefix, memberName), out value);
            return(value);
        }
예제 #2
0
 /// <summary>
 /// Resolves the content of the control immediately during the parsing.
 /// Override this method if you need lazy loading of tree node contents.
 /// </summary>
 protected virtual void ResolveControlContentImmediately(IAbstractControl control, List <DothtmlNode> content)
 {
     foreach (var node in content)
     {
         var child = ProcessNode(control, node, control.Metadata, control.DataContextTypeStack);
         if (child != null)
         {
             treeBuilder.AddChildControl(control, child);
         }
     }
 }
        public IEnumerable <ControlUsageError> Validate(IAbstractControl control)
        {
            var type = GetControlType(control.Metadata);

            if (type == null)
            {
                return(null);
            }

            var result  = new List <ControlUsageError>();
            var methods = cache.GetOrAdd(type, FindMethods);

            foreach (var method in methods)
            {
                var par  = method.GetParameters();
                var args = new object[par.Length];
                for (int i = 0; i < par.Length; i++)
                {
                    if (par[i].ParameterType.IsAssignableFrom(control.GetType()))
                    {
                        args[i] = control;
                    }
                    else if (control.DothtmlNode != null && par[i].ParameterType.IsAssignableFrom(control.DothtmlNode.GetType()))
                    {
                        args[i] = control.DothtmlNode;
                    }
                    else
                    {
                        goto Error; // I think it is better that throw exceptions and catch them
                    }
                }
                var r = method.Invoke(null, args);
                if (r is IEnumerable <ControlUsageError> )
                {
                    result.AddRange((IEnumerable <ControlUsageError>)r);
                }
                else if (r is IEnumerable <string> )
                {
                    result.AddRange((r as IEnumerable <string>).Select(e => new ControlUsageError(e)));
                }
                continue;
                Error :;
            }

            return(result
                   // add current node to the error, if no control is specified
                   .Select(e => e.Nodes.Length == 0 ?
                           new ControlUsageError(e.ErrorMessage, control.DothtmlNode) :
                           e));
        }
        public IEnumerable<ControlUsageError> Validate(IAbstractControl control)
        {
            var type = GetControlType(control.Metadata);
            if (type == null) return null;

            var result = new List<ControlUsageError>();
            var methods = cache.GetOrAdd(type, FindMethods);
            foreach (var method in methods)
            {
                var par = method.GetParameters();
                var args = new object[par.Length];
                for (int i = 0; i < par.Length; i++)
                {
                    if (par[i].ParameterType.IsAssignableFrom(control.GetType()))
                    {
                        args[i] = control;
                    }
                    else if (control.DothtmlNode != null && par[i].ParameterType.IsAssignableFrom(control.DothtmlNode.GetType()))
                    {
                        args[i] = control.DothtmlNode;
                    }
                    else
                    {
                        goto Error; // I think it is better that throw exceptions and catch them
                    }
                }
                var r = method.Invoke(null, args);
                if(r is IEnumerable<ControlUsageError>)
                {
                    result.AddRange((IEnumerable<ControlUsageError>)r);
                }
                else if(r is IEnumerable<string>)
                {
                    result.AddRange((r as IEnumerable<string>).Select(e => new ControlUsageError(e, new[] { control.DothtmlNode })));
                }
                continue;
                Error:;
            }
            return result;
        }
예제 #5
0
        private void ProcessAttributeProperties(IAbstractControl control, DothtmlAttributeNode[] nodes, IDataContextStack dataContext)
        {
            var doneAttributes = new HashSet <DothtmlAttributeNode>();

            string getName(DothtmlAttributeNode n) => n.AttributePrefix == null ? n.AttributeName : n.AttributePrefix + ":" + n.AttributeName;

            void resolveAttribute(DothtmlAttributeNode attribute)
            {
                var name = getName(attribute);

                if (!doneAttributes.Add(attribute))
                {
                    return;
                }

                var property = controlResolver.FindProperty(control.Metadata, name);

                if (property == null)
                {
                    attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!");
                }
                else
                {
                    var dependsOn = property.DataContextChangeAttributes.SelectMany(c => c.PropertyDependsOn);
                    foreach (var p in dependsOn.SelectMany(t => nodes.Where(n => t == getName(n))))
                    {
                        resolveAttribute(p);
                    }
                    ProcessAttribute(property, attribute, control, dataContext);
                }
            }

            // set properties from attributes
            foreach (var attr in nodes)
            {
                resolveAttribute(attr);
            }
        }
        public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
        {
            IPropertyDescriptor controlProperty;
            if (!control.Metadata.TryGetProperty(PropertyName, out controlProperty))
            {
                throw new Exception($"The property '{PropertyName}' was not found on control '{control.Metadata.Type}'!");
            }

            IAbstractPropertySetter setter;
            if (control.TryGetProperty(controlProperty, out setter))
            {
                var binding = setter as IAbstractPropertyBinding;
                if (binding == null)
                {
                    return dataContext;
                }
                return binding.Binding.ResultType;
            }
            else
            {
                if (AllowMissingProperty) return dataContext;
                else throw new Exception($"Property '{PropertyName}' is required on '{control.Metadata.Type.Name}'.");
            }
        }
예제 #7
0
 public void SetProperty(IAbstractControl control, IAbstractPropertySetter setter)
 {
     ((ResolvedControl)control).SetProperty((ResolvedPropertySetter)setter);
 }
 public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
 {
     return(new ResolvedTypeDescriptor(type));
 }
예제 #9
0
 public void AddChildControl(IAbstractContentNode control, IAbstractControl child)
 {
     ((ResolvedContentNode)control).AddChild((ResolvedControl)child);
 }
예제 #10
0
 public abstract ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null);
예제 #11
0
        public static bool HasPropertyValue(this IAbstractControl control, IPropertyDescriptor property)
        {
            IAbstractPropertySetter setter;

            return(control.TryGetProperty(property, out setter) && setter is IAbstractPropertyValue);
        }
예제 #12
0
 public abstract ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null);
예제 #13
0
        /// <summary>
        /// Gets the data context change behavior for the specified control property.
        /// </summary>
        protected virtual IDataContextStack GetDataContextChange(IDataContextStack dataContext, IAbstractControl control, IPropertyDescriptor property)
        {
            if (dataContext == null)
            {
                return(null);
            }

            var manipulationAttribute = property != null ? property.DataContextManipulationAttribute : control.Metadata.DataContextManipulationAttribute;

            if (manipulationAttribute != null)
            {
                return(manipulationAttribute.ChangeStackForChildren(dataContext, control, property, (parent, changeType) => CreateDataContextTypeStack(changeType, parentDataContextStack: parent)));
            }

            var attributes = property != null ? property.DataContextChangeAttributes : control.Metadata.DataContextChangeAttributes;

            if (attributes == null || attributes.Length == 0)
            {
                return(dataContext);
            }

            var type = dataContext.DataContextType;

            foreach (var attribute in attributes.OrderBy(a => a.Order))
            {
                type = attribute.GetChildDataContextType(type, dataContext, control, property);
                if (type == null)
                {
                    break;
                }
            }
            return(CreateDataContextTypeStack(type, parentDataContextStack: dataContext));
        }
예제 #14
0
 public override ITypeDescriptor?GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor?property = null)
 {
     return(new ResolvedTypeDescriptor(GenericTypeDefinition).MakeGenericType(dataContext));
 }
예제 #15
0
 public void SetHtmlAttribute(IAbstractControl control, string attributeName, object value)
 {
     ((ResolvedControl) control).SetHtmlAttribute(attributeName, value);
 }
예제 #16
0
 public IAbstractPropertyControl BuildPropertyControl(IPropertyDescriptor property, IAbstractControl control)
 {
     return new ResolvedPropertyControl((DotvvmProperty)property, (ResolvedControl)control);
 }
		public abstract IDataContextStack ChangeStackForChildren(IDataContextStack original, IAbstractControl control, IPropertyDescriptor property, Func<IDataContextStack, ITypeDescriptor, IDataContextStack> createNewFrame);
예제 #18
0
        /// <summary>
        /// Processes the attribute node.
        /// </summary>
        private void ProcessAttribute(DothtmlAttributeNode attribute, IAbstractControl control, IDataContextStack dataContext)
        {
            if (attribute.AttributePrefix == "html")
            {
                if (!control.Metadata.HasHtmlAttributesCollection)
                {
                    attribute.AddError($"The control '{control.Metadata.Type.FullName}' cannot use HTML attributes!");
                }
                else
                {
                    try
                    {
                        treeBuilder.SetHtmlAttribute(control, ProcessAttributeValue(attribute, dataContext));
                    }
                    catch (NotSupportedException ex)
                    {
                        if (ex.InnerException == null)
                        {
                            throw;
                        }
                        else
                        {
                            attribute.AddError(ex.Message);
                        }
                    }
                }
                return;
            }

            if (!string.IsNullOrEmpty(attribute.AttributePrefix))
            {
                attribute.AddError("Attributes with XML namespaces are not supported!");
                return;
            }

            // find the property
            var property = FindProperty(control.Metadata, attribute.AttributeName);

            if (property != null)
            {
                if (control.HasProperty(property))
                {
                    attribute.AttributeNameNode.AddError($"control '{ ((DothtmlElementNode)control.DothtmlNode).FullTagName }' already has property '{ attribute.AttributeName }'.");
                }

                if (property.IsBindingProperty || property.DataContextManipulationAttribute != null) // when DataContextManipulationAttribute is set, lets hope that author knows what is he doing.
                {
                    dataContext = GetDataContextChange(dataContext, control, property);
                }

                if (!property.MarkupOptions.MappingMode.HasFlag(MappingMode.Attribute))
                {
                    attribute.AddError($"The property '{property.FullName}' cannot be used as a control attribute!");
                    return;
                }

                // set the property
                if (attribute.ValueNode == null)
                {
                    attribute.AddError($"The attribute '{property.Name}' on the control '{control.Metadata.Type.FullName}' must have a value!");
                }
                else if (attribute.ValueNode is DothtmlValueBindingNode)
                {
                    // binding
                    var bindingNode = (attribute.ValueNode as DothtmlValueBindingNode).BindingNode;
                    if (property.IsVirtual)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain bindings because it's not DotvvmProperty.");
                    }
                    else if (treatBindingAsHardCodedValue.Contains(bindingNode.Name))
                    {
                        if (!property.MarkupOptions.AllowHardCodedValue)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding, it can contain only hard-coded value or resource binding.");
                        }
                    }
                    else
                    {
                        if (!property.MarkupOptions.AllowBinding)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding.");
                        }
                    }
                    var binding         = ProcessBinding(bindingNode, dataContext);
                    var bindingProperty = treeBuilder.BuildPropertyBinding(property, binding, attribute);
                    treeBuilder.SetProperty(control, bindingProperty);
                }
                else
                {
                    // hard-coded value in markup
                    if (!property.MarkupOptions.AllowHardCodedValue)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain hard coded value.");
                    }

                    var textValue     = attribute.ValueNode as DothtmlValueTextNode;
                    var value         = ConvertValue(textValue.Text, property.PropertyType);
                    var propertyValue = treeBuilder.BuildPropertyValue(property, value, attribute);
                    treeBuilder.SetProperty(control, propertyValue);
                }
            }
            else if (control.Metadata.HasHtmlAttributesCollection)
            {
                // if the property is not found, add it as an HTML attribute
                try
                {
                    treeBuilder.SetHtmlAttribute(control, ProcessAttributeValue(attribute, dataContext));
                }
                catch (NotSupportedException ex)
                {
                    if (ex.InnerException == null)
                    {
                        throw;
                    }
                    else
                    {
                        attribute.AddError(ex.Message);
                    }
                }
            }
            else
            {
                attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!");
            }
        }
예제 #19
0
        /// <summary>
        /// Gets the data context change behavior for the specified control property.
        /// </summary>
        protected virtual IDataContextStack GetDataContextChange(IDataContextStack dataContext, IAbstractControl control, IPropertyDescriptor property)
        {
            if (dataContext == null)
            {
                return(null);
            }

            var manipulationAttribute = property != null ? property.DataContextManipulationAttribute : control.Metadata.DataContextManipulationAttribute;

            if (manipulationAttribute != null)
            {
                return(manipulationAttribute.ChangeStackForChildren(dataContext, control, property, (parent, changeType) => CreateDataContextTypeStack(changeType, parentDataContextStack: parent)));
            }

            var attributes = property != null ? property.DataContextChangeAttributes : control.Metadata.DataContextChangeAttributes;

            if (attributes == null || attributes.Length == 0)
            {
                return(dataContext);
            }

            try
            {
                var(type, extensionParameters) = ApplyContextChange(dataContext, attributes, control, property);

                if (type == null)
                {
                    return(dataContext);
                }
                else
                {
                    return(CreateDataContextTypeStack(type, parentDataContextStack: dataContext, extensionParameters: extensionParameters.ToArray()));
                }
            }
            catch (Exception exception)
            {
                var node = property != null && control.TryGetProperty(property, out var v) ? v.DothtmlNode : control.DothtmlNode;
                node?.AddError($"Could not compute the type of DataContext: {exception}");

                return(CreateDataContextTypeStack(null, parentDataContextStack: dataContext));
            }
        }
예제 #20
0
        /// <summary>
        /// Processes the element which contains property value.
        /// </summary>
        private IAbstractPropertySetter ProcessElementProperty(IAbstractControl control, IPropertyDescriptor property, IEnumerable <DothtmlNode> elementContent, DothtmlElementNode propertyWrapperElement)
        {
            IEnumerable <IAbstractControl> filterByType(ITypeDescriptor type, IEnumerable <IAbstractControl> controls) =>
            FilterOrError(controls,
                          c => c.Metadata.Type.IsAssignableTo(type),
                          c => {
                // empty nodes are only filtered, non-empty nodes cause errors
                if (c.DothtmlNode.IsNotEmpty())
                {
                    c.DothtmlNode.AddError($"Control type {c.Metadata.Type.FullName} can't be used in collection of type {type.FullName}.");
                }
            });

            // resolve data context
            var dataContext = control.DataContextTypeStack;

            dataContext = GetDataContextChange(dataContext, control, property);

            // the element is a property
            if (IsTemplateProperty(property))
            {
                // template
                return(treeBuilder.BuildPropertyTemplate(property, ProcessTemplate(control, elementContent, dataContext), propertyWrapperElement));
            }
            else if (IsCollectionProperty(property))
            {
                var collectionType = GetCollectionType(property);
                // collection of elements
                var collection = elementContent.Select(childObject => ProcessNode(control, childObject, control.Metadata, dataContext));
                if (collectionType != null)
                {
                    collection = filterByType(collectionType, collection);
                }

                return(treeBuilder.BuildPropertyControlCollection(property, collection.ToArray(), propertyWrapperElement));
            }
            else if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(string))))
            {
                // string property
                var strings = FilterNodes <DothtmlLiteralNode>(elementContent, property);
                var value   = string.Concat(strings.Select(s => s.Value));
                return(treeBuilder.BuildPropertyValue(property, value, propertyWrapperElement));
            }
            else if (IsControlProperty(property))
            {
                var children = filterByType(property.PropertyType, elementContent.Select(childObject => ProcessNode(control, childObject, control.Metadata, dataContext))).ToArray();
                if (children.Length > 1)
                {
                    // try with the empty nodes are excluded
                    children = children.Where(c => c.DothtmlNode.IsNotEmpty()).ToArray();
                    if (children.Length > 1)
                    {
                        foreach (var c in children.Skip(1))
                        {
                            c.DothtmlNode.AddError($"The property '{property.MarkupOptions.Name}' can have only one child element!");
                        }
                    }
                }
                if (children.Length >= 1)
                {
                    return(treeBuilder.BuildPropertyControl(property, children[0], propertyWrapperElement));
                }
                else
                {
                    return(treeBuilder.BuildPropertyControl(property, null, propertyWrapperElement));
                }
            }
            else
            {
                control.DothtmlNode.AddError($"The property '{property.FullName}' is not supported!");
                return(treeBuilder.BuildPropertyValue(property, null, propertyWrapperElement));
            }
        }
예제 #21
0
 public abstract IDataContextStack ChangeStackForChildren(IDataContextStack original, IAbstractControl control, IPropertyDescriptor property, Func <IDataContextStack, ITypeDescriptor, IDataContextStack> createNewFrame);
 public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
 {
     return new ResolvedTypeDescriptor(type);
 }
 public override IDataContextStack ChangeStackForChildren(IDataContextStack original, IAbstractControl control, IPropertyDescriptor property, Func <IDataContextStack, ITypeDescriptor, IDataContextStack> createNewFrame)
 {
     return(original.Parent);
 }
예제 #24
0
 public override IDataContextStack ChangeStackForChildren(IDataContextStack original, IAbstractControl control, IPropertyDescriptor property, Func <IDataContextStack, ITypeDescriptor, IDataContextStack> createNewFrame)
 {
     return(DataContextStack.Create(ResolvedTypeDescriptor.ToSystemType(original.DataContextType), (DataContextStack)original.Parent,
                                    bindingPropertyResolvers: new Delegate[] {
         new Func <ParsedExpressionBindingProperty, ParsedExpressionBindingProperty>(e => {
             if (e.Expression.NodeType == ExpressionType.Constant && (string)((ConstantExpression)e.Expression).Value == "abc")
             {
                 return new ParsedExpressionBindingProperty(Expression.Constant("def"));
             }
             else
             {
                 return e;
             }
         })
     }));
 }
        public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
        {
            if (!control.Metadata.TryGetProperty(PropertyName, out var controlProperty))
            {
                throw new Exception($"The property '{PropertyName}' was not found on control '{control.Metadata.Type}'!");
            }

            if (control.TryGetProperty(controlProperty, out var setter) && setter is IAbstractPropertyBinding binding)
            {
                return(binding.Binding.ResultType);
            }

            return(controlProperty.PropertyType);
        }
예제 #26
0
        => control.Content.All(c => !DothtmlNodeHelper.IsNotEmpty(c.DothtmlNode));     // allow only whitespace literals

        public static bool HasProperty(this IAbstractControl control, IPropertyDescriptor property)
        {
            IAbstractPropertySetter blackHole;

            return(control.TryGetProperty(property, out blackHole));
        }
예제 #27
0
        /// <summary>
        /// Processes the content of the control node.
        /// </summary>
        public void ProcessControlContent(IAbstractControl control, IEnumerable <DothtmlNode> nodes)
        {
            var  content    = new List <DothtmlNode>();
            bool properties = true;

            foreach (var node in nodes)
            {
                var element = node as DothtmlElementNode;
                if (element != null && properties)
                {
                    var property = controlResolver.FindProperty(control.Metadata, element.TagName);
                    if (property != null && string.IsNullOrEmpty(element.TagPrefix) && property.MarkupOptions.MappingMode.HasFlag(MappingMode.InnerElement))
                    {
                        content.Clear();
                        string error;
                        if (!treeBuilder.AddProperty(control, ProcessElementProperty(control, property, element.Content, element), out error))
                        {
                            element.AddError(error);
                        }

                        foreach (var attr in element.Attributes)
                        {
                            attr.AddError("Attributes can't be set on element property.");
                        }
                    }
                    else
                    {
                        content.Add(node);
                        if (node.IsNotEmpty())
                        {
                            properties = false;
                        }
                    }
                }
                else
                {
                    content.Add(node);
                }
            }
            if (control.Metadata.DefaultContentProperty != null)
            {
                if (control.HasProperty(control.Metadata.DefaultContentProperty))
                {
                    foreach (var c in content)
                    {
                        if (c.IsNotEmpty())
                        {
                            c.AddError($"Property { control.Metadata.DefaultContentProperty.FullName } was already set.");
                        }
                    }
                }
                else if (!content.All(c => c is DothtmlLiteralNode && string.IsNullOrWhiteSpace(((DothtmlLiteralNode)c).Value)))
                {
                    string error;
                    if (!treeBuilder.AddProperty(control, ProcessElementProperty(control, control.Metadata.DefaultContentProperty, content, null), out error))
                    {
                        content.First().AddError(error);
                    }
                }
            }
            else
            {
                if (!control.Metadata.IsContentAllowed)
                {
                    foreach (var item in content)
                    {
                        if (item.IsNotEmpty())
                        {
                            item.AddError($"Content not allowed inside {control.Metadata.Type.Name}.");
                        }
                    }
                }
                else
                {
                    ResolveControlContentImmediately(control, content);
                }
            }
        }
예제 #28
0
 public static IAbstractPropertySetter GetHtmlAttribute(this IAbstractControl control, string memberName) =>
 GetPropertyGroupMember(control, "", memberName);
예제 #29
0
        /// <summary>
        /// Gets the data context change behavior for the specified control property.
        /// </summary>
        protected virtual IDataContextStack GetDataContextChange(IDataContextStack dataContext, IAbstractControl control, IPropertyDescriptor property)
        {
            if (dataContext == null)
            {
                return(null);
            }

            var manipulationAttribute = property != null ? property.DataContextManipulationAttribute : control.Metadata.DataContextManipulationAttribute;

            if (manipulationAttribute != null)
            {
                return(manipulationAttribute.ChangeStackForChildren(dataContext, control, property, (parent, changeType) => CreateDataContextTypeStack(changeType, parentDataContextStack: parent)));
            }

            var attributes = property != null ? property.DataContextChangeAttributes : control.Metadata.DataContextChangeAttributes;

            if (attributes == null || attributes.Length == 0)
            {
                return(dataContext);
            }

            var(type, extensionParameters) = ApplyContextChange(dataContext, attributes, control, property);
            return(CreateDataContextTypeStack(type, parentDataContextStack: dataContext, extensionParameters: extensionParameters.ToArray()));
        }
예제 #30
0
 public static bool HasEmptyContent(this IAbstractControl control)
 => control.Content.All(c => !DothtmlNodeHelper.IsNotEmpty(c.DothtmlNode));     // allow only whitespace literals
예제 #31
0
 public void SetProperty(IAbstractControl control, IAbstractPropertySetter setter)
 {
     ((ResolvedControl)control).SetProperty((ResolvedPropertySetter)setter);
 }
예제 #32
0
 public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
 {
     return(TypeDescriptorUtils.GetCollectionItemType(dataContext));
 }
예제 #33
0
        public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
        {
            if (!control.Metadata.TryGetProperty(PropertyName, out var controlProperty))
            {
                throw new Exception($"The property '{PropertyName}' was not found on control '{control.Metadata.Type}'!");
            }

            if (control.TryGetProperty(controlProperty, out var setter))
            {
                return(setter is IAbstractPropertyBinding binding
                    ? binding.Binding.ResultType
                    : dataContext);
            }

            if (AllowMissingProperty)
            {
                return(dataContext);
            }

            throw new Exception($"Property '{PropertyName}' is required on '{control.Metadata.Type.Name}'.");
        }
예제 #34
0
        /// <summary>
        /// Processes the attribute node.
        /// </summary>
        private void ProcessAttribute(DothtmlAttributeNode attribute, IAbstractControl control, IDataContextStack dataContext)
        {
            var name = attribute.AttributePrefix == null ? attribute.AttributeName : attribute.AttributePrefix + ":" + attribute.AttributeName;

            // find the property
            var property = controlResolver.FindProperty(control.Metadata, name);

            if (property != null)
            {
                if (property.IsBindingProperty || property.DataContextManipulationAttribute != null) // when DataContextManipulationAttribute is set, lets hope that author knows what is he doing.
                {
                    dataContext = GetDataContextChange(dataContext, control, property);
                }

                if (!property.MarkupOptions.MappingMode.HasFlag(MappingMode.Attribute))
                {
                    attribute.AddError($"The property '{property.FullName}' cannot be used as a control attribute!");
                    return;
                }

                // set the property
                if (attribute.ValueNode == null)
                {
                    // implicitly set boolean property
                    if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(bool))))
                    {
                        string error;
                        if (!treeBuilder.AddProperty(control, treeBuilder.BuildPropertyValue(property, true, attribute), out error))
                        {
                            attribute.AddError(error);
                        }
                    }
                    else if (property.MarkupOptions.AllowAttributeWithoutValue)
                    {
                        string error;
                        if (!treeBuilder.AddProperty(control, treeBuilder.BuildPropertyValue(property, (property as DotVVM.Framework.Binding.DotvvmProperty)?.DefaultValue, attribute), out error))
                        {
                            attribute.AddError(error);
                        }
                    }
                    else
                    {
                        attribute.AddError($"The attribute '{property.Name}' on the control '{control.Metadata.Type.FullName}' must have a value!");
                    }
                }
                else if (attribute.ValueNode is DothtmlValueBindingNode)
                {
                    // binding
                    var bindingNode = (attribute.ValueNode as DothtmlValueBindingNode).BindingNode;
                    if (property.IsVirtual && !property.IsBindingProperty && property.PropertyType.FullName != "System.Object")
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain bindings because it's not DotvvmProperty.");
                    }
                    else if (!treatBindingAsHardCodedValue.Contains(bindingNode.Name))
                    {
                        if (!property.MarkupOptions.AllowBinding)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding.");
                        }
                    }
                    var    binding         = ProcessBinding(bindingNode, dataContext, property);
                    var    bindingProperty = treeBuilder.BuildPropertyBinding(property, binding, attribute);
                    string error;
                    if (!treeBuilder.AddProperty(control, bindingProperty, out error))
                    {
                        attribute.AddError(error);
                    }
                }
                else
                {
                    // hard-coded value in markup
                    if (!property.MarkupOptions.AllowHardCodedValue)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain hard coded value.");
                    }

                    var    textValue     = attribute.ValueNode as DothtmlValueTextNode;
                    var    value         = ConvertValue(WebUtility.HtmlDecode(textValue.Text), property.PropertyType);
                    var    propertyValue = treeBuilder.BuildPropertyValue(property, value, attribute);
                    string error;
                    if (!treeBuilder.AddProperty(control, propertyValue, out error))
                    {
                        attribute.AddError(error);
                    }
                }
            }
            else
            {
                attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!");
            }
        }
예제 #35
0
 public bool AddProperty(IAbstractControl control, IAbstractPropertySetter setter, out string error)
 {
     return(((ResolvedControl)control).SetProperty((ResolvedPropertySetter)setter, false, out error));
 }
예제 #36
0
        /// <summary>
        /// Processes the element which contains property value.
        /// </summary>
        private IAbstractPropertySetter ProcessElementProperty(IAbstractControl control, IPropertyDescriptor property, IEnumerable <DothtmlNode> elementContent, DothtmlElementNode propertyWrapperElement)
        {
            // resolve data context
            var dataContext = control.DataContextTypeStack;

            dataContext = GetDataContextChange(dataContext, control, property);

            // the element is a property
            if (IsTemplateProperty(property))
            {
                // template
                return(treeBuilder.BuildPropertyTemplate(property, ProcessTemplate(control, elementContent, dataContext), propertyWrapperElement));
            }
            else if (IsCollectionProperty(property))
            {
                var collectionType = GetCollectionType(property);
                // collection of elements
                var collection =
                    FilterNodes <DothtmlElementNode>(elementContent, property)
                    .Select(childObject => ProcessObjectElement(childObject, dataContext));
                if (collectionType != null)
                {
                    collection = FilterOrError(collection,
                                               c => c.Metadata.Type.IsAssignableTo(collectionType),
                                               c => c.DothtmlNode.AddError($"Control type {c.Metadata.Type.FullName} can't be used in collection of type {collectionType.FullName}."));
                }

                return(treeBuilder.BuildPropertyControlCollection(property, collection.ToArray(), propertyWrapperElement));
            }
            else if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(string))))
            {
                // string property
                var strings = FilterNodes <DothtmlLiteralNode>(elementContent, property);
                var value   = string.Concat(strings.Select(s => s.Value));
                return(treeBuilder.BuildPropertyValue(property, value, propertyWrapperElement));
            }
            else if (IsControlProperty(property))
            {
                // new object
                var children = FilterNodes <DothtmlElementNode>(elementContent, property).ToList();
                if (children.Count > 1)
                {
                    foreach (var c in children.Skip(1))
                    {
                        c.AddError($"The property '{property.MarkupOptions.Name}' can have only one child element!");
                    }
                    children = children.Take(1).ToList();
                }
                if (children.Count == 1)
                {
                    return(treeBuilder.BuildPropertyControl(property, ProcessObjectElement(children[0], dataContext), propertyWrapperElement));
                }
                else
                {
                    return(treeBuilder.BuildPropertyControl(property, null, propertyWrapperElement));
                }
            }
            else
            {
                control.DothtmlNode.AddError($"The property '{property.FullName}' is not supported!");
                return(treeBuilder.BuildPropertyValue(property, null, propertyWrapperElement));
            }
        }
예제 #37
0
 public void AddChildControl(IAbstractContentNode control, IAbstractControl child)
 {
     ((ResolvedContentNode)control).AddChild((ResolvedControl)child);
 }
예제 #38
0
        public static (ITypeDescriptor type, List <BindingExtensionParameter> extensionParameters) ApplyContextChange(IDataContextStack dataContext, DataContextChangeAttribute[] attributes, IAbstractControl control, IPropertyDescriptor property)
        {
            var type = dataContext.DataContextType;
            var extensionParameters = new List <BindingExtensionParameter>();

            foreach (var attribute in attributes.OrderBy(a => a.Order))
            {
                if (type == null)
                {
                    break;
                }
                extensionParameters.AddRange(attribute.GetExtensionParameters(type));
                type = attribute.GetChildDataContextType(type, dataContext, control, property);
            }
            return(type, extensionParameters);
        }
예제 #39
0
 public IAbstractPropertyControl BuildPropertyControl(IPropertyDescriptor property, IAbstractControl control, DothtmlElementNode wrapperElement)
 {
     return(new ResolvedPropertyControl((DotvvmProperty)property, (ResolvedControl)control)
     {
         DothtmlNode = wrapperElement
     });
 }
		public override IDataContextStack ChangeStackForChildren(IDataContextStack original, IAbstractControl control, IPropertyDescriptor property, Func<IDataContextStack, ITypeDescriptor, IDataContextStack> createNewFrame)
		{
			return original.Parent;
		}
 public override ITypeDescriptor GetChildDataContextType(ITypeDescriptor dataContext, IDataContextStack controlContextStack, IAbstractControl control, IPropertyDescriptor property = null)
 {
     return TypeDescriptorUtils.GetCollectionItemType(dataContext);
 }