Skip to main content

Practice Exercises - Fundamentals

How to Use These Exercises​

These hands-on exercises reinforce what you've learned in the fundamentals section. Start with Exercise 1 and work your way up.

Recommended approach:

  1. Read the exercise
  2. Try it yourself first
  3. Check the solution
  4. Understand why it works

Exercise 1: Explore a DOCX File​

Difficulty: ⭐ Beginner
Time: 10 minutes
Skills: Document structure, file exploration

Task​

  1. Create a simple DOCX file in Word with:

    • One paragraph of text
    • One small image
    • Bold formatting on some words
  2. Rename it from .docx to .zip

  3. Extract the contents

  4. Find and open these files:

    • /word/document.xml
    • /word/_rels/document.xml.rels
    • [Content_Types].xml

Questions​

  1. Where is your paragraph text located in document.xml?
  2. How many relationship entries are in document.xml.rels?
  3. What is the image file format in /word/media/?
Solution & Explanation

Solution​

  1. Paragraph text: Look for <w:t> elements inside <w:r> inside <w:p> in document.xml

  2. Relationships: Open /word/_rels/document.xml.rels and count <Relationship> elements. You should see:

    • One for styles.xml
    • One for the image
    • Possibly others for themes, fonts, etc.
  3. Image format: Look in /word/media/ folder. Common formats:

    • .png for PNG images
    • .jpg or .jpeg for JPEG images

What You Learned​

  • DOCX is a ZIP archive
  • Content is in XML files
  • Images are stored separately
  • Relationships connect parts together

Next Step​

Try modifying the text directly in document.xml, re-ZIP, and open in Word!


Exercise 2: Fix Broken XML​

Difficulty: ⭐⭐ Intermediate
Time: 15 minutes
Skills: Element hierarchy, common mistakes

Task​

Fix the errors in this XML snippet:

<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:t>This is wrong</w:t>

<w:p>
<w:r>
<w:tbl>
<w:tr>
<w:tc>
<w:t>Cell content</w:t>
</w:tc>
</w:tr>
</w:tbl>
</w:r>
</w:p>

<w:p>
<w:r>
<w:sdt>
<w:sdtContent>
<w:t>Field value</w:t>
</w:sdtContent>
</w:sdt>
</w:r>
</w:p>
</w:body>
</w:document>
Solution & Explanation

Errors Found​

  1. Line 3: Text directly in body without paragraph/run wrapper
  2. Line 7: Table inside run (tables are block-level)
  3. Line 10: Cell content missing paragraph wrapper
  4. Line 18: SDT inside run (block SDT should not be in run)
  5. Line 20: Text missing run wrapper inside SDT

Corrected Version​

<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<!-- Fix 1: Wrap text in paragraph and run -->
<w:p>
<w:r>
<w:t>This is correct</w:t>
</w:r>
</w:p>

<!-- Fix 2: Move table to body level -->
<w:tbl>
<w:tblPr>
<w:tblW w:w="5000" w:type="pct"/>
</w:tblPr>
<w:tblGrid>
<w:gridCol w:w="5000"/>
</w:tblGrid>
<w:tr>
<w:tc>
<!-- Fix 3: Add paragraph wrapper in cell -->
<w:p>
<w:r>
<w:t>Cell content</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>

<!-- Fix 4: Move SDT to body level -->
<w:sdt>
<w:sdtPr>
<w:tag w:val="fieldName"/>
</w:sdtPr>
<w:sdtContent>
<!-- Fix 5: Add paragraph and run wrappers -->
<w:p>
<w:r>
<w:t>Field value</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
</w:body>
</w:document>

Key Lessons​

  • Text always needs: body → p → r → t
  • Tables are block-level (not inside runs)
  • Table cells must have paragraphs
  • Block SDTs are not inside runs

Exercise 3: Create a Simple Paragraph Programmatically​

Difficulty: ⭐⭐ Intermediate
Time: 20 minutes
Skills: C# OpenXML SDK, proper hierarchy

Task​

Using the OpenXML SDK, write code to create a document with:

  • One centered paragraph
  • Bold text "Property Address: "
  • Normal text "123 Main Street"
  • 14pt font size

Starting Code​

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

public void CreateDocument()
{
using var doc = WordprocessingDocument.Create(
"output.docx",
WordprocessingDocumentType.Document
);

var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.Body = new Body();

// TODO: Add your paragraph here

doc.Save();
}
Solution & Explanation

