//// This code is part of Document Solutions for Word demos.// Copyright (c) MESCIUS inc. All rights reserved.//usingSystem;usingSystem.IO;usingSystem.Globalization;usingGrapeCity.Documents.Word; namespaceDsWordWeb.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.publicclassDataTplFixPbbAutoRange {// Code demonstrating the problem:GcWordDocumentProblem() {usingvar oceans = File.OpenRead(Path.Combine("Resources", "data", "oceans.json"));var doc = newGcWordDocument(); doc.DataTemplate.DataSources.Add("ds", oceans);// Limit the number of processed records so that everything fits on a single page:var take3 = newint[] { 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:GcWordDocumentFix() {usingvar oceans = File.OpenRead(Path.Combine("Resources", "data", "oceans.json"));var doc = newGcWordDocument(); doc.DataTemplate.DataSources.Add("ds", oceans);// Limit the number of processed records so that everything fits on a single page:var take3 = newint[] { 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; } publicGcWordDocumentCreateDocx() {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; } }}