How can i loop over Tree in ZK? - zk

I try to get the values of the ZK Tree from this example. But i not get the all childrens value...what i tried:
List<Treechildren> treeChildren = new ArrayList<>();
List<Label> labelList = new ArrayList<>();
List<Label> childs = new ArrayList<>();
tree.getChildren().forEach(component -> {
if(component instanceof Treechildren){
treeChildren.add((Treechildren) component);
}
});
List<Treeitem> treeItems = new ArrayList<>();
treeChildren.forEach(treechildren ->{
treeItems.addAll(treechildren.getChildren());
});
List<Treechildren> children = new ArrayList<>();
treeItems.forEach(treeitem -> {
System.err.println("treeitem: " + treeitem);
if (treeitem.getChildren().size() > 1) {
treeitem.getChildren().forEach(child -> {
if(child instanceof Treechildren){
children.add((Treechildren) child);
} else {
List<Treecell> tcList = new ArrayList<>();
child.getChildren().forEach(component -> {
if(component instanceof Treecell) {
tcList.add((Treecell) component);
}
});
List<Hlayout> hlList = new ArrayList<>();
tcList.forEach(treecell ->{
System.err.println("treecell" + treecell);
hlList.addAll(treecell.getChildren());
});
hlList.forEach(hlayout -> {
childs.addAll(hlayout.getChildren());
});
}
});
}else {
List<Treerow> tr = new ArrayList<>();
treeitem.getChildren().forEach(component -> {
if (component instanceof Treerow) {
tr.add((Treerow) component);
} else {
}
});
List<Treecell> TC = new ArrayList<>();
tr.forEach(treerow -> {
TC.addAll(treerow.getChildren());
});
List<Hlayout> HL = new ArrayList<>();
TC.forEach(treecell -> {
HL.addAll(treecell.getChildren());
});
HL.forEach(hlayout -> labelList.addAll(hlayout.getChildren()));
}
});
labelList.forEach(label -> System.err.println("label: " + label.getValue()));
childs.forEach(label -> System.err.println("childs: " + label.getValue()));
}
But i not get the whole tree labels....the aim is to save the tree to database when i push the submit button.

Since you did not post your tree, I can only guess how it looks. But I tried to at least understand your code and found a potential bug: In line 21 handle the case where tree nodes have sub nodes. You add the Treechildren to children, but never do anything with it. So you loop will stop at the first level.
First of all, I would recommend using the special methods for trees and their components:
Tree: getTreechildren()
Treeitem: getTree(), getTreerow() and getTreechildren()
Treerow: getTree() and getLinkedTreechildren()
These help you to get rid of all the casting and instanceof checks. Furthermore, you can specify the type of children when calling getChildren():
List<Treecell> cells = row.<Treecell> getChildren()
Now you don't have to cast, but to be careful that all children are of the same type.
Finally, I refactored your code to understand it:
// Find all items in the tree, level by level
List<Treeitem> items = new ArrayList<>(tree.getTreechildren().getChildren());
List<Treeitem> itemsTemp = new ArrayList<>(tree.getTreechildren().getChildren());
while (!itemsTemp.isEmpty())
{
Treeitem nextChild = itemsTemp.remove(0);
if (nextChild.getTreechildren() != null)
{
items.addAll(nextChild.getTreechildren().getChildren());
itemsTemp.addAll(nextChild.getTreechildren().getChildren());
}
}
// convert item -> row -> cells -> hlayouts -> children
List<Component> children = items.stream()
.map(Treeitem::getTreerow)
.filter(Objects::nonNull)
.flatMap(row -> row.<Treecell> getChildren().stream())
.flatMap(cell -> cell.<Hlayout> getChildren().stream())
.flatMap(hlayout -> hlayout.getChildren().stream())
.collect(Collectors.toList());
// // without streams
// List<Component> children = new ArrayList<>();
// for (Treeitem item : items)
// {
// if (item.getTreerow() != null)
// {
// for (Treecell cell : item.getTreerow().<Treecell> getChildren())
// {
// for (Hlayout hlayout : cell.<Hlayout> getChildren())
// {
// children.addAll(hlayout.getChildren());
// }
// }
// }
// }
You can see that in the while loop, I looked at the tree's children on the first level, then at their children, that at those children's children, and so on. Then I used the methods mentioned above to find the components of each tree node.
Again, I do not know how your tree looks, so I do not know whether this works for you. Maybe you could post a small example.