Complete Solution​

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

public void CreateDocument()
{
using var doc = WordprocessingDocument.Create(
"output.docx",
WordprocessingDocumentType.Document
);

var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.Body = new Body();

// Create paragraph with centered alignment
var paragraph = new Paragraph();

// Add paragraph properties
var paragraphProperties = new ParagraphProperties(
new Justification { Val = JustificationValues.Center }
);
paragraph.Append(paragraphProperties);

// Create first run for bold text
var run1 = new Run();
var run1Properties = new RunProperties(
new Bold(),
new FontSize { Val = "28" } // 14pt = 28 half-points
);
run1.Append(run1Properties);
run1.Append(new Text("Property Address: "));
paragraph.Append(run1);

// Create second run for normal text
var run2 = new Run();
var run2Properties = new RunProperties(
new FontSize { Val = "28" } // 14pt
);
run2.Append(run2Properties);
run2.Append(new Text("123 Main Street"));
paragraph.Append(run2);

// Add paragraph to body
body.Append(paragraph);

doc.Save();
}

Alternative: Fluent Style​

body.Append(
new Paragraph(
new ParagraphProperties(
new Justification { Val = JustificationValues.Center }
),
new Run(
new RunProperties(
new Bold(),
new FontSize { Val = "28" }
),
new Text("Property Address: ")
),
new Run(
new RunProperties(
new FontSize { Val = "28" }
),
new Text("123 Main Street")
)
)
);

Key Points​

  1. Font size: Specified in half-points (28 = 14pt)
  2. Two runs: Different formatting requires separate runs
  3. Paragraph properties: Alignment at paragraph level
  4. Run properties: Bold and size at run level

Exercise 4: Create a Simple Table​

Difficulty: ⭐⭐⭐ Advanced
Time: 30 minutes
Skills: Tables, proper cell structure

Task​

Create a 2×2 table with:

  • Border around all cells
  • First row with bold headers "Name" and "Value"
  • Second row with "Property Type" and "Single Family"

Requirements​

  • Table should be 100% page width
  • Cells should be equal width
  • Don't forget: cells must have paragraphs!
Solution & Explanation

Complete Solution​

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

public void CreateTableDocument()
{
using var doc = WordprocessingDocument.Create(
"table.docx",
WordprocessingDocumentType.Document
);

var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.Body = new Body();

// Create table
var table = new Table();

// Table properties
var tableProperties = new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct }, // 100%
new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 4 },
new BottomBorder { Val = BorderValues.Single, Size = 4 },
new LeftBorder { Val = BorderValues.Single, Size = 4 },
new RightBorder { Val = BorderValues.Single, Size = 4 },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 4 },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 4 }
)
);
table.Append(tableProperties);

// Table grid (2 equal columns)
var tableGrid = new TableGrid(
new GridColumn { Width = "2500" },
new GridColumn { Width = "2500" }
);
table.Append(tableGrid);

// Header row
var headerRow = new TableRow();

// Cell 1: "Name" (bold)
var headerCell1 = new TableCell();
headerCell1.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));
headerCell1.Append(new Paragraph(
new Run(
new RunProperties(new Bold()),
new Text("Name")
)
));
headerRow.Append(headerCell1);

// Cell 2: "Value" (bold)
var headerCell2 = new TableCell();
headerCell2.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));
headerCell2.Append(new Paragraph(
new Run(
new RunProperties(new Bold()),
new Text("Value")
)
));
headerRow.Append(headerCell2);

table.Append(headerRow);

// Data row
var dataRow = new TableRow();

// Cell 1: "Property Type"
var dataCell1 = new TableCell();
dataCell1.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));
dataCell1.Append(new Paragraph(
new Run(
new Text("Property Type")
)
));
dataRow.Append(dataCell1);

// Cell 2: "Single Family"
var dataCell2 = new TableCell();
dataCell2.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));
dataCell2.Append(new Paragraph(
new Run(
new Text("Single Family")
)
));
dataRow.Append(dataCell2);

table.Append(dataRow);

// Add table to body
body.Append(table);

doc.Save();
}

Helper Method Version​

private TableCell CreateCell(string text, bool bold = false)
{
var cell = new TableCell();

cell.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));

var run = new Run();
if (bold)
{
run.Append(new RunProperties(new Bold()));
}
run.Append(new Text(text));

cell.Append(new Paragraph(run));

