create a new document from word template with multiple pages using documentformat.openxml - ms-word

I have a Microsoft Word template with some content controls. It contains a table of contents and some extra information.
On the page, I have set some content controls where I would like to inject a new text from a RESTful service. If the RESTful service returns an array of data (objects), I need to replicate the information on each page based on the Word template.
Any idea on how can I do that using the Open XML SDK (DocumentFormat.OpenXml)?
Edit:
I found this post here, which is great, but I don't know how can I apply the array of data to multiple pages from the same template.
So, how can I create multiple pages from the same template in a new document? The data comes in as an array.

The sample code below (which is unit-tested and works) does what you are trying to achieve. It is based on the following interpretation of the question and assumptions:
"Control place holders" means "Rich Text content controls", which are called block-level structured document tags (SDTs) in Open XML lingo and are thus represented by the SdtBlock class in the Open XML SDK.
The content controls have tags, meaning the relevant w:sdt elements have grandchild elements like <w:tag="tagValue" />. Those tags are used to link the data received from the REST service to the content controls.
The data is provided as a Dictionary<string, string>, mapping tag values to the content control text (data).
The general approach is to perform a pure functional transformation of the main document part of your WordprocessingDocument. The void WriteContentControls(WordprocessingDocument) method wraps the outermost pure functional transformation object TransformDocument(OpenXmlElement). The latter uses an inner pure functional transformation object TransformSdtBlock(OpenXmlElement, string).
public class ContentControlWriter
{
private readonly IDictionary<string, string> _contentMap;
/// <summary>
/// Initializes a new ContentControlWriter instance.
/// </summary>
/// <param name="contentMap">The mapping of content control tags to content control texts.
/// </param>
public ContentControlWriter(IDictionary<string, string> contentMap)
{
_contentMap = contentMap;
}
/// <summary>
/// Transforms the given WordprocessingDocument by setting the content
/// of relevant block-level content controls.
/// </summary>
/// <param name="wordDocument">The WordprocessingDocument to be transformed.</param>
public void WriteContentControls(WordprocessingDocument wordDocument)
{
MainDocumentPart part = wordDocument.MainDocumentPart;
part.Document = (Document) TransformDocument(part.Document);
}
private object TransformDocument(OpenXmlElement element)
{
if (element is SdtBlock sdt)
{
string tagValue = GetTagValue(sdt);
if (_contentMap.TryGetValue(tagValue, out string text))
{
return TransformSdtBlock(sdt, text);
}
}
return Transform(element, TransformDocument);
}
private static object TransformSdtBlock(OpenXmlElement element, string text)
{
return element is SdtContentBlock
? new SdtContentBlock(new Paragraph(new Run(new Text(text))))
: Transform(element, e => TransformSdtBlock(e, text));
}
private static string GetTagValue(SdtElement sdt) => sdt
.Descendants<Tag>()
.Select(tag => tag.Val.Value)
.FirstOrDefault();
private static T Transform<T>(T element, Func<OpenXmlElement, object> transformation)
where T : OpenXmlElement
{
var transformedElement = (T) element.CloneNode(false);
transformedElement.Append(element.Elements().Select(e => (OpenXmlElement) transformation(e)));
return transformedElement;
}
}
This should provide enough input for implementing your specific solution even if the details vary (e.g., regarding how you map your array of data to the specific content controls). Further, if you don't use block-level structured document tags (SdtBlock, Rich Text content controls) but but rather inline-level structured document tags (SdtRun, plain text content controls), the principle is the same. Instead of the Paragraph instances (w:p elements) contained in SdtContentBlock instances, you would have Run instances (w:r elements) contained in SdtContentRun instances.
Update 2019-11-23: My CodeSnippets GitHub repository contains the code for the ContentControlWriter and AltChunkAssemblyTests classes. The latter shows how the ContentControlWriter class can be used.

Related

How to specify Contains in UI Automation PropertyCondition

