/// <summary> /// Creates a display from this row tree. The display is an ordered list of rows, in which each row is a form-item list of values and a section for each /// child row-tree display. /// </summary> /// <param name="rowTree">The merge row tree.</param> /// <param name="fieldNameTree">The fields that you want to include in the display. Pass null for all.</param> /// <param name="omitListIfSingleRow">Pass true to omit the root ordered-list component if the tree has exactly one row.</param> /// <param name="useSubtractiveMode">Pass true if you want the field-name tree to represent excluded fields, rather than included fields.</param> public static FlowComponent ToRowTreeDisplay( this MergeRowTree rowTree, MergeFieldNameTree fieldNameTree, bool omitListIfSingleRow = false, bool useSubtractiveMode = false) => new GenericFlowContainer( omitListIfSingleRow && rowTree.Rows.Count() == 1 ? getRow(rowTree.Rows.Single(), fieldNameTree, useSubtractiveMode) : new StackList(from i in rowTree.Rows select getRow(i, fieldNameTree, useSubtractiveMode).ToComponentListItem()).ToCollection(), classes: rowTreeClass);
private static IReadOnlyCollection <FlowComponent> getRow(MergeRow row, MergeFieldNameTree fieldNameTree, bool useSubtractiveMode) { var valueFormItems = (useSubtractiveMode ? row.Values.Where(mergeValue => fieldNameTree?.FieldNames.All(i => i != mergeValue.Name) ?? false) : fieldNameTree?.FieldNames.Select(fieldName => row.Values.Single(i => i.Name == fieldName)) ?? row.Values).Select( mergeValue => { IReadOnlyCollection <PhrasingComponent> value = null; if (mergeValue is MergeValue <string> stringValue) { value = stringValue.Evaluate(false).ToComponents(); } // Use ApplicationException instead of MailMergingException because the field names can easily be validated before this method is called. return(value == null ? throw new ApplicationException("Merge field {0} evaluates to an unsupported type.".FormatWith(mergeValue.Name)) : value.ToFormItem(label: mergeValue.Name.ToComponents())); }) .Materialize(); var valueList = valueFormItems.Any() ? FormItemList.CreateGrid(2, items: valueFormItems).ToCollection() : Enumerable.Empty <FlowComponent>().Materialize(); var children = (useSubtractiveMode ? row.Children.Select( childRowTree => { MergeFieldNameTree childFieldNameTree = null; if (fieldNameTree != null) { var childNameAndFieldNameTree = fieldNameTree.ChildNamesAndChildren.SingleOrDefault(i => i.Item1 == childRowTree.NodeName); childFieldNameTree = childNameAndFieldNameTree != null ? childNameAndFieldNameTree.Item2 : new MergeFieldNameTree(Enumerable.Empty <string>()); } return(new { rowTree = childRowTree, fieldNameTree = childFieldNameTree }); }) .Where(i => i.fieldNameTree != null) : fieldNameTree?.ChildNamesAndChildren.Select( childNameAndFieldNameTree => new { rowTree = row.Children.Single(i => i.NodeName == childNameAndFieldNameTree.Item1), fieldNameTree = childNameAndFieldNameTree.Item2 }) ?? row.Children.Select(childRowTree => new { rowTree = childRowTree, fieldNameTree = (MergeFieldNameTree)null })) .Where(child => child.rowTree.Rows.Any()) .Select( child => new Section( child.rowTree.NodeName, new StackList(child.rowTree.Rows.Select(i => getRow(i, child.fieldNameTree, useSubtractiveMode).ToComponentListItem())).ToCollection())) .Materialize(); return(children.Any() ? valueList.Append(new GenericFlowContainer(children, classes: rowTreeChildClass)).Materialize() : valueList); }