Related

NatTable filtering row header layer for dynamic list

When I use dynamic list for Nattable the filtering via filter header layer it does not work for me.
I want to be able to filter all columns. I have respective code for each column in the filter row configuration class and it works if the input is a static list.
Attached my code snippet:
public Control createPeopleTable(Composite parent) {
// create a new ConfigRegistry which will be needed for GlazedLists
configRegistry = new ConfigRegistry();
peopleTableColumns = PeopleTableColumnDefinition.getPeopleTableColumns();
final Map<String, String> propertyToLabelMap = new HashMap<>();
final String[] propertyNames = new String[peopleTableColumns.size()];
for (int i = 0; i < peopleTableColumns.size(); ++i) {
final PeopleTableColumnDefinition col = peopleTableColumns.get(i);
propertyNames[i] = col.getProperty();
propertyToLabelMap.put(propertyNames[i], col.getDisplayName());
}
columnPropertyAccessor = new ExtendedReflectiveColumnPropertyAccessor<>(propertyNames);columnPropertyAccessor = new ExtendedReflectiveColumnPropertyAccessor<>(propertyNames);
peopleList = new ArrayList<>();
eventList = GlazedLists.eventList(peopleList);
dynamicValues = GlazedLists.threadSafeList(eventList);
bodyLayerStack = new BodyLayerStack(dynamicValues, columnPropertyAccessor);
.....
IFilterStrategy<People> filterStrategy = new DefaultGlazedListsFilterStrategy<>(
bodyLayerStack.getFilterList(), columnPropertyAccessor, configRegistry);
// Note: The column header layer is wrapped in a filter row composite.
// This plugs in the filter row functionality
FilterRowHeaderComposite<People> filterRowHeaderLayer = new FilterRowHeaderComposite<>(filterStrategy,
sortHeaderLayer, bodyLayerStack.getBodyDataProvider(), configRegistry);
// build the row header layer
IDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyLayerStack.getBodyDataProvider());
DataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, bodyLayerStack,
bodyLayerStack.getSelectionLayer());
// build the corner layer
IDataProvider cornerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider,
rowHeaderDataProvider);
DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, filterRowHeaderLayer);
// build the grid layer
GridLayer gridLayer = new GridLayer(bodyLayerStack, filterRowHeaderLayer, rowHeaderLayer, cornerLayer);
// turn the auto configuration off as we want to add our header menu
// configuration
natTable = new NatTable(parent, gridLayer, false);
.....
natTable.addConfiguration(new FilterRowConfiguration());
natTable.configure();
...
return natTable;
}
class FilterRowConfiguration extends AbstractRegistryConfiguration {
#Override
public void configureRegistry(IConfigRegistry configRegistry) {
// register the FilterRowTextCellEditor in the first column which
// immediately commits on key press
configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new FilterRowTextCellEditor(),
DisplayMode.NORMAL, FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX
+ PeopleTableColumnDefinition.Name.columnIndex());
.....
}
class BodyLayerStack extends AbstractLayerTransform {
private FilterList<People> filterList;
private final SelectionLayer selectionLayer;
private SortedList<People> sortedList;
private ViewportLayer viewportLayer;
public BodyLayerStack(EventList<People> values,
IColumnPropertyAccessor<People> columnPropertyAccessor) {
// use the SortedList constructor with 'null' for the Comparator
// because the Comparator
// will be set by configuration
sortedList = new SortedList<>(values, null);
// wrap the SortedList with the FilterList
filterList = new FilterList<>(sortedList);
peopleListDataProvider = new PeopleListDataProvider<>(filterList, columnPropertyAccessor);
bodyDataLayer = new DataLayer(peopleListDataProvider);
final ColumnOverrideLabelAccumulator columnLabelAccumulator = new ColumnOverrideLabelAccumulator(
bodyDataLayer);
for (int i = 0; i < peopleTableColumns.size(); i++) {
PeopleTableColumnDefinition peopleTableColumnDefinition = peopleTableColumns.get(i);
columnLabelAccumulator.registerColumnOverrides(i, peopleTableColumnDefinition.getTableLabel());
}
bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
// layer for event handling of GlazedLists and PropertyChanges
GlazedListsEventLayer<People> glazedListsEventLayer = new GlazedListsEventLayer<>(bodyDataLayer,
filterList);
selectionLayer = new SelectionLayer(glazedListsEventLayer);
viewportLayer = new ViewportLayer(selectionLayer);
FreezeLayer freezeLayer = new FreezeLayer(selectionLayer);
compositeFreezeLayer = new CompositeFreezeLayer(freezeLayer, viewportLayer, selectionLayer);
setUnderlyingLayer(compositeFreezeLayer);
}
public SortedList<People> getSortedList() {
return sortedList;
}
public SelectionLayer getSelectionLayer() {
return this.selectionLayer;
}
public FilterList<People> getFilterList() {
return this.filterList;
}
public PeopleListDataProvider<People> getBodyDataProvider() {
return peopleListDataProvider;
}
}
public void setListOfPeople(List<People> listOfPeople) {
this.eventList.getReadWriteLock().writeLock().lock();
this.dynamicValues.getReadWriteLock().writeLock().lock();
try {
peopleList.clear();
eventList.clear();
dynamicValues.clear();
peopleList.addAll(listOfPeople);
eventList.addAll(listOfPeople);
dynamicValues.addAll(listOfPeople);
peopleListDataProvider.setPeopleList(peopleList);
} finally {
eventList.getReadWriteLock().writeLock().unlock();
dynamicValues.getReadWriteLock().writeLock().unlock();
}
}
It seems you haven't understood the principles of GlazedLists and Java object references.
First you don't have to call clear() and addAll() on all lists. The GlazedLists are views on the base list, so it should be enough to call on the EventList or a higher list like the FilterList.
Second mistake is that you set another list on the IDataProvider. And that list is not the FilterList. So actually you disable the filter as the FilterList is doing the filter magic. And setting the list is not necessary as you performed a list modification. So why changing it then?

