DataTplFixPbbAutoRange.cs
- //
- // This code is part of Document Solutions for Word demos.
- // Copyright (c) MESCIUS inc. All rights reserved.
- //
- using System;
- using System.IO;
- using System.Drawing;
- using System.Collections.Generic;
- using System.Linq;
- using System.Globalization;
- using GrapeCity.Documents.Word;
-
- namespace DsWordWeb.Demos
- {
- // This example shows how to avoid errors related to pbb (paragraph-block-behavior) formatter
- // which are caused by the template engine automatically adding implicit ranges that form
- // an invalid template structure.
- public class DataTplFixPbbAutoRange
- {
- // Code demonstrating the problem:
- GcWordDocument Problem()
- {
- using var oceans = File.OpenRead(Path.Combine("Resources", "data", "oceans.json"));
- var doc = new GcWordDocument();
- doc.DataTemplate.DataSources.Add("ds", oceans);
- // Limit the number of processed records so that everything fits on a single page:
- var take3 = new int[] { 1, 2, 3 };
- doc.DataTemplate.DataSources.Add("take3", take3);
-
- // Define a 1x1 table:
- var table = doc.Body.Tables.Add(1, 1);
- var cell_00 = table[0, 0];
- // Add a pbb (paragraph-block-behavior) range to a cell that will display ocean name
- // (#take3 simply limits the number of oceans and does not affect the problem or fix):
- cell_00.GetRange().Paragraphs.First.GetRange().Runs.Add("{{#take3}:seq(take3)}{{#ds}:follow(take3):pbb()}{{ds.name}}: ");
- // Add a value tag which for every ocean will display the names of its seas:
- cell_00.GetRange().Paragraphs.Add("{{ds.seas.name}}");
- // Add an inner 1x1 table to the cell and get its only cell:
- var inner_cell = cell_00.GetRange().Tables.Add(1, 1)[0, 0];
- // We want the sea names to be duplicated in the inner cell
- // but we do now explicitly specify the #ds.seas range, relying
- // on automatic range expansion.
- // Incorrect: this will not work, see explanation below:
- inner_cell.GetRange().Paragraphs.First.GetRange().Runs.Add("{{ds.seas.name}}");
- // Close #ds range in the cell_00:
- cell_00.GetRange().Paragraphs.Last.GetRange().Runs.Add("{{/ds}}{{/take3}}");
-
- /* Problem explanation:
- *
- * The defined layout that uses concise template syntax:
- *
- * +--------------------------------+ (1)
- * | {{#ds}:pbb()}{{ds.name}} |
- * | {{ds.seas.name}} |
- * | +--------------------+ (2) |
- * | | {{ds.seas.name}} | |
- * | +--------------------+ |
- * | {{/ds}} |
- * +--------------------------------+
- *
- * (1) is cell_11
- * (2) is inner_cell
- *
- * The above concise template uses implicit ranges, and is automatically expanded
- * to the following form by the template engine before processing:
- *
- * +--------------------------------------------+
- * | {{#ds}:pbb()}{{ds.name}} |
- * | {{#ds.seas}:pbb()}{{ds.seas.name}} |
- * | +--------------------------------+ |
- * | | {{ds.seas.name}}{{/ds.seas}} | |
- * | +--------------------------------+ |
- * | {{/ds}} |
- * +--------------------------------------------+
- *
- * Note that a new auto-generated inner range '{{#ds.seas}}' was created by the template engine.
- * When the engine adds an automatic range, it places the start tag ('{{#ds.seas}}' here) immediately
- * in front of the first used tag, and places the end tag ('{{/ds.seas}}' here) immediately after the
- * last used tag. But in this case this results in an invalid template, as the start and end auto generated
- * tags are placed in different cells which is not allowed.
- */
-
- doc.DataTemplate.Process(CultureInfo.GetCultureInfo("en-US"));
- return doc;
- }
-
- // Code demonstrating the fix:
- GcWordDocument Fix()
- {
- using var oceans = File.OpenRead(Path.Combine("Resources", "data", "oceans.json"));
- var doc = new GcWordDocument();
- doc.DataTemplate.DataSources.Add("ds", oceans);
- // Limit the number of processed records so that everything fits on a single page:
- var take3 = new int[] { 1, 2, 3 };
- doc.DataTemplate.DataSources.Add("take3", take3);
-
- // Define a 1x1 table:
- var table = doc.Body.Tables.Add(1, 1, doc.Styles[BuiltInStyleId.ListTable4Accent4]);
- var cell_00 = table[0, 0];
- // Add a pbb (paragraph-block-behavior) range to a cell that will display ocean name
- // (#take3 simply limits the number of oceans and does not affect the problem or fix):
- cell_00.GetRange().Paragraphs.First.GetRange().Runs.Add("{{#take3}:seq(take3)}{{#ds}:follow(take3):pbb()}{{ds.name}}: ");
- // Correct: here we explicitly define the start of '#ds.seas' collection,
- // and add a value tag which for every ocean will display the names of its seas:
- cell_00.GetRange().Paragraphs.First.GetRange().Runs.Add("{{#ds.seas}:pbb()}{{ds.seas.name}}");
- // Add an inner 1x1 table to the cell and get its only cell:
- var inner_cell = cell_00.GetRange().Tables.Add(1, 1, doc.Styles[BuiltInStyleId.ListTable5DarkAccent5])[0, 0];
- // We want the sea names to be duplicated in the inner cell:
- inner_cell.GetRange().Paragraphs.First.GetRange().Runs.Add("{{ds.seas.name}}");
- // Explicitly end the '#ds.seas' range, ensuring that start and end tags remain in the same cell:
- cell_00.GetRange().Paragraphs.Last.GetRange().Runs.Add("{{/ds.seas}}{{/ds}}{{/take3}}");
-
- /*
- * New and valid layout:
- * +--------------------------------------------+(1)
- * | {{#ds}:pbb()}{{ds.name}} |
- * | {{#ds.seas}:pbb()}{{ds.seas.name}} |
- * | +--------------------------------+(2) |
- * | | {{ds.seas.name}} | |
- * | +--------------------------------+ |
- * | {{/ds.seas}}{{/ds}} |
- * +--------------------------------------------+
- *
- * (1) is cell_11
- * (2) is inner_cell
- */
- doc.DataTemplate.Process(CultureInfo.GetCultureInfo("en-US"));
- return doc;
- }
-
- public GcWordDocument CreateDocx()
- {
- GcWordDocument doc;
- try
- {
- // This fails:
- doc = Problem();
- }
- catch (Exception ex)
- {
- // This works:
- doc = Fix();
- // Insert a brief explanation of the problem and the fix into the generated document:
- doc.Body.Paragraphs.Insert(
- $"The error \"{ex.Message}\" occurred because a pbb (paragraph-block-behavior) formatter on an auto-generated range " +
- $"started in one table cell and ended in another. A pbb formatter must start and end in the same table cell.",
- doc.Styles[BuiltInStyleId.BlockText],
- InsertLocation.Start);
- }
- return doc;
- }
- }
- }
-