I have a word document, that uses several textboxes, which contain mail merge fields.
I want to mail merge programmatically into a word document, by using a system that I have made, that searches for any mail merge fields in a document and then inserts the appropriate value into the mail merge fields and then saves the document as a new file.
By using
Document.StoryRanges
I am able to do the above process for 1 text box.
But if I make several text boxes, it only seems to insert the value into one of the textboxes consistently. The texbox that receives the value does not change. If I try to delete that textbox, the mail merging process does not work and then I have to fiddle with some of the other textboxes to get it to work. For example I have to bring the textbox backwards and then forwards for the system to mail merge into only of the textboxes.
I have tried creating a foreach loop to go into each textbox, without much success. So I did some debugging and found that the system is only reading the entire document and one of the textboxes as StoryRanges.
Here's a solution I used recently. You can get to the mail merge fields through Document.Shapes. I'm not sure exactly what's going on here, but it worked for me.
public static List<Field> getMailMergeFields(Document document)
{
List<Field> mailMergeFields = new List<Field>();
foreach (Shape shape in document.Shapes)
{
if (shape.TextFrame.HasText != 0)
{
foreach (Field field in shape.TextFrame.ContainingRange.Fields)
{
if (isMailMergeField(field)) mailMergeFields.Add(field);
}
}
}
foreach (Field field in document.Fields)
{
if (isMailMergeField(field)) mailMergeFields.Add(field);
}
return mailMergeFields;
}
public static bool isMailMergeField(Field field)
{
string fullField = field.Code.Text.Trim();
if (!fullField.StartsWith("MERGEFIELD")) return false;
if (!fullField.EndsWith(#"\* MERGEFORMAT")) return false;
return true;
}
Hope this helps.
Related
I have got a txt file with 10 lines in it, each line is a record with 5 different fields;
Farrell,Jade,Louise,2011/09/13,F
I am using the commas to split record by FamilyName, FirstName, MiddleName, EnrolmentDate and Gender. I want each field to have its own text box then use buttons to look through the different records.
Everything so far is working under the load button which reads the data from the file and puts it into the text boxes using the code below which works but it only shows the first record so i want a buttons to show the next record, previous record, first and last record and also a button to sort the data from A-Z by the family name. Any help on how to go forward would be great! thanks!
private void Load_BT_Click(object sender, EventArgs e)
{
OpenFileDialog filechooser = new OpenFileDialog();
StreamReader filereader = new StreamReader("StudentFile.txt");
String inputrecord = filereader.ReadLine();
string[] inputfields;
if (inputrecord != null)
{
inputfields = inputrecord.Split(',');
FamName_TXT.Text = inputfields[0];
FirstName_TXT.Text = inputfields[1];
MiddleName_TXT.Text = inputfields[2];
Enrolment_txt.Text = inputfields[3];
Gender_TXT.Text = inputfields[4];
}
else
{
MessageBox.Show("End of File");
}
}
I think there are some design issues here, but i will address your immediate concern. The problem is you only read one line. You need to iterate over all the lines in the textfile. I am assuming you want the load button to load all the data at once.
string[] allRecords = filereader.ReadAllLines();
foreach(string inputRecord in allRecords) {
string[] inputfields = inputRecord.Split(',');
//insert the textbox.Text += inputfields[0] + "\n"; etc
}
if you want a single button to resort the data across all the textboxes.
you really should create a class called Person with properties that correspond to your fields and override compareTo so you can sort by last name or maybe use linq to do the sort for you.
you need a list that will host all of these person objects
from there you can populate the textboxes accordingly
Create a sort button that will reorder the list or create a new list and repopulate the textboxes.
1-3 would be done in the load button. The reason for the person class is because you want all the data you read in from the file to be associate with an object. if you try to do the sorting directly from the textboxes as you seem to be trying to do you will run into issues such as how to make sure the data doesnt get jumbled. It certainly may be possible but it is not an elegant way and would be more work in my mind
I'm creating a Microsoft Office 2013 Add-in that does some behaviour similar to the Address book in an Active Mail item:
I'm working with Microsoft.Office.Interop.Outlook.MailItem and using the Recipients property, and the Recipients.ResolveAll() method to load and remove addresses to it, everything seems to work fine until I click OK on my loaded from so I send control back to the active mail Item. At this point Outlook goes crazy and doesn't place the addresses properly in the To or the CC, or some of them go missing.
I'm thinking that one solution to the problem could be to trigger the Check Names when the user is ready to click OK and send all the addresses from my form to the Active mail item.
How can I trigger this action / button?
I couldn't find anything that could do that in the MailItem class. I would like something similar to this (but to use from within my poped-out windows form in Outloook):
UPDATE:
This are some bits of my code to add more context:
I use the method AddRecipientToActiveItem for each item that I have. It will verify if it already exists (it was already added), if not it will resolve it and if it is correct add it.
private void AddRecipientToActiveItem(string recipientAddress, Recipients recipientList, OlMailRecipientType recipientType)
{
Recipient recipientObject = default(Recipient);
if (!string.IsNullOrWhiteSpace(recipientAddress) && !EmailRecipientAlreadyExists(recipientAddress, recipientType))
{
recipientObject = recipientList.Add(recipientAddress);
recipientObject.Resolve();
if (recipientObject.Resolved)
{
recipientObject.Type = (int)recipientType;
recipientList.ResolveAll();
}
else
{
recipientObject.Delete();
}
}
}
for this, I have to iterate through each element in the list of recipients and compare by address + type (From,To,CC,BCC) pair:
private bool EmailRecipientAlreadyExists(string fullEmailAddress, OlMailRecipientType recipientType)
{
foreach (Microsoft.Office.Interop.Outlook.Recipient recipientObject in ActiveMailItem.Recipients)
{
if (GetRecipientEmailAddress(recipientObject) != null)
{
if (GetRecipientEmailAddress(recipientObject).Equals(fullEmailAddress) && recipientObject.Type == (int)recipientType)
return true;
}
}
return false;
}
But the user can also add addresses to their email item manually, and some of them exist in the exchange server, but others are simple smtp address, so when comparing I have to handle both scenarios:
private string GetRecipientEmailAddress(Microsoft.Office.Interop.Outlook.Recipient recipientObject)
{
Outlook.ExchangeUser objExchangeUser = null;
if (recipientObject.Address != null && recipientObject.AddressEntry != null)
objExchangeUser = recipientObject.AddressEntry.GetExchangeUser();
if (recipientObject.Address == null && objExchangeUser == null)
return recipientObject.Name;
if (objExchangeUser == null)
return recipientObject.Address;
return objExchangeUser.PrimarySmtpAddress;
}
}
It seems like this what I've done is not enough to keep consistent states of the email addresses when I read them from the mail item and put them in a text box of my form, then add more addresses and send them back to the mail item and close the form, and repeat the process.
The Namespace class provides the GetSelectNamesDialog method which obtains a SelectNamesDialog object for the current session. It displays the Select Names dialog box for the user to select entries from one or more address lists, and returns the selected entries in the collection object specified by the property SelectNamesDialog.Recipients.
Sub SelectRecipients()
Dim oMsg As MailItem
Set oMsg = Application.CreateItem(olMailItem)
Dim oDialog As SelectNamesDialog
Set oDialog = Application.Session.GetSelectNamesDialog
With oDialog
.InitialAddressList = _
Application.Session.GetGlobalAddressList
.Recipients = oMsg.Recipients
If .Display Then
'Recipients Resolved
oMsg.Subject = "Hello"
oMsg.Send
End If
End With
End Sub
The dialog box displayed by SelectNamesDialog.Display is similar to the Select Names dialog box in the Outlook user interface. It observes the size and position settings of the built-in Select Names dialog box. However, its default state does not show Message Recipients above the To, Cc, and Bcc edit boxes. For more information on using the SelectNamesDialog object to display the Select Names dialog box, see Display Names from the Address Book.
I have a problem with the code below, basically what is does is read from a path where files with extension .config are stored, it reads the names of the files without extension and displays them all in a combobox. That works fine and if you click on the down arrow and select a name it actually does what it's supposed to, however, once I have selected an item from the dropdown with the mouse and I go back and start typing inside the combobox my application crashes throwing a exception.
I've tried adding a try-catch-finally but it keeps throwing the same error. Could it be the loop that is causing my application to crash once I start typing in the combobox?
p.d. If I just use the mouse to select an item from the dropdown menu my application works fine but once I've selected an item with the mouse and use the keyboard to type another item name inside the combobox my app crashes. Any pointers would be helpful.
// Gets all the file names from the path assigned to templatePath
// and assigns it to the string array fname
string[] fname = Directory.GetFiles(templatePath);
// Begin sorting through the file names assigned to the string array fname
foreach (string file in fname)
{
// Remove the extension from the file names and compare the list with
// the dropdown selected item
if (System.IO.Path.GetFileNameWithoutExtension(file) == cbTemplates.SelectedItem.ToString())
{
// StreamReader gets the contents from the found file and assigns
// them to the labels
using (var obj = new StreamReader(File.OpenRead(file)))
{
lbl1.Content = obj.ReadLine();
lbl2.Content = obj.ReadLine();
lbl3.Content = obj.ReadLine();
lbl4.Content = obj.ReadLine();
lbl5.Content = obj.ReadLine();
lbl6.Content = obj.ReadLine();
lbl7.Content = obj.ReadLine();
lbl8.Content = obj.ReadLine();
lbl9.Content = obj.ReadLine();
lbl10.Content = obj.ReadLine();
obj.Dispose();
}
}
}
My guess is this is probably causing the error:
cbTemplates.SelectedItem.ToString()
When you start typing in the combobox, the SelectedItem becomes null.
You should test whether the cbTemplates.SelectedItem is null before attempting to invoke ToString() on it. And if you're trying to match on the text of the combo-box, you might try using cbTemplates.Text instead.
And as others commented on your question, you don't need to call Dispose inside using and you should consider the possibility that the file might not contain 10 lines..
I am trying to make a tool to take a formatted text file that stores our logging information and returns only certain parts of it. For example the log file looks something like this:
[TimeStamp] <Config>: Configuration note is shown here
[TimeStamp] <Info>: Information is written here
[TimeStamp] <Info>: More Information
[TimeStamp] <Step>: A generated step is writing a message
[TimeStamp] <Warning>: A warning is logged
[TimeStamp] <Error>: An error has occurred
I would like to take this text from the file, and based on checkboxes for each log message type in the angle brackets, the user can hide what they don't care to see. Such as unchecking the 'Step' checkbox would hide the Step line, but if they were to recheck it, it would reappear in the text window.
I tried storing each line into a string which is stored in a list to keep each line in order, however this method is very very slow at changing the text. The method is shown below
logTextbox.Text = "";
foreach (string line in CompleteLog) //CompleteLog is list containing each line in log file
{
if (CheckLine(line)) //Checks line based on what the user wants to see
{
WriteLine(line);
}
}
Any suggestions would be very welcome
Edit:
private bool CheckLine(string line)
{
int left = line.IndexOf('<');
int right = line.IndexOf(">:");
string logtype = line.Substring(left+1, right - left-1);
if (ValidLogs.Any(p => p.ToLower().Equals(logtype.ToLower())))
{
return true;
}
return false;
}
ValidLogs is a list of strings that contain what is allowed that is setup on load, and changed on a check event for the checkboxes corresponding to the log type. The 1st method above is used on load and each check event to update what is displayed.
I think the best way to achieve this would be to use a listview control. You could add an item per line in the text file and then filter the items (rows) that are displayed depending on the checkboxes. May I recommend ObjectListView as a way of achieving this and making it look beautiful.
Assuming you don't have memory limitations or excessively large files and your filter criteria are fixed you could pre-parse each line for the filter criteria and store the filter details on a log object you'd then have objects with simple properties to check rather having to re-parse everytime.
public class LogLine {
public string Text { get; set; }
public eLogLevel LogLevel { get; set; }
// other filter properties
}
Then you would change your CheckLines() to filter the list of LogLine objects.
logTextbox.Text = "";
foreach (LogLine line in ParsedLog) //ParsedLog is list containing each line in log file pre-parsed
{
if (CheckLine(line)) //Checks line based on what the user wants to see
{
WriteLine(line.Text);
}
}
I am new to C#
I just want to ask if is it possible to check against a SUBItem in a listview for repeated values before i do a ADD() method??
Lets say i have a listview and i can add/delete items
I can have numerous items and subitems
I would like to perform a check before adding the file that i'm opening into the listview
The file that I'm going to put into would be, name of a file, eg example.txt
And if this file exists in the subitem, i will not add into the listview
Does anyone have any idea on how to check a subitem value against the value that I'm going to add into?
TIA
Well, you can iterate through the Items property of the ListView, and then the Subitems property for each item and finally check against the subitem's Text property.
The other option is to store the already added items in a List, and check if it already contains that item you want to add.
Edit: as requested, added sample code below.
private bool _CheckFileName(string fileName)
{
foreach(ListViewItem item in this.myListView.Items)
{
// this is the code when all your subitems are file names - if an item contains only one subitem which is a filename,
// then you can just against that subitem, which is better in terms of performance
foreach(ListViewItem.ListViewSubItem subItem in item.SubItems)
{
// you might want to ignore the letter case
if(String.Equals(fileName, subItem.Text))
{
return false;
}
}
}
return true;
}
using(var ofd = new OpenFileDialog())
{
// ... setup the dialog ...
if(ofd.ShowDialog() == DialogResult.Cancel)
{
// cancel
return;
}
// note that FileOpenDialog.FileName will give you the absolute path of the file; if you want only the file name, you should use Path.GetFileName()
if(!_CheckFileName(ofd.FileName))
{
// file already added
return;
}
// we're cool...
}
I didn't test the code so it is possible that I have some typos, if so, please add a comment and I'll fix it (though perhaps it would be better if you tried to figure it out yourself first :)).
You could create a helper class that contains information about each item. For each ListViewItem you create a new instance of this class and set ListViewItem.Tag to this instance.
You'd only have to iterate over all the items, get the helper object for the item and compare with that helper object.