How do I save Xml Changes Back to the Original Document

I need to update the Styles (styles.xml) part of an MS Word document due to a problem with a vendor's product.
So far I've been able to extract and update the xml I need. The only problem, is that I don't know how to save my changes back to the document.
The code below is working just fine. I usually output the xml to the console to make sure it's going in just fine. At the end, I know I need to perform some save operation, but the XDocument.Save( /stream/) hasn't worked.
Here's where I am so far
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
Console.WriteLine(stylesPart.Styles.OuterXml);
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
}
}
}
I found the answer in the SAVE THE PARTS section of this page Replace the styles parts in a word processing document (Open XML SDK)
See the end of this code for the solution. You'll also see what I've tried.
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
//stylesDoc.Save(#"C:\WinTest\HooRah.xml"); -- I only use this to verify that I've updated everything correctly
//using (XmlWriter xw = XmlWriter.Create(stylesPart.GetStream(FileMode.Create, FileAccess.Write)))
//{
// stylesDoc.Save(xw); -- DID NOT WORK EITHER
// doc.Save();
//}
// THIS WORKED
stylesDoc.Save(new StreamWriter(stylesPart.GetStream(FileMode.Create, FileAccess.Write)));
}
}
}

Read content of repeating sections with apache POI

I Have a word document with a repeating section, containing other content controls.
In java project, I have a function that gets all sdts (content controls) from a word document in apache POI, in a List List.
When I inspect my repeating section in that list, I can get the text inside all content controls (inside my repeating section) but is apears as a long paragraph instead of other sdt nodes.
Is there a way to inspect content of repeating section sdt with Apache POI ? I can't find anything about it in the doc
function that gets all sdts
private static List
extractSDTsFromBodyElements(List<IBodyElement> elements) {
List<AbstractXWPFSDT> sdts = new ArrayList<AbstractXWPFSDT>();
for (IBodyElement e : elements) {
if (e instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e;
sdts.add(sdt);
} else if (e instanceof XWPFParagraph) {
XWPFParagraph p = (XWPFParagraph) e;
for (IRunElement e2 : p.getIRuns()) {
if (e2 instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e2;
sdts.add(sdt);
}
}
}
}
return sdts;
}
The XWPF part of apache poi is rudimentary until now and highly in development. In XWPFSDT is this mentioned also: "Experimental class to offer rudimentary read-only processing of of StructuredDocumentTags/ContentControl". So until now your code only gets the surrounding XWPFSDT of the repeating content control but not the inner controls. One could have seen that by having some debugging outputs in the code. See my System.out.println(...).
So to really get all XWPFSDTs we must go other ways using the underlaying XMLdirectly.
Lets have a complete example.
Look at this Worddocument:
As you see there is a single control to input the group name, then a repeating content control around three controls to input name, amount and date and then a single control to input the employee. All controls which shall be read have titles set. So whether the title is set, is the criterion whether a control is important for reading or not.
The following code now can read all controls and their content:
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.ArrayList;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.apache.xmlbeans.XmlCursor;
import javax.xml.namespace.QName;
public class ReadWordForm {
/*
private static List<AbstractXWPFSDT> extractSDTsFromBodyElements(List<IBodyElement> elements) {
List<AbstractXWPFSDT> sdts = new ArrayList<AbstractXWPFSDT>();
for (IBodyElement e : elements) {
if (e instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e;
System.out.println("block: " + sdt);
sdts.add(sdt);
} else if (e instanceof XWPFParagraph) {
XWPFParagraph p = (XWPFParagraph) e;
for (IRunElement e2 : p.getIRuns()) {
if (e2 instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e2;
System.out.println("inline: " + sdt);
sdts.add(sdt);
}
}
}
}
return sdts;
}
*/
private static List<XWPFSDT> extractSDTsFromBody(XWPFDocument document) {
XWPFSDT sdt;
XmlCursor xmlcursor = document.getDocument().getBody().newCursor();
QName qnameSdt = new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "sdt", "w");
List<XWPFSDT> allsdts = new ArrayList<XWPFSDT>();
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameSdt.equals(xmlcursor.getName())) {
if (xmlcursor.getObject() instanceof CTSdtRun) {
sdt = new XWPFSDT((CTSdtRun)xmlcursor.getObject(), document);
//System.out.println("inline: " + sdt);
allsdts.add(sdt);
} else if (xmlcursor.getObject() instanceof CTSdtBlock) {
sdt = new XWPFSDT((CTSdtBlock)xmlcursor.getObject(), document);
//System.out.println("block: " + sdt);
allsdts.add(sdt);
}
}
}
}
return allsdts;
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument(new FileInputStream("WordDataCollectingForm.docx"));
/*
List<IBodyElement> bodyelements = document.getBodyElements();
List<AbstractXWPFSDT> sdts = extractSDTsFromBodyElements(bodyelements);
*/
List<XWPFSDT> allsdts = extractSDTsFromBody(document);
for (XWPFSDT sdt : allsdts) {
//System.out.println(sdt);
String title = sdt.getTitle();
String content = sdt.getContent().getText();
if (!(title == null) && !(title.isEmpty())) {
System.out.println(title + ": " + content);
} else {
System.out.println("====sdt without title====");
}
}
document.close();
}
}