I am using UI Automation for GUI testing.
My window title contains the application name appended by a filename.
So, I want to specify Contains in my Name PropertyCondition.
I checked the overload but it is related to Ignoring the Case of the Name value.
Can anyone let me know how to specify Contains in my Name PropertyCondition?
Regards,
kvk938
I've tried solution of Max Young but could not wait for its completion. Probably my visual tree was too large, not sure. I've decided that it's my app and I should use knowledge of concrete element type I am searching for, in my case it was WPF TextBlock so I've made this:
public AutomationElement FindElementBySubstring(AutomationElement element, ControlType controlType, string searchTerm)
{
AutomationElementCollection textDescendants = element.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, controlType));
foreach (AutomationElement el in textDescendants)
{
if (el.Current.Name.Contains(searchTerm))
return el;
}
return null;
}
sample usage:
AutomationElement textElement = FindElementBySubstring(parentElement, ControlType.Text, "whatever");
and it worked fast.
As far as I know their is no way to do a contains while using the name property but you could do something like this.
/// <summary>
/// Returns the first automation element that is a child of the element you passed in and contains the string you passed in.
/// </summary>
public AutomationElement GetElementByName(AutomationElement aeElement, string sSearchTerm)
{
AutomationElement aeFirstChild = TreeWalker.RawViewWalker.GetFirstChild(aeElement);
AutomationElement aeSibling = null;
while ((aeSibling = TreeWalker.RawViewWalker.GetNextSibling(aeFirstChild)) != null)
{
if (aeSibling.Current.Name.Contains(sSearchTerm))
{
return aeSibling;
}
}
return aeSibling;
}
Then you would do this to get the desktop and pass the desktop with your string into the above method
/// <summary>
/// Finds the automation element for the desktop.
/// </summary>
/// <returns>Returns the automation element for the desktop.</returns>
public AutomationElement GetDesktop()
{
AutomationElement aeDesktop = AutomationElement.RootElement;
return aeDesktop;
}
Complete usage would look something like
AutomationElement oAutomationElement = GetElementByName(GetDesktop(), "Part of my apps name");

Form fields are reset on validation error

I have a rather complex form in the way that the number of form fields is flexibel. In short, the model object is a TLabel (TranslationLabel) that contains a Map of values (translations). Language here is an enum so the idea is that the number of fields (text areas) for which a translation is given depends on the values in this enum.
This is my form (simplified):
public class TranslationEditForm extends Form {
private final static List<Language> LANGUAGES = newArrayList(Language.values());
public TranslationEditForm(String id, final TranslationLabelView label) {
super(id, new CompoundPropertyModel<TranslationLabelView>(label));
ListView<Language> textAreas = new ListView<Language>("translationRepeater", LANGUAGES) {
#Override
protected void populateItem(final ListItem<Language> itemLang) {
//loop through the languages and create 1 textarea per language
itemLang.add(new Label("language", itemLang.getModelObject().toString()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
//return the value for current language
return label.getValue(itemLang.getModelObject());
}
#Override
public void setObject(String object) {
//set the value for current language
label.getTranslations().put(itemLang.getModelObject(), object);
}
};
itemLang.add(new TextArea<String>("value", textModel).setRequired(true));
}
};
//add the repeater containing a textarea per language to the form
this.add(textAreas);
}
}
Now, it works fine, 1 text area is created per language and its value is also set nicely; even more when changed the model gets updated as intended.
If you submit the form after emptying a text area (so originally there was a value) then of course there is a validation error (required). Normal (wicket) behaviour would be that the invalid field is still empty but for some reason the original value is reset and I don't understand why.
If I override onError like this:
#Override
protected void onError() {
this.updateFormComponentModels();
}
then it is fine, the value of the field is set to the submitted value (empty) instead of the original value.
Any idea what is causing this? What is wicket failing to do because the way I've set up the form (because with a simple form/model this is working fine as are the wicket examples)?
Posted as answer, so the question can be marked as solved:
ListView does recreate all its items at render time. This means that the validation will be broken. Have a look at API doc of the ListView
Calling setReuseItems() on the ListView solves this.
Regards,
Bert

Cross-Process Drag and Drop of custom object type contain sorted list of XmlNodes in WinForms C#