return cell;
}

// Usage:
var headerRow = new TableRow(
CreateCell("Name", bold: true),
CreateCell("Value", bold: true)
);

var dataRow = new TableRow(
CreateCell("Property Type"),
CreateCell("Single Family")
);

Key Points​

  1. Table width: 5000 pct = 100% of page width
  2. Grid definition: Required for proper column layout
  3. Cell paragraphs: Every cell must have at least one paragraph
  4. Border properties: Set at table level applies to all cells

Exercise 5: Add an Image​

Difficulty: ⭐⭐⭐ Advanced
Time: 30 minutes
Skills: Relationships, images, Drawing namespace

Task​

Add an image to a document:

  1. Create a new document
  2. Add a paragraph with text "Property Photo:"
  3. Add the image below the text
  4. Image should be 3 inches wide

Requirements​

  • Use a PNG or JPG image file
  • Create proper relationship
  • Use correct Drawing namespace elements
Solution & Explanation

Complete Solution​

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using A = DocumentFormat.OpenXml.Drawing;
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;

public void CreateDocumentWithImage(string imagePath)
{
using var doc = WordprocessingDocument.Create(
"image-doc.docx",
WordprocessingDocumentType.Document
);

var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.Body = new Body();

// Add text paragraph
body.Append(new Paragraph(
new Run(
new Text("Property Photo:")
)
));

// Add image part and get relationship ID
var imagePart = mainPart.AddImagePart(ImagePartType.Png);
using (var stream = new FileStream(imagePath, FileMode.Open))
{
imagePart.FeedData(stream);
}
string relationshipId = mainPart.GetIdOfPart(imagePart);

// Create image element
const long emusPerInch = 914400;
long widthEmus = 3 * emusPerInch; // 3 inches
long heightEmus = 3 * emusPerInch; // 3 inches (adjust based on aspect ratio)

var element = new Drawing(
new DW.Inline(
new DW.Extent { Cx = widthEmus, Cy = heightEmus },
new DW.EffectExtent { LeftEdge = 0L, TopEdge = 0L,
RightEdge = 0L, BottomEdge = 0L },
new DW.DocProperties { Id = 1U, Name = "Property Photo" },
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks { NoChangeAspect = true }
),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties {
Id = 0U, Name = "photo.png"
},
new PIC.NonVisualPictureDrawingProperties()
),
new PIC.BlipFill(
new A.Blip { Embed = relationshipId },
new A.Stretch(new A.FillRectangle())
),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset { X = 0L, Y = 0L },
new A.Extents { Cx = widthEmus, Cy = heightEmus }
),
new A.PresetGeometry(new A.AdjustValueList())
{ Preset = A.ShapeTypeValues.Rectangle }
)
)
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }
)
) {
DistanceFromTop = 0U,
DistanceFromBottom = 0U,
DistanceFromLeft = 0U,
DistanceFromRight = 0U
}
);

// Add image in paragraph
body.Append(new Paragraph(
new Run(element)
));

doc.Save();
}

Key Concepts​

  1. EMUs: English Metric Units (914,400 per inch)
  2. Relationship: Created by AddImagePart() and referenced by ID
  3. Namespace aliases: A, DW, PIC for different schemas
  4. Image must be in run: Drawing element goes inside run inside paragraph

Simplified Helper​

public static Drawing CreateImage(string relationshipId, 
long widthEmus, long heightEmus)
{
return new Drawing(
new DW.Inline(
new DW.Extent { Cx = widthEmus, Cy = heightEmus },
new DW.DocProperties { Id = 1U, Name = "Image" },
// ... (rest of structure)
)
);
}

Exercise 6: Validate a Document​

Difficulty: ⭐⭐ Intermediate
Time: 15 minutes
Skills: Validation, error detection

Task​

Write a method that validates a document and reports any errors.

Requirements​

  • Use OpenXmlValidator
  • Report error description and XPath
  • Return true if valid, false if errors found
Solution & Explanation

Complete Solution​

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using System;
using System.Linq;

public bool ValidateDocument(string docPath)
{
using var doc = WordprocessingDocument.Open(docPath, false);

var validator = new OpenXmlValidator();
var errors = validator.Validate(doc).ToList();

if (!errors.Any())
{
Console.WriteLine("✅ Document is valid!");
return true;
}

Console.WriteLine($"❌ Found {errors.Count} validation error(s):\n");

foreach (var error in errors)
{
Console.WriteLine($"Error: {error.Description}");
Console.WriteLine($"Path: {error.Path.XPath}");
Console.WriteLine($"Part: {error.Part.Uri}");
Console.WriteLine();
}

return false;
}

