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:
- Read the exercise
- Try it yourself first
- Check the solution
- Understand why it works
Exercise 1: Explore a DOCX File
Difficulty: ⭐ Beginner
Time: 10 minutes
Skills: Document structure, file exploration
Task
-
Create a simple DOCX file in Word with:
- One paragraph of text
- One small image
- Bold formatting on some words
-
Rename it from
.docxto.zip -
Extract the contents
-
Find and open these files:
/word/document.xml/word/_rels/document.xml.rels[Content_Types].xml
Questions
- Where is your paragraph text located in
document.xml? - How many relationship entries are in
document.xml.rels? - What is the image file format in
/word/media/?
Solution & Explanation
Solution
-
Paragraph text: Look for
<w:t>elements inside<w:r>inside<w:p>indocument.xml -
Relationships: Open
/word/_rels/document.xml.relsand count<Relationship>elements. You should see:- One for styles.xml
- One for the image
- Possibly others for themes, fonts, etc.
-
Image format: Look in
/word/media/folder. Common formats:.pngfor PNG images.jpgor.jpegfor 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
- Line 3: Text directly in body without paragraph/run wrapper
- Line 7: Table inside run (tables are block-level)
- Line 10: Cell content missing paragraph wrapper
- Line 18: SDT inside run (block SDT should not be in run)
- 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
- Font size: Specified in half-points (28 = 14pt)
- Two runs: Different formatting requires separate runs
- Paragraph properties: Alignment at paragraph level
- 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
- Table width: 5000 pct = 100% of page width
- Grid definition: Required for proper column layout
- Cell paragraphs: Every cell must have at least one paragraph
- 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:
- Create a new document
- Add a paragraph with text "Property Photo:"
- Add the image below the text
- 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
- EMUs: English Metric Units (914,400 per inch)
- Relationship: Created by
AddImagePart()and referenced by ID - Namespace aliases:
A,DW,PICfor different schemas - 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
trueif valid,falseif 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:
- Centered title "Property Appraisal Summary"
- Property address paragraph
- 2×3 table with property details:
- Headers: "Field" and "Value"
- Rows: Property Type, Square Feet, Appraised Value
- Property photo (3"×3")
- 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:
- Review Common Fatal Mistakes when stuck
- Check Element Hierarchy Rules for valid structures
- Use OpenXML SDK Productivity Tool to inspect
💡 Remember: Practice makes perfect. The more you work with these concepts, the more natural they become!