I had a similar problem as the user posting at the following location:
Cross-Process Drag and Drop of custom object type in WinForms C#
Luckily, I have figured out how to serialize almost all parts of my custom object except for a SortedList object.
I need this object because it contains some pretty crucial information to my application, and the Xml nesting is pretty messy.
When I comment out the line adding the SortedList in the ISerializable member GetObjectData(), the object makes it across to the new app. When I leave it in, it doesn't, and I can't figure out how to get it serialized.
I have done some looking both here on StackOverflow and on the web, but have found nothing of use.
I am using the following code to check if my object is serializable so it can be drag & dropped to another application:
/// <summary>
/// Determine if object can be fully serializable to binary format.
/// </summary>
/// <param name="obj"></param>
/// <param name="errorMsg">If return value false, contains reason for failure.</param>
/// <returns></returns>
public static bool IsSerializable(object obj, out string errorMsg)
{
errorMsg = "";
using (MemoryStream mem = new MemoryStream())
{
BinaryFormatter bin = new BinaryFormatter();
try
{
bin.Serialize(mem, obj);
return true;
}
catch (Exception ex)
{
errorMsg = string.Format("Object cannot be serialized: {0}", ex.ToString());
return false;
}
}
}
Does anyone have any suggestions that can help me out? I would like to keep my list of XmlNodes intact during the drag/drop if possible, but wouldn't be opposed to doing some additional coding to break it up into serialize-able pieces and reconstructing it on the other side. The important thing is that the end result must contain a SortedList.
If necessary, I can provide the contents of my custom object I am serializing for drag and drop if it will help.
Thanks,
Kyle K.
I finally figured out how to correctly serialize my object. I was using a SortedList of XmlNodes, which I found out the XmlNode object is not serializable. I switched my implementation to contain a SortedList of strings, and now everything works just fine.
Thanks,
Kyle

ASP.NET GetFullHtmlFieldId not returning valid id

In MVC2 template, I normally use this.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldName)
to generate the id field of an html element. This has worked in most of the cases.
However this method does not actually return valid id field, it merely prefix fieldName with ViewData.TemplateInfo.HtmlFieldPrefix, this is causing problems for me when rendering collections which has [] in the HtmlFieldPrefix.
I have been manually converting those characters to _ where I find the need, but this seems to be not elegant (repeated code), has anyone found a good way to generate id field properly?
Can you be more specific about the kind of problems do you have?
For example, there is an elegant approach to editing variable length list with validation support provided. While it doesn't use templates still remains DRY with partial views.
While the ids are inconsistent - the names are OK and only problem I encountered is that using jquery.infieldlabel it appeared that label's for attribute (generated by GetFullHtmlFieldId inside LabelFor helper) didn't match id of the appropriate TextBoxFor input. So I created LabelForCollectionItem helper method that just uses the same method for id generation as the TextBox - TagBuilder.GenerateId(fullName)
Maybe the code doesn't correspond to your need but hope it will help somebody since I found your question among the first searching for solution to my problem.
public static class LabelExtensions
{
/// <summary>
/// Generates Label with "for" attribute corresponding to the id rendered by input (e.g. TextBoxFor),
/// for the case when input is a collection item (full name contains []).
/// GetFullHtmlFieldId works incorrect inside Html.BeginCollectionItem due to brackets presense.
/// This method copies TextBox's id generation.
/// </summary>
public static MvcHtmlString LabelForCollectionItem<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression,
string labelText = null, object htmlAttributes = null) where TModel : class
{
var tag = new TagBuilder("label");
tag.MergeAttributes(new RouteValueDictionary(htmlAttributes)); // to convert an object into an IDictionary
// set inner text
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
string innerText = labelText ?? GetDefaultLabelText(html, expression, htmlFieldName);
if (string.IsNullOrEmpty(innerText))
{
return MvcHtmlString.Empty;
}
tag.SetInnerText(innerText);
// set for attribute
string forId = GenerateTextBoxId(tag, html, htmlFieldName);
tag.Attributes.Add("for", forId);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.InputExtensions
/// </summary>
private static string GenerateTextBoxId<TModel>(TagBuilder tagBuilder, HtmlHelper<TModel> html, string htmlFieldName)
{
string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName);
tagBuilder.GenerateId(fullName);
string forId = tagBuilder.Attributes["id"];
tagBuilder.Attributes.Remove("id");
return forId;
}
/// <summary>
/// Extracted from System.Web.Mvc.Html.LabelExtensions
/// </summary>
private static string GetDefaultLabelText<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string htmlFieldName)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
return labelText;
}
}

Silverlight MVVM header detail