Enhanced Version with Error Categories​

public ValidationResult ValidateDocumentDetailed(string docPath)
{
using var doc = WordprocessingDocument.Open(docPath, false);

var validator = new OpenXmlValidator();
var errors = validator.Validate(doc).ToList();

var result = new ValidationResult
{
IsValid = !errors.Any(),
TotalErrors = errors.Count,
SchemaErrors = errors.Count(e => e.ErrorType == ValidationErrorType.Schema),
SemanticErrors = errors.Count(e => e.ErrorType == ValidationErrorType.Semantic),
Errors = errors.Select(e => new ErrorInfo
{
Description = e.Description,
XPath = e.Path.XPath,
PartUri = e.Part?.Uri?.ToString()
}).ToList()
};

return result;
}

public class ValidationResult
{
public bool IsValid { get; set; }
public int TotalErrors { get; set; }
public int SchemaErrors { get; set; }
public int SemanticErrors { get; set; }
public List<ErrorInfo> Errors { get; set; }
}

public class ErrorInfo
{
public string Description { get; set; }
public string XPath { get; set; }
public string PartUri { get; set; }
}

Usage​

// Simple validation
bool isValid = ValidateDocument("report.docx");

// Detailed validation
var result = ValidateDocumentDetailed("report.docx");
if (!result.IsValid)
{
Console.WriteLine($"Schema errors: {result.SchemaErrors}");
Console.WriteLine($"Semantic errors: {result.SemanticErrors}");

foreach (var error in result.Errors)
{
Console.WriteLine($"- {error.Description}");
}
}

Challenge Exercise: Complete Document​

Difficulty: ⭐⭐⭐⭐ Expert
Time: 60 minutes
Skills: All fundamentals

Task​

Create a complete appraisal report summary document with:

  1. Centered title "Property Appraisal Summary"
  2. Property address paragraph
  3. 2×3 table with property details:
    • Headers: "Field" and "Value"
    • Rows: Property Type, Square Feet, Appraised Value
  4. Property photo (3"×3")
  5. Footer note paragraph

Requirements​

  • Use proper styles
  • All elements properly nested
  • Document must validate with no errors
  • Use helper methods to avoid repetition
Solution & Explanation

Complete Solution​

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using A = DocumentFormat.OpenXml.Drawing;
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
using PIC = DocumentFormat.OpenXml.Drawing.Pictures;

public void CreateAppraisalSummary(string imagePath)
{
using var doc = WordprocessingDocument.Create(
"appraisal-summary.docx",
WordprocessingDocumentType.Document
);

var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
var body = mainPart.Document.Body = new Body();

// 1. Title
body.Append(CreateCenteredParagraph("Property Appraisal Summary",
bold: true, fontSize: 28));

// 2. Property Address
body.Append(CreateParagraph("Property Address: 123 Main Street, Springfield, IL"));

// 3. Details Table
body.Append(CreateDetailsTable());

// 4. Property Photo
if (File.Exists(imagePath))
{
body.Append(CreateParagraph("Property Photo:"));
body.Append(AddImage(mainPart, imagePath, 3, 3));
}

// 5. Footer Note
body.Append(CreateParagraph(
"This summary is for informational purposes only.",
italic: true, fontSize: 20
));

doc.Save();

// Validate
ValidateDocument(doc);
}

private Paragraph CreateCenteredParagraph(string text,
bool bold = false,
int fontSize = 24)
{
var para = new Paragraph();

// Center alignment
para.Append(new ParagraphProperties(
new Justification { Val = JustificationValues.Center }
));

// Create run with formatting
var run = new Run();
var runProps = new RunProperties();

if (bold) runProps.Append(new Bold());
runProps.Append(new FontSize { Val = fontSize.ToString() });

run.Append(runProps);
run.Append(new Text(text));
para.Append(run);

return para;
}

private Paragraph CreateParagraph(string text,
bool bold = false,
bool italic = false,
int fontSize = 24)
{
var para = new Paragraph();
var run = new Run();

var runProps = new RunProperties();
if (bold) runProps.Append(new Bold());
if (italic) runProps.Append(new Italic());
runProps.Append(new FontSize { Val = fontSize.ToString() });

run.Append(runProps);
run.Append(new Text(text));
para.Append(run);

return para;
}