how to get drop down list by type respective alphabet in ComboBoxViewerCellEditor

In ComboBoxViewerCellEditor I want to write something and as a result I will get the matching dropdown value.
Can you suggest how to get this? Please find the code below:
public TCOperationColumnEditable(TableViewer viewer) {
super(viewer);
try
{
this.viewer = viewer;
//this.editor = new TextCellEditor(this.viewer.getTable());
OperationGSON[] allOperations = OperationAPIHandler.getInstance().getAllOperations();
ArrayList<String> opnName = new ArrayList<String>();
for(OperationGSON opn : allOperations)
{
opnName.add(opn.opnName);
}
this.editor = new ComboBoxViewerCellEditor(this.viewer.getTable(), SWT.FULL_SELECTION );
this.editor.setLabelProvider(new LabelProvider());
this.editor.setContentProvider(new ArrayContentProvide());
this.editor.setInput(opnName);
String[] stockArr = new String[opnName.size()];
stockArr = opnName.toArray(stockArr);
new AutoCompleteField(this.viewer.getControl(), new CComboContentAdapter(), stockArr);
}
catch(Exception e)
{
System.out.println("[" + getClass().getName() + " : TCOperationColumnEditable()] - Exception : " + e.getMessage());
e.printStackTrace();
}
}
enter image description here
Subclass TextCellEditor.
For a content proposal, use field assist API of JFace
(org.eclipse.jface.fieldassist). Content assist is enabled
when the cell editor is activated for the first time:
if (contentProposalAdapter == null) {
....
// enable content assist on the cell editor's text widget
contentProposalAdapter = new ContentProposalAdapter(text, new TextContentAdapter(), proposalProvider, activationKeyStroke, null);
} else {
contentProposalAdapter.setEnabled(true);
}
super.activate();
....
Make sure to also override method
TextCellEditor#dependsOnExternalFocusListener() to return false always.
Otherwise, you'll face some serious problems concerning focus.

