private static List <FormRow> CreateRows(IEnumerable <AttrElementTuple> elements, int gridLength) { var rows = new List <FormRow>(); List <FormElement> currentLine = null; foreach (var item in elements) { var attr = item.Attr; var element = item.Element; if (!attr.StartsNewRow) { rows.Add(new FormRow(false, attr.RowSpan) { Elements = { new FormElementContainer(0, gridLength, new List <FormElement> { element }) } }); currentLine = null; continue; } if (attr.ShareLine) { if (currentLine != null) { currentLine.Add(element); } else { currentLine = new List <FormElement> { element }; var row = new FormRow(); row.Elements.Add(new FormElementContainer(0, gridLength, currentLine)); rows.Add(row); } } else { rows.Add(new FormRow { Elements = { new FormElementContainer(0, gridLength, new List <FormElement> { element }) } }); currentLine = null; } } return(rows); }
private FormDefinition BuildDefinition(Type type) { // Only classes are allowed. // Primitives should be retrieved from prebuilt definitions. if (!type.IsClass || typeof(MulticastDelegate).IsAssignableFrom(type.BaseType)) { return(null); } var formDefinition = new FormDefinition(type); var mode = DefaultFields.AllExcludingReadonly; var grid = new[] { 1d }; var beforeFormContent = new List <AttrElementTuple>(); var afterFormContent = new List <AttrElementTuple>(); foreach (var attribute in type.GetCustomAttributes()) { switch (attribute) { case ResourceAttribute resource: formDefinition.Resources.Add(resource.Name, resource.Value is string expr ? (IValueProvider)BoundExpression.Parse(expr) : new LiteralValue(resource.Value)); break; case FormAttribute form: mode = form.Mode; grid = form.Grid; if (grid == null || grid.Length < 1) { grid = new[] { 1d }; } break; case FormContentAttribute contentAttribute: if (contentAttribute.Placement == Placement.After) { afterFormContent.Add(new AttrElementTuple(contentAttribute, contentAttribute.GetElement())); } else if (contentAttribute.Placement == Placement.Before) { beforeFormContent.Add(new AttrElementTuple(contentAttribute, contentAttribute.GetElement())); } break; case MetaAttribute meta: if (!string.IsNullOrEmpty(meta.Name)) { formDefinition.Metadata[meta.Name] = meta.Value; } break; } } beforeFormContent.Sort((a, b) => a.Attr.Position.CompareTo(b.Attr.Position)); afterFormContent.Sort((a, b) => a.Attr.Position.CompareTo(b.Attr.Position)); var gridLength = grid.Length; // Pass one - get list of valid properties. var properties = Utilities .GetProperties(type, mode) .Select(p => new PropertyInfoWrapper(p)) .ToArray(); // Pass two - build form elements. var elements = new List <ElementWrapper>(); foreach (var property in properties) { var deserializer = TryGetDeserializer(property.PropertyType); // Query property builders. var element = Build(property, deserializer); if (element == null) { // Unhandled properties are ignored. continue; } // Pass three - initialize elements. foreach (var initializer in FieldInitializers) { initializer.Initialize(element, property, deserializer); } var wrapper = new ElementWrapper(element, property); // Set layout. var attr = property.GetCustomAttribute <FieldAttribute>(); if (attr != null) { wrapper.Position = attr.Position; wrapper.Row = attr.Row; wrapper.Column = attr.Column; wrapper.ColumnSpan = attr.ColumnSpan; } elements.Add(wrapper); } // Pass four - order elements. elements = elements.OrderBy(element => element.Position).ToList(); // Pass five - group rows and calculate layout. var layout = PerformLayout(grid, elements); // Pass six - add attached elements. var rows = new List <FormRow>(); // Before form. rows.AddRange(CreateRows(beforeFormContent, gridLength)); foreach (var row in layout) { var before = new List <AttrElementTuple>(); var after = new List <AttrElementTuple>(); foreach (var element in row.Elements) { var property = element.Property; foreach (var attr in property.GetCustomAttributes <FormContentAttribute>()) { if (attr.Placement == Placement.Before) { before.Add(new AttrElementTuple(attr, attr.GetElement())); } else if (attr.Placement == Placement.After) { after.Add(new AttrElementTuple(attr, attr.GetElement())); } } } before.Sort((a, b) => a.Attr.Position.CompareTo(b.Attr.Position)); after.Sort((a, b) => a.Attr.Position.CompareTo(b.Attr.Position)); // Before element. rows.AddRange(CreateRows(before, gridLength)); // Field row. var formRow = new FormRow(); formRow.Elements.AddRange( row.Elements.Select(w => { var inlineElements = w.Property .GetCustomAttributes <FormContentAttribute>() .Where(attr => attr.Placement == Placement.Inline) .Select(attr => new AttrElementTuple(attr, attr.GetElement())) .OrderBy(tuple => tuple.Attr.Position) .ToList(); w.Element.LinePosition = (Position)(-1); if (inlineElements.Count != 0) { return(new FormElementContainer(w.Column, w.ColumnSpan, inlineElements .Select(t => t.Element) .Concat(new[] { w.Element }) .ToList())); } return(new FormElementContainer(w.Column, w.ColumnSpan, w.Element)); })); rows.Add(formRow); // After element. rows.AddRange(CreateRows(after, gridLength)); } // After form. rows.AddRange(CreateRows(afterFormContent, gridLength)); // Wrap up everything. formDefinition.Grid = grid; formDefinition.FormRows = rows; formDefinition.Freeze(); foreach (var element in formDefinition.FormRows.SelectMany(r => r.Elements).SelectMany(c => c.Elements)) { element.Freeze(); } return(formDefinition); }