private Table CreateDetailsTable()
{
var table = new Table();

// Table properties
table.Append(new TableProperties(
new TableWidth { Width = "5000", Type = TableWidthUnitValues.Pct },
new TableBorders(
new TopBorder { Val = BorderValues.Single, Size = 4 },
new BottomBorder { Val = BorderValues.Single, Size = 4 },
new LeftBorder { Val = BorderValues.Single, Size = 4 },
new RightBorder { Val = BorderValues.Single, Size = 4 },
new InsideHorizontalBorder { Val = BorderValues.Single, Size = 4 },
new InsideVerticalBorder { Val = BorderValues.Single, Size = 4 }
)
));

// Grid
table.Append(new TableGrid(
new GridColumn { Width = "2500" },
new GridColumn { Width = "2500" }
));

// Header row
table.Append(CreateTableRow("Field", "Value", bold: true));

// Data rows
table.Append(CreateTableRow("Property Type", "Single Family"));
table.Append(CreateTableRow("Square Feet", "2,400"));
table.Append(CreateTableRow("Appraised Value", "$450,000"));

return table;
}

private TableRow CreateTableRow(string cell1Text, string cell2Text,
bool bold = false)
{
var row = new TableRow();
row.Append(CreateTableCell(cell1Text, bold));
row.Append(CreateTableCell(cell2Text, bold));
return row;
}

private TableCell CreateTableCell(string text, bool bold = false)
{
var cell = new TableCell();

cell.Append(new TableCellProperties(
new TableCellWidth { Width = "2500", Type = TableWidthUnitValues.Dxa }
));

var run = new Run();
if (bold)
{
run.Append(new RunProperties(new Bold()));
}
run.Append(new Text(text));

cell.Append(new Paragraph(run));

return cell;
}

private Paragraph AddImage(MainDocumentPart mainPart,
string imagePath,
double widthInches,
double heightInches)
{
// Add image part
var imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
using (var stream = new FileStream(imagePath, FileMode.Open))
{
imagePart.FeedData(stream);
}

var relationshipId = mainPart.GetIdOfPart(imagePart);

// Calculate EMUs
const long emusPerInch = 914400;
long widthEmus = (long)(widthInches * emusPerInch);
long heightEmus = (long)(heightInches * emusPerInch);

// Create drawing
var element = new Drawing(
new DW.Inline(
new DW.Extent { Cx = widthEmus, Cy = heightEmus },
new DW.EffectExtent {
LeftEdge = 0L, TopEdge = 0L,
RightEdge = 0L, BottomEdge = 0L
},
new DW.DocProperties { Id = 1U, Name = "Property Photo" },
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks { NoChangeAspect = true }
),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties {
Id = 0U, Name = "photo.jpg"
},
new PIC.NonVisualPictureDrawingProperties()
),
new PIC.BlipFill(
new A.Blip { Embed = relationshipId },
new A.Stretch(new A.FillRectangle())
),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset { X = 0L, Y = 0L },
new A.Extents { Cx = widthEmus, Cy = heightEmus }
),
new A.PresetGeometry(new A.AdjustValueList())
{ Preset = A.ShapeTypeValues.Rectangle }
)
)
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }
)
)
);

return new Paragraph(new Run(element));
}

private void ValidateDocument(WordprocessingDocument doc)
{
var validator = new OpenXmlValidator();
var errors = validator.Validate(doc).ToList();

if (errors.Any())
{
Console.WriteLine($"⚠️ Document has {errors.Count} validation error(s)");
foreach (var error in errors.Take(5)) // Show first 5
{
Console.WriteLine($" - {error.Description}");
}
}
else
{
Console.WriteLine("✅ Document validated successfully!");
}
}

What This Demonstrates​

  • Helper methods for reusability
  • Proper element hierarchy throughout
  • Table creation with proper structure
  • Image insertion with relationships
  • Document validation
  • Clean, maintainable code

Next Steps​

Congratulations on completing the fundamentals! You now have a solid foundation in OOXML document structure.

➡️ Next Level: Intermediate Documentation - Tables, Content Controls, Styles

Practice More:

  • Modify these exercises with your own requirements
  • Create helper methods for common patterns
  • Build a library of reusable components

Get Help:


💡 Remember: Practice makes perfect. The more you work with these concepts, the more natural they become!