So lets say i have an OrderModel and an OrderViewModel. I have the Supplier, Order Date, etc properties on both the ViewModel and the Model and they are linked up. Seen examples on this and seems straighforward enough, although somewhat duplicated in terms of writing setters/getters.
Now what do I do with the OrderDetails? In my model I would have a List.
Do I have an OrderDetailViewModel for the OrderDetail? If so then how does the OrderViewModel provide that? As an ObservableCollection? And if so how do you keep that in sync with the original List?
This is where I haven't seen a decent example. If there is one out there someone could point me to, I'd appreciate it. I liek the concept of the MVVM but I am starting to thing its a hell of a lot of overhead. Why not just have the ViewModel handle the model part as well. In day to day LOB apps is there really that much difference between the two to warrant all the code that true MVVM seems to required?
It looks like this is what you need: http://jonas.follesoe.no/SpeakingAtMSDNLiveNextMonth.aspx
A translation on google gives this as the abstract for the talk:
Silverlight 2 was released this autumn, and lays a good foundation for developers who want to create rich Internet applications (RIA) based on. NET. In this session we in Depth in Silverlight 2 that development and the benefits of choosing Silverlight 2 as a platform for data-centric business applications. The session will cover among other things, data access via secured WCF services, how to structure the code using the Model-View-View Model pattern (MVVM), how to write code, designers can work with, and easy-Blend tips for developers. The session will be built around a dive log application where the code will be available after the presentation.
However in the mean time Jonas has already talked about MVVM here:
http://jonas.follesoe.no/YouCardRevisitedImplementingTheViewModelPattern.aspx
You can use something like this to keep your ObservableCollections synchronised between the model and view model:
/// <summary>
/// Keeps one collection synchronised with another.
/// </summary>
/// <typeparam name="Source">The type of the source items.</typeparam>
/// <typeparam name="Destination">The type of the destination items.</typeparam>
public class CollectionSync<Source, Destination>
{
private readonly Func<Source, Destination> _destItemFactory;
private readonly Action<Destination> _destItemRemover;
private readonly IList<Destination> _destList;
private readonly IList<Source> _sourceList;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionSync<Source, Destination>"/> class.
/// </summary>
/// <param name="sourceList">The source list.</param>
/// <param name="destList">The destination list.</param>
/// <param name="destItemFactory">Factory method which creates a Destination for a given Source.</param>
/// <param name="destItemRemover">Method called when a Destination is removed.</param>
public CollectionSync(IList<Source> sourceList,
IList<Destination> destList,
Func<Source, Destination> destItemFactory,
Action<Destination> destItemRemover)
{
_destItemFactory = destItemFactory;
_destItemRemover = destItemRemover;
_sourceList = sourceList;
_destList = destList;
((INotifyCollectionChanged) _sourceList).CollectionChanged += SourceCollection_CollectionChanged;
PopulateWithAllItems();
}
private void PopulateWithAllItems()
{
foreach (Source sourceItem in _sourceList)
_destList.Add(_destItemFactory(sourceItem));
}
private void SourceCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
OnItemsAdded(args.NewStartingIndex, args.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
OnItemsRemoved(args.OldStartingIndex, args.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
OnItemsReset();
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
throw new NotImplementedException();
}
}
private void OnItemsReset()
{
_destList.Clear();
PopulateWithAllItems();
}
private void OnItemsRemoved(int index, ICollection items)
{
int itemCount = items.Count;
for (int i = 0; i < itemCount; i++)
{
Destination removed = _destList[index];
_destList.RemoveAt(index);
if (_destItemRemover != null)
_destItemRemover(removed);
}
}
private void OnItemsAdded(int index, IList items)
{
int itemIndex = index;
foreach (Source item in items)
{
// Add to Items collection
_destList.Insert(itemIndex, _destItemFactory(item));
itemIndex++;
}
}
}
Taking your Order/OrderDetails example, in your Order view model you would hook up the two ObservableCollections like this:
_modelToViewModelSync = new CollectionSync<IOrderDetail, OrderDetailViewModel>(
orderDetailModels, // the list of your order details models
OrderDetails, // the list of order details view models exposed by the Order view model
x => new OrderDetailViewModel(x), // factory method to create a view model
null); // do something here if you care when your view models are removed
When it comes to the question "Do I need another view model", my answer is this: If all your view is doing is showing the model data, there is no harm in binding directly to the order directly. Creating a ViewModel for this would be very much redundant. The time when the ViewModel needs to be created is when you have logic or state in the "order detail" screen that needs to be represented. Instead of adding that to the model, you create a ViewModel at that time.
As far as keeping those items in sync, Similar to GraemeF, I have created a Binder class that is uses reflection to bind two values together. It keeps my model and viewmodel properties in sync, and it can be used to keep other things in sync, like this particular collection. There is some overhead in creating a binder like this, but once it is done, you can specify data correlations in a functional way, which is really nice.