C# docx bookmarks loop - c#

I want to iterate through all bookmarks inside document and set text to each bookmark.Name from datagridview cells values which is already loaded. I'm stuck here in this loop. Please, any suggestions?
using (Novacode.DocX document = DocX.Load(template))
{
foreach (Novacode.Bookmark bookmark in document.Bookmarks)
{
//MessageBox.Show("\tFound bookmarks {0}", bookmark.Name);
//var bookmarks = bookmark.Name;
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[0].Value.ToString());
int i = document.Bookmarks.Count;
var bookmarks = document.Bookmarks[i].Name;
document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[0].Value.ToString());
document.Bookmarks[0].SetText(dataGridViewRow.Cells[1].Value.ToString());
document.Bookmarks[1].SetText(dataGridViewRow.Cells[2].Value.ToString());
document.Bookmarks[2].SetText(dataGridViewRow.Cells[3].Value.ToString());
document.Bookmarks[3].SetText(dataGridViewRow.Cells[4].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[2].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[3].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[4].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[5].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[6].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[7].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[8].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[9].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[10].Value.ToString());
//document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[11].Value.ToString());
}
document.SaveAs(path2);
}

If I understand you correctly, this is what you are trying to achieve with the loop:
using (Novacode.DocX document = DocX.Load(template))
{
int i = 0;
foreach (Novacode.Bookmark bookmark in document.Bookmarks)
{
var bookmarks = document.Bookmarks[i].Name;
document.Bookmarks[bookmark.Name].SetText(dataGridViewRow.Cells[i+1].Value.ToString());
i++;
}
document.SaveAs(path2);
}
What we've done here is declared a variable i which is outside the loop but we increment its value with every foreach iteration. Alternatively, you could rewrite the loop and use a for loop instead:
for(int i=0; i< document.Bookmarks.Count)
{
//change the code here accordingly
}
Let me know if this helps.
Thank you.

Related

Remove column by name [duplicate]

I have a code
foreach (DataColumn dataTableCol in this.dataTable.Columns)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTableCol.ColumnName.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
I want to remove some "things" from collection if they aren't found in another collection.
When I run the above program, I get
Iteration may not execute as collection was modified
"Collection was modified" - Coz the remove must have been hit
What is the way to achieve such a thing then ?
What I can think of is make a note of all the "things" to remove and then
foreach( aThing in all_things_to_remove)
remove_from_collection(aThing)
But above doesn't seem a good way to me, as I have to do another looping and am using extra memory
In this specific case, where you're looping through a small collection containing a few columns, you can just create a new collection (via ToList()), so that you're not iterating over the same collection you're modifying:
foreach (var dataTableCol in dataTable.Columns.Cast<DataColumn>().ToList())
{
...
dataTable.Columns.Remove(dataTableCol.ColumnName);
}
The recommended way, especially if the collection is large, is to enumerate backwards:
for (var i = dataTable.Columns.Count - 1; i >= 0; i--)
{
...
dataTable.Columns.Remove(dataTable.Columns[i].ColumnName);
}
You cannot remove items from a collection while enumerating it with the foreach loop. Make a copy of the collection using Collection.ToArray() and run you foreach on the copy and remove your item from the actual collection.
Since DataTable.Columns does not have ToArray or ToList methods, you could use the CopyTo() method and copy the entire columns to a ColumnsArray.
If you do not want to create a copy, then you can use for loop instead of foreach loop. You could edit you code this way:
for (int i = 0; i < dataTable.Columns.Count; i++)
{
bool columnFound = false;
foreach (GRTColumnView uiColumn in descriptor.UIColumns)
{
if (dataTable.Columns[i].Name.Equals(uiColumn.Name))
{
columnFound = true;
break;
}
}
if (!columnFound)
{
if (this.dataTable.Columns.Contains(dataTableCol.ColumnName))
this.dataTable.Columns.Remove(dataTableCol.ColumnName);
}
}
the other way is lowering the index after removing column.
for (int i = 0; i < datatable.Columns.Count; i++)
{
if (datatable.Columns[i].ColumnName.Contains("Column"))
{
datatable.Columns.RemoveAt(i);
i--;
}
}
if (dt.Columns.Contains("RecordID")){
dt.Columns.Remove("RecordID");
dt.AcceptChanges();
}

How to get string value of a List<t> Item