OpenXML 2 SDK - Word document - Create bulleted list programmatically

Using the OpenXML SDK, 2.0 CTP, I am trying to programmatically create a Word document. In my document I have to insert a bulleted list, an some of the elements of the list must be underlined. How can I do this?
Lists in OpenXML are a little confusing.
There is a NumberingDefinitionsPart that describes all of the lists in the document. It contains information on how the lists should appear (bulleted, numbered, etc.) and also assigns and ID to each one.
Then in the MainDocumentPart, for every item in the list you want to create, you add a new paragraph and assign the ID of the list you want to that paragraph.
So to create a bullet list such as:
Hello,
world!
You would first have to create a NumberingDefinitionsPart:
NumberingDefinitionsPart numberingPart =
mainDocumentPart.AddNewPart<NumberingDefinitionsPart>("someUniqueIdHere");
Numbering element =
new Numbering(
new AbstractNum(
new Level(
new NumberingFormat() { Val = NumberFormatValues.Bullet },
new LevelText() { Val = "·" }
) { LevelIndex = 0 }
) { AbstractNumberId = 1 },
new NumberingInstance(
new AbstractNumId() { Val = 1 }
) { NumberID = 1 });
element.Save(numberingPart);
Then you create the MainDocumentPart as you normally would, except in the paragraph properties, assign the numbering ID:
MainDocumentPart mainDocumentPart =
package.AddMainDocumentPart();
Document element =
new Document(
new Body(
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("Hello, ") { Space = "preserve" })),
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("world!") { Space = "preserve" }))));
element.Save(mainDocumentPart);
There is a better explanation of the options available in the OpenXML reference guide in Section 2.9.
I wanted something that would allow me to add more than one bullet list to a document. After banging my head against my desk for a while, I managed to combine a bunch of different posts and examine my document with the Open XML SDK 2.0 Productity Tool and figured some stuff out. The document it produces now passes validation for by version 2.0 and 2.5 of the SDK Productivity tool.
Here is the code; hopefully it saves someone some time and aggravation.
Usage:
const string fileToCreate = "C:\\temp\\bulletTest.docx";
if (File.Exists(fileToCreate))
File.Delete(fileToCreate);
var writer = new SimpleDocumentWriter();
List<string> fruitList = new List<string>() { "Apple", "Banana", "Carrot"};
writer.AddBulletList(fruitList);
writer.AddParagraph("This is a spacing paragraph 1.");
List<string> animalList = new List<string>() { "Dog", "Cat", "Bear" };
writer.AddBulletList(animalList);
writer.AddParagraph("This is a spacing paragraph 2.");
List<string> stuffList = new List<string>() { "Ball", "Wallet", "Phone" };
writer.AddBulletList(stuffList);
writer.AddParagraph("Done.");
writer.SaveToFile(fileToCreate);
Using statements:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
Code
public class SimpleDocumentWriter : IDisposable
{
private MemoryStream _ms;
private WordprocessingDocument _wordprocessingDocument;
public SimpleDocumentWriter()
{
_ms = new MemoryStream();
_wordprocessingDocument = WordprocessingDocument.Create(_ms, WordprocessingDocumentType.Document);
var mainDocumentPart = _wordprocessingDocument.AddMainDocumentPart();
Body body = new Body();
mainDocumentPart.Document = new Document(body);
}
public void AddParagraph(string sentence)
{
List<Run> runList = ListOfStringToRunList(new List<string> { sentence});
AddParagraph(runList);
}
public void AddParagraph(List<string> sentences)
{
List<Run> runList = ListOfStringToRunList(sentences);
AddParagraph(runList);
}
public void AddParagraph(List<Run> runList)
{
var para = new Paragraph();
foreach (Run runItem in runList)
{
para.AppendChild(runItem);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
body.AppendChild(para);
}
public void AddBulletList(List<string> sentences)
{
var runList = ListOfStringToRunList(sentences);
AddBulletList(runList);
}
public void AddBulletList(List<Run> runList)
{
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = _wordprocessingDocument.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = _wordprocessingDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
Numbering element = new Numbering();
element.Save(numberingPart);
}
// Insert an AbstractNum into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last AbstractNum and BEFORE the first NumberingInstance or we will get a validation error.
var abstractNumberId = numberingPart.Numbering.Elements<AbstractNum>().Count() + 1;
var abstractLevel = new Level(new NumberingFormat() {Val = NumberFormatValues.Bullet}, new LevelText() {Val = "·"}) {LevelIndex = 0};
var abstractNum1 = new AbstractNum(abstractLevel) {AbstractNumberId = abstractNumberId};
if (abstractNumberId == 1)
{
numberingPart.Numbering.Append(abstractNum1);
}
else
{
AbstractNum lastAbstractNum = numberingPart.Numbering.Elements<AbstractNum>().Last();
numberingPart.Numbering.InsertAfter(abstractNum1, lastAbstractNum);
}
// Insert an NumberingInstance into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last NumberingInstance and AFTER all the AbstractNum entries or we will get a validation error.
var numberId = numberingPart.Numbering.Elements<NumberingInstance>().Count() + 1;
NumberingInstance numberingInstance1 = new NumberingInstance() {NumberID = numberId};
AbstractNumId abstractNumId1 = new AbstractNumId() {Val = abstractNumberId};
numberingInstance1.Append(abstractNumId1);
if (numberId == 1)
{
numberingPart.Numbering.Append(numberingInstance1);
}
else
{
var lastNumberingInstance = numberingPart.Numbering.Elements<NumberingInstance>().Last();
numberingPart.Numbering.InsertAfter(numberingInstance1, lastNumberingInstance);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
foreach (Run runItem in runList)
{
// Create items for paragraph properties
var numberingProperties = new NumberingProperties(new NumberingLevelReference() {Val = 0}, new NumberingId() {Val = numberId});
var spacingBetweenLines1 = new SpacingBetweenLines() { After = "0" }; // Get rid of space between bullets
var indentation = new Indentation() { Left = "720", Hanging = "360" }; // correct indentation
ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
RunFonts runFonts1 = new RunFonts() { Ascii = "Symbol", HighAnsi = "Symbol" };
paragraphMarkRunProperties1.Append(runFonts1);
// create paragraph properties
var paragraphProperties = new ParagraphProperties(numberingProperties, spacingBetweenLines1, indentation, paragraphMarkRunProperties1);
// Create paragraph
var newPara = new Paragraph(paragraphProperties);
// Add run to the paragraph
newPara.AppendChild(runItem);
// Add one bullet item to the body
body.AppendChild(newPara);
}
}
public void Dispose()
{
CloseAndDisposeOfDocument();
if (_ms != null)
{
_ms.Dispose();
_ms = null;
}
}
public MemoryStream SaveToStream()
{
_ms.Position = 0;
return _ms;
}
public void SaveToFile(string fileName)
{
if (_wordprocessingDocument != null)
{
CloseAndDisposeOfDocument();
}
if (_ms == null)
throw new ArgumentException("This object has already been disposed of so you cannot save it!");
using (var fs = File.Create(fileName))
{
_ms.WriteTo(fs);
}
}
private void CloseAndDisposeOfDocument()
{
if (_wordprocessingDocument != null)
{
_wordprocessingDocument.Close();
_wordprocessingDocument.Dispose();
_wordprocessingDocument = null;
}
}
private static List<Run> ListOfStringToRunList(List<string> sentences)
{
var runList = new List<Run>();
foreach (string item in sentences)
{
var newRun = new Run();
newRun.AppendChild(new Text(item));
runList.Add(newRun);
}
return runList;
}
}
Adam's answer above is correct except it is new NumberingInstance( instead of new Num( as noted in a comment.
Additionally, if you have multiple lists, you should have multiple Numbering elements (each with it's own id eg 1, 2, 3 etc -- one for each list in the document. This doesn't seem to be a problem with bullet lists, but numbered lists will continue using the same numbering sequence (as opposed to starting over again at 1) because it will think that it's the same list. The NumberingId has to be referenced in your paragraph like this:
ParagraphProperties paragraphProperties1 = new ParagraphProperties();
ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "ListParagraph" };
NumberingProperties numberingProperties1 = new NumberingProperties();
NumberingLevelReference numberingLevelReference1 = new NumberingLevelReference() { Val = 0 };
NumberingId numberingId1 = new NumberingId(){ Val = 1 }; //Val is 1, 2, 3 etc based on your numberingid in your numbering element
numberingProperties1.Append(numberingLevelReference1);
numberingProperties1.Append(numberingId1);
paragraphProperties1.Append(paragraphStyleId1);
paragraphProperties1.Append(numberingProperties1);
Children of the Level element will have an effect on the type of bullet, and the indentation.
My bullets were too small until I added this to the Level element:
new NumberingSymbolRunProperties(
new RunFonts() { Hint = FontTypeHintValues.Default, Ascii = "Symbol", HighAnsi = "Symbol" })
Indentation was a problem until I added this element to the Level element as well:
new PreviousParagraphProperties(
new Indentation() { Left = "864", Hanging = "360" })
And if you are like me - creating a document from a template, then you may want to use this code, to handle both situations - when your template does or does not contain any numbering definitions:
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = document.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = document.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
}
Not sure if this helps anyone, but here is my snippet for inserting a list of bullets.
Create the word processing document and word document body
WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open($"{tempFolder}{tempFileName}", true);
Body wordDocumentBody = wordprocessingDocument.MainDocumentPart.Document.Body;
Then get the insert index. This is placeholder text inside the word document, so you can insert into the correct place in the word document.
int insertIndex = wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First());
You can then call this method to insert into the word document in both bullet points for lettering.
public static void InsertBullets(Body wordDocumentBody, int insertIndex, int bulletStyle, List<string> strToAdd)
{
//// Bullet Styles:
// 1 - Standard bullet
// 2 - Numbered
foreach (string item in strToAdd)
{
Paragraph para = wordDocumentBody.InsertAt(new Paragraph(), insertIndex);
ParagraphProperties paragraphProperties = new ParagraphProperties();
paragraphProperties.Append(new ParagraphStyleId() { Val = "ListParagraph" });
paragraphProperties.Append(new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = bulletStyle }));
para.Append(paragraphProperties);
para.Append(new Run(
new RunProperties(
new RunStyle() { Val = "ListParagraph" },
new NoProof()),
new Text($"{item}")));
insertIndex++;
}
}
You can then remove the placeholder text after with this.
wordDocumentBody.Elements().ElementAt(wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First())).Remove();