Click here to Skip to main content
15,898,134 members
Articles / Desktop Programming / WPF

Document Template Processor

Rate me:
Please Sign up or sign in to vote.
4.14/5 (7 votes)
27 Jun 2009CPOL4 min read 43.6K   1.3K   40  
This article will describe how to create an RTF template file, parse and populate it with runtime data.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using Microsoft.Win32;

namespace CrazyStory
{
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		private static readonly RoutedUICommand menuFileCommand;
		private readonly OpenFileDialog openFile;
		private readonly SaveFileDialog saveFile;
		private FlowDocument _originalDocument;

		const string filterRtf = "RichText Files (*.rtf)|*.rtf";
		const string filterRtfXaml = filterRtf + "|XAML Files (*.xaml)|*.xaml";

		/// <summary>
		/// 
		/// </summary>
		public MainWindow()
		{
			InitializeComponent();

			string startDirectory =Environment.CurrentDirectory + @"\..\..\..\Documents";
			openFile = new OpenFileDialog();
			openFile.InitialDirectory = startDirectory;

			saveFile = new SaveFileDialog();
			saveFile.InitialDirectory = startDirectory;
		}

		/// <summary>
		/// 
		/// </summary>
		static MainWindow()
		{
			menuFileCommand = new RoutedUICommand("File", "MenuFileCommand", typeof(MainWindow), null);
		}

		#region Commands

		/// <summary>
		/// 
		/// </summary>
		public static RoutedUICommand MenuFileCommand
		{
			get { return menuFileCommand; }
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void menuFileCommand_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			MenuItem item = e.OriginalSource as MenuItem;
			if (item == null) 
				return;

			switch (item.Tag.ToString())
			{
				case "create":
					Create();
					break;
				case "open":
					Open();
					break;
				case "load":
					Load();
					break;
				case "save":
					Save();
					break;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		private void Create()
		{
			openFile.Filter = filterRtf;
			openFile.CheckFileExists = false;
			if (openFile.ShowDialog() == false)
				return;

			using (File.Create(openFile.FileName))
			{
			}

			Process.Start(openFile.FileName);
		}

		/// <summary>
		/// 
		/// </summary>
		private void Open()
		{
			openFile.Filter = filterRtf;
			openFile.CheckFileExists = false;
			if (openFile.ShowDialog() == false)
				return;

			Process.Start(openFile.FileName);
		}

		/// <summary>
		/// 
		/// </summary>
		private void Load()
		{
 			try
			{
				openFile.Filter = filterRtfXaml;
				openFile.CheckFileExists = true;
				if (openFile.ShowDialog() == false)
					return;

				string documentFile = openFile.FileName;
				string extension = Path.GetExtension(documentFile).ToLower();
				FlowDocument flowDocument = null;

				using (FileStream fs = File.Open(documentFile, FileMode.Open))
				{
					switch (extension)
					{
						case ".xaml":
							flowDocument = XamlReader.Load(fs) as FlowDocument;
							break;
						case ".rtf":
							flowDocument = new FlowDocument();
							TextRange range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
							range.Load(fs, DataFormats.Rtf);
							break;
					}

					_originalDocument = flowDocument;
					ParseDocument();

					docViewer.Document = flowDocument;
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		private void Save()
		{
			try
			{
				saveFile.Filter = filterRtfXaml;
				if (saveFile.ShowDialog() == false)
					return;

				string documentFile = saveFile.FileName;
				string extension = Path.GetExtension(documentFile).ToLower();

				FlowDocument flowDocument = docViewer.Document as FlowDocument;
				if (flowDocument == null)
					return;

				using (FileStream fileStream = File.Open(documentFile, FileMode.Create))
				{
					switch (extension)
					{
						case ".xaml":
							XamlWriter.Save(flowDocument, fileStream);
							break;
						case ".rtf":
							TextRange range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
							range.Save(fileStream, DataFormats.Rtf);
							break;
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}

		#endregion

		/// <summary>
		/// Look for the Markers by parsing the document
		/// </summary>
		private void ParseDocument()
		{
			SortedList<string, List<Run>> markers = new SortedList<string, List<Run>>();
			gridWords.Children.Clear();

			foreach (Block block in _originalDocument.Blocks)
			{
				Paragraph paragraph = block as Paragraph;
				if (paragraph == null)
					continue;

				foreach (Inline inline in paragraph.Inlines)
				{
					Span span = inline as Span;
					if (span == null)
						continue;

					// This is where the text would be found to replace
					Run run = span.Inlines.FirstInline as Run;
					if (run == null)
						continue;

					FindMarker(markers, run);
				}
			}
		}

		/// <summary>
		/// Look for the marker within the Run
		/// </summary>
		/// <param name="markers"></param>
		/// <param name="run"></param>
		private void FindMarker(SortedList<string, List<Run>> markers, Run run)
		{
			Regex regex = new Regex(@"(\<\#(?<Name>[^#]+)\#\>)");

			MatchCollection matches = regex.Matches(run.Text);
			foreach (Match match in matches)
			{
				string text = match.Groups["Name"].Value.Trim();

				if (markers.Keys.Contains(text))
				{
					markers[text].Add(run);
					continue;
				}

				// Create list and assign the Run
				List<Run> runList = new List<Run> {run};
				markers.Add(text, runList);

				BuildTextBox(runList, text);
			}
		}

		/// <summary>
		/// Create a TextBox to enter a replacement value into
		/// </summary>
		/// <param name="runList"></param>
		/// <param name="text"></param>
		private void BuildTextBox(List<Run> runList, string text)
		{
			// Add a Row in the Grid for this Prompt
			RowDefinition row = new RowDefinition();
			gridWords.RowDefinitions.Add(row);

			// Create Prompt Label
			Label lbl = new Label();
			lbl.Content = text + ":";

			// Add Prompt Label to the Grid
			Grid.SetColumn(lbl, 0);
			Grid.SetRow(lbl, gridWords.RowDefinitions.Count - 1);
			gridWords.Children.Add(lbl);

			// Add the TextBox to enter value
			TextBox txt = new TextBox();
			Grid.SetColumn(txt, 1);
			Grid.SetRow(txt, gridWords.RowDefinitions.Count - 1);
			gridWords.Children.Add(txt);

			// Attach the List Items to replace with the value in the TextBox
			txt.Tag = runList;
		}

		private void cmdGenerate_Click(object sender, RoutedEventArgs e)
		{
			// Go through each UI Element in the Grid
			foreach (UIElement child in gridWords.Children)
			{
				// Get the TextBox out of Column 1
				if (Grid.GetColumn(child) != 1)
					continue;

				TextBox txt = child as TextBox;
				if (txt == null || txt.Text == "")
					continue;

				// Get the list of Items to Replace
				List<Run> runList = txt.Tag as List<Run>;
				if (runList == null)
					continue;

				// Replace the text
				foreach (Run run in runList)
					run.Text = txt.Text;
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Webbert Solutions
United States United States
Dave is an independent consultant working in a variety of industries utilizing Microsoft .NET technologies.

Comments and Discussions