I have some file paths stored in a List and need to attach them to an email. But how can I access the values (in my case: file paths as string values) of my List Items?
Here is the code:
List<string> filesToSend = new List<string>();
filesToSend = (List<string>)Session["filesListForFilesToSend"];
for (int i = 0; i < filesToSend.Count; i++)
{
//message.Attachments.Add(filesToSend[i].????????????????????);
}
Thanks in advance
filesToSend[i] will return the path string you want
Try This
foreach(string EachString in filesToSend)
{
message.Attachments.Add(EachString)
}
First, you do not need to instance a first list the after read the list in session, just:
List<string> filesToSend = (List<string>)Session["filesListForFilesToSend"];
When you access and List by index you will get the object of generic type. You can do it using a lot of ways, for sample:
using for loop:
for (int i = 0; i < filesToSend.Count; i++)
message.Attachments.Add(filesToSend[i]);
or foreach
foreach(string file in filesToSend)
message.Attachments.Add(file);
or while
int i = filesToSend.Lenght;
while(i--)
message.Attachments.Add(filesToSend[i]);
I would use foreach statement, but while will give you more performance (keep in mind you will loop in the reverse order).
The error was not how I was trying to get the string out of my List. The error was how I was trying to attach it to my message.
for (int i = 0; i < filesToSend.Count; i++)
{
string filePath = filesToSend[i];
Attachment attached = new Attachment(filePath);
attached.Name = filePath;
message.Attachments.Add(attached);
}
Thats the way it works for me. Thank you all

How to read tables from a particular place in a document?

When I use the below line It reads all tables of that particular document:
foreach (Microsoft.Office.Interop.Word.Table tableContent in document.Tables)
But I want to read tables of a particular content for example from one identifier to another identifier.
Identifier can be in the form of [SRS oraganisation_123] to another identifier [SRS Oraganisation_456]
I want to read the tables only in between the above mentioned identifiers.
Suppose 34th page contains my identifier so I want read all tables from that point to until I come across my second identifier. I don't want to read remaining tables.
Please ask me for any clarification in the question.
Say start and end Identifiers are stored in variables called myStartIdentifier and myEndIdentifier -
Range myRange = doc.Range();
int iTagStartIdx = 0;
int iTagEndIdx = 0;
if (myRange.Find.Execute(myStartIdentifier))
iTagStartIdx = myRange.Start;
myRange = doc.Range();
if (myRange.Find.Execute(myEndIdentifier))
iTagEndIdx = myRange.Start;
foreach (Table tbl in doc.Range(iTagStartIdx,iTagEndIdx).Tables)
{
// Your code goes here
}
Not sure how your program is structured... but if you can access the identifier in tableContent then you should be able to write a LINQ query.
var identifiers = new List<string>();
identifiers.Add("myIdentifier");
var tablesWithOnlyTheIdentifiersIWant = document.Tables.Select(tableContent => identifiers.Contains(tableContent.Identifier)
foreach(var tableContent in tablesWithOnlyTheIdentifiersIWant)
{
//Do something
}
Go through following code, if it helps you.
System.Data.DataTable dt = new System.Data.DataTable();
foreach (Microsoft.Office.Interop.Word.Cell c in r.Cells)
{
if(c.Range.Text=="Content you want to compare")
dt.Columns.Add(c.Range.Text);
}
foreach (Microsoft.Office.Interop.Word.Row row in newTable.Rows)
{
System.Data.DataRow dr = dt.NewRow();
int i = 0;
foreach (Cell cell in row.Cells)
{
if (!string.IsNullOrEmpty(cell.Range.Text)&&(cell.Range.Text=="Text you want to compare with"))
{
dr[i] = cell.Range.Text;
}
}
dt.Rows.Add(dr);
i++;
}
Go through following linked 3rd number answer.
Replace bookmark text in Word file using Open XML SDK

Adding text to multiples rows in word with a single bookmark

Is it possible to add several rows with the help of Bookmarks and openXML to a word document?
We have a worddocument that serves as a report template.
In that template we need to add several transaction rows.
The problem is that the number of rows aren't static. It could be 0, 1 or 42 for example.
In the current template (which we can change) we have added 3 bookmarks
TransactionPart, TransactionPart2 and TransactionPart3.
The tree transactionparts forms a singel row with three different datacontent (ID, Description, Amount)
If we have just one transaction row we have no problem adding the data to those bookmarks, but what do we do when we should add row two? There are no bookmarks for more rows.
Is there a smart way of doing this?
Or should we change the worddocument so that the rows end up in a table? Would that solve the problem in a better way?
I would put a single bookmark lets call it "transactions" inside a 3 coloumn table.
Like this
When you know the design of the table, but not the number of rows you'll be needing the simplest way is to add a row for each line of data you have.
You could accomplish that with a code like this
//make some data.
List<String[]> data = new List<string[]>();
for (int i = 0; i < 10; i++)
data.Add(new String[] {"this","is","sparta" });
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("yourDocument.docx", true))
{
var mainPart = wordDoc.MainDocumentPart;
var bookmarks = mainPart.Document.Body.Descendants<BookmarkStart>();
var bookmark =
from n in bookmarks
where n.Name == "transactions"
select n;
OpenXmlElement elem = bookmark.First().Parent;
//isolate tabel
while (!(elem is DocumentFormat.OpenXml.Wordprocessing.Table))
elem = elem.Parent;
var table = elem; //found
//save the row you wanna copy in each time you have data.
var oldRow = elem.Elements<TableRow>().Last();
DocumentFormat.OpenXml.Wordprocessing.TableRow row = (TableRow)oldRow.Clone();
//remove old row
elem.RemoveChild<TableRow>(oldRow);
foreach (String[] s in data)
{
DocumentFormat.OpenXml.Wordprocessing.TableRow newrow = (TableRow)row.Clone();
var cells = newrow.Elements<DocumentFormat.OpenXml.Wordprocessing.TableCell>();
//we know we have 3 cells
for(int i = 0; i < cells.Count(); i++)
{
var c = cells.ElementAt(i);
var run = c.Elements<Paragraph>().First().Elements<Run>().First();
var text = run.Elements<Text>().First();
text.Text = s[i];
}
table.AppendChild(newrow);
}
}
You end up with this
I've tested this code on a pretty basic document and know it works.
Good luck and let me know if I can clarify further.

Problem removing row in datatable while enumerating

I get the following error while I try to delete a row while looping through it.
C#: Collection was modified; enumeration operation may not execute
I've been doing some research for a while, and I've read some similar posts here, but I still haven't found the right answer.
foreach (DataTable table in JobsDS.Tables)
{
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
removeJob(id);
}
}
}
}
}
any help would be greatly appreciated
Instead of using foreach, use a reverse for loop:
for(int i = table.Rows.Count - 1; i >= 0; i--)
{
DataRow row = table.Rows[i];
//do your stuff
}
Removing the row indeed modifies the original collection of rows. Most enumerators are designed to explode if they detect the source sequence has changed in the middle of an enumeration - rather than try to handle all the weird possibilities of foreaching across something that is changing and probably introduce very subtle bugs, it is safer to simply disallow it.
You cannot modify a collection inside of a foreach around it.
Instead, you should use a backwards for loop.
If you want to remove Elements from a loop on a list of Elements, the trick is to use a for loop, start from the last Element and go to the first Element.
In your example :
int t_size = table.Rows.Count -1;
for (int i = t_size; i >= 0; i--)
{
DataRow row = table.Rows[i];
// your code ...
}
Edit : not quick enough :)
Also, if you depend on the order that you process the rows and a reverse loop does not work for you. You can add the rows that you want to delete to a List and then after you exit the foreach loop you can delete the rows added to the list. For example,
foreach (DataTable table in JobsDS.Tables)
{
List<DataRow> rowsToRemove = new List<DataRow>();
foreach (DataRow row in table.Rows)
{
if (row["IP"].ToString() != null && row["IP"].ToString() != "cancelled")
{
string newWebServiceUrl = "http://" + row["IP"].ToString() + "/mp/Service.asmx";
webService.Url = newWebServiceUrl;
string polledMessage = webService.mpMethod(row["IP"].ToString(), row["ID"].ToString());
if (polledMessage != null)
{
if (polledMessage == "stored")
{
//removeJob(id);
rowsToRemove.Add(row);
}
}
}
}
rowsToRemove.ForEach(r => removeJob(r["ID"].ToString()));
}
Somehow removeJob(id) changes one of the IEnumerables your enumerating (table.Rows or JobsDS.Tables, from the name of the method I guess it would be the latter), maybe via DataBinding.
I'm not sure the backwards for is going to work directly because it seems you're removing an element enumerated in the outer foreach from within the inner foreach. It's hard to tell without more info about what happens in removeJob(id).

Categories

Resources