簡體   English   中英

你如何在c#中郵件合並word文檔

[英]How do you mail merge a word document in c#

我正在努力實現的目標

在我的 c# 應用程序中,我想從應用程序中的數據生成報告(word 文檔),我認為最好的方法是使用我的應用程序中的數據源執行類似郵件合並的操作。

我試過的

  1. 我嘗試按照此郵件合並到 word 中,但是這使用了您需要付費的 GemBox
  2. 我曾嘗試使用 Microsoft.Office.Interop.Word 但是當我不知道如何引用保存的模板文檔時,我沒有做到:

     Dictionary<string, string> MailMerge = new Dictionary<string, string>() { { "ID", "123" }, { "Name", "Test" }, { "Address1", "Test" }, { "Address2", "Test" }, { "Address3", "Test" }, { "Address4", "Test" }, { "PostCode", "Test" }, { "Year End", "Test" }, { "SicCode", "123" }, }; Document doc = new Document(); doc.MailMerge.Execute(MailMerge);

概括

我正在尋找一些關於進一步研究什么的指導,因為我相信必須有一種“標准”的方式來做到這一點。

這通過使用Microsoft.Office.Interop.Word非常簡單。 是有關如何執行此操作的簡單分步教程。

用字符串替換合並域的代碼是這樣的:

public static void TextToWord(string pWordDoc, string pMergeField, string pValue)
{
    Object oMissing = System.Reflection.Missing.Value;
    Object oTrue = true;
    Object oFalse = false;
    Word.Application oWord = new Word.Application();
    Word.Document oWordDoc = new Word.Document();
    oWord.Visible = true;
    Object oTemplatePath = pWordDoc;
    oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);
    foreach (Word.Field myMergeField in oWordDoc.Fields)
    {
        Word.Range rngFieldCode = myMergeField.Code;
        String fieldText = rngFieldCode.Text;
        if (fieldText.StartsWith(" MERGEFIELD"))
        {
            Int32 endMerge = fieldText.IndexOf("\\");
            Int32 fieldNameLength = fieldText.Length - endMerge;
            String fieldName = fieldText.Substring(11, endMerge - 11);
            fieldName = fieldName.Trim();
            if (fieldName == pMergeField)
            {
                myMergeField.Select();
                oWord.Selection.TypeText(pValue);
            }
        }
    }
}

最初張貼在這里這里

如果您希望使用字典一次替換多個字段,請使用以下代碼:

public static void TextToWord(string pWordDoc, Dictionary<string, string> pDictionaryMerge)
    {
        Object oMissing = System.Reflection.Missing.Value;
        Object oTrue = true;
        Object oFalse = false;
        Microsoft.Office.Interop.Word.Application oWord = new Microsoft.Office.Interop.Word.Application();
        Microsoft.Office.Interop.Word.Document oWordDoc = new Microsoft.Office.Interop.Word.Document();
        oWord.Visible = true;
        Object oTemplatePath = pWordDoc;
        oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing);

        foreach (Microsoft.Office.Interop.Word.Field myMergeField in oWordDoc.Fields)
        {
            Microsoft.Office.Interop.Word.Range rngFieldCode = myMergeField.Code;
            String fieldText = rngFieldCode.Text;
            if (fieldText.StartsWith(" MERGEFIELD"))
            {
                Int32 endMerge = fieldText.IndexOf("\\");
                Int32 fieldNameLength = fieldText.Length - endMerge;
                String fieldName = fieldText.Substring(11, endMerge - 11);
                fieldName = fieldName.Trim();
                foreach (var item in pDictionaryMerge)
                {
                    if (fieldName == item.Key)
                    {
                        myMergeField.Select();
                        oWord.Selection.TypeText(item.Value);
                    }
                }
            }
        }
    }

無法相信第三方軟件會為 Word 的界面功能收取數千美元的費用。 我在我的項目中完美解決了這個郵件合並的事情——沒有第三方,對IIS沒有特殊要求,就用OpenXML。 因此,將這 4 個函數添加到您的項目中:

public static void dotx2docx(string sourceFile, string targetFile)
    {
        MemoryStream documentStream;
        using (Stream tplStream = File.OpenRead(sourceFile))
        {
            documentStream = new MemoryStream((int)tplStream.Length);
            CopyStream(tplStream, documentStream);
            documentStream.Position = 0L;
        }

        using (WordprocessingDocument template = WordprocessingDocument.Open(documentStream, true))
        {
            template.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
            MainDocumentPart mainPart = template.MainDocumentPart;
            mainPart.DocumentSettingsPart.AddExternalRelationship("http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate",
               new Uri(targetFile, UriKind.Absolute));

            mainPart.Document.Save();
        }
        File.WriteAllBytes(targetFile, documentStream.ToArray());
    }
    public static void CopyStream(Stream source, Stream target)
    {
        if (source != null)
        {
            MemoryStream mstream = source as MemoryStream;
            if (mstream != null) mstream.WriteTo(target);
            else
            {
                byte[] buffer = new byte[2048];
                int length = buffer.Length, size;
                while ((size = source.Read(buffer, 0, length)) != 0)
                    target.Write(buffer, 0, size);
            }
        }
    }
    public static void Mailmerge(string templatePath, string DestinatePath, DataRow dr, DataColumnCollection columns)
    {
        try
        {
            dotx2docx(templatePath, DestinatePath);
        }
        catch //incase the server does not support MS Office Word 2003 / 2007 / 2010
        {
            File.Copy(templatePath, DestinatePath, true);
        }
        using (WordprocessingDocument doc = WordprocessingDocument.Open(DestinatePath, true))
        {
            var allParas = doc.MainDocumentPart.Document.Descendants<DocumentFormat.OpenXml.Wordprocessing.Text>();
            Text PreItem = null;
            string PreItemConstant = null;
            bool FindSingleAnglebrackets = false;
            bool breakFlag = false;
            List<Text> breakedFiled = new List<Text>();
            foreach (Text item in allParas)
            {
                foreach (DataColumn cl in columns)
                {
                    //<Today>
                    if (item.Text.Contains("«" + cl.ColumnName + "»") || item.Text.Contains("<" + cl.ColumnName + ">"))
                    {
                        item.Text = item.Text.Replace("<" + cl.ColumnName + ">", dr[cl.ColumnName].ToString())
                                             .Replace("«" + cl.ColumnName + "»", dr[cl.ColumnName].ToString());
                        FindSingleAnglebrackets = false;
                        breakFlag = false;
                        breakedFiled.Clear();
                    }
                    else if //<Today
                    (item.Text != null
                        && (
                                (item.Text.Contains("<") && !item.Text.Contains(">"))
                                || (item.Text.Contains("«") && !item.Text.Contains("»"))
                            )
                        && (item.Text.Contains(cl.ColumnName))
                    )
                    {
                        FindSingleAnglebrackets = true;
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"\<" + cl.ColumnName + @"(?!\w)", dr[cl.ColumnName].ToString());
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"\«" + cl.ColumnName + @"(?!\w)", dr[cl.ColumnName].ToString());
                    }
                    else if //Today> or Today
                    (
                        PreItemConstant != null
                        && (
                                (PreItemConstant.Contains("<") && !PreItemConstant.Contains(">"))
                                || (PreItemConstant.Contains("«") && !PreItemConstant.Contains("»"))
                            )
                        && (item.Text.Contains(cl.ColumnName))
                    )
                    {
                        if (item.Text.Contains(">") || item.Text.Contains("»"))
                        {
                            FindSingleAnglebrackets = false;
                            breakFlag = false;
                            breakedFiled.Clear();
                        }
                        else
                        {
                            FindSingleAnglebrackets = true;
                        }
                        if (PreItemConstant == "<" || PreItemConstant == "«")
                        {
                            PreItem.Text = "";
                        }
                        else
                        {
                            PreItem.Text = global::System.Text.RegularExpressions.Regex.Replace(PreItemConstant, @"\<" + cl.ColumnName + @"(?!\w)", dr[cl.ColumnName].ToString());
                            PreItem.Text = global::System.Text.RegularExpressions.Regex.Replace(PreItemConstant, @"\«" + cl.ColumnName + @"(?!\w)", dr[cl.ColumnName].ToString());
                        }
                        if (PreItemConstant.Contains("<") || PreItemConstant.Contains("«")) // pre item is like '[blank]«'
                        {
                            PreItem.Text = PreItem.Text.Replace("<", "");
                            PreItem.Text = PreItem.Text.Replace("«", "");
                        }
                        if (item.Text.Contains(cl.ColumnName + ">") || item.Text.Contains(cl.ColumnName + "»"))
                        {
                            item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"(?<!\w)" + cl.ColumnName + @"\>", dr[cl.ColumnName].ToString());
                            item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"(?<!\w)" + cl.ColumnName + @"\»", dr[cl.ColumnName].ToString());

                        }
                        else
                        {
                            item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"(?<!\w)" + cl.ColumnName + @"(?!\w)", dr[cl.ColumnName].ToString());
                        }
                    }
                    else if (FindSingleAnglebrackets && (item.Text.Contains("»") || item.Text.Contains(">")))
                    {
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"(?<!\w)" + cl.ColumnName + @"\>", dr[cl.ColumnName].ToString());
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"(?<!\w)" + cl.ColumnName + @"\»", dr[cl.ColumnName].ToString());
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"^\s*\>", "");
                        item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"^\s*\»", "");
                        FindSingleAnglebrackets = false;
                        breakFlag = false;
                        breakedFiled.Clear();
                    }
                    else if (item.Text.Contains("<") || item.Text.Contains("«")) // no ColumnName
                    {

                    }
                } //end of each columns
                PreItem = item;
                PreItemConstant = item.Text;
                if (breakFlag
                    || (item.Text.Contains("<") && !item.Text.Contains(">"))
                    || (item.Text.Contains("«") && !item.Text.Contains("»"))
                   )
                {
                    breakFlag = true;
                    breakedFiled.Add(item);
                    string combinedfiled = "";
                    foreach (Text t in breakedFiled)
                    {
                        combinedfiled += t.Text;
                    }
                    foreach (DataColumn cl in columns)
                    {
                        //<Today>
                        if (combinedfiled.Contains("«" + cl.ColumnName + "»") || combinedfiled.Contains("<" + cl.ColumnName + ">"))
                        {
                            //for the first part, remove the last '<' and tailing content
                            breakedFiled[0].Text = global::System.Text.RegularExpressions.Regex.Replace(breakedFiled[0].Text, @"<\w*$", "");
                            breakedFiled[0].Text = global::System.Text.RegularExpressions.Regex.Replace(breakedFiled[0].Text, @"<\w*$", "");

                            //remove middle parts
                            foreach (Text t in breakedFiled)
                            {
                                if (!t.Text.Contains("<") && !t.Text.Contains("«") && !t.Text.Contains(">") && !t.Text.Contains("»"))
                                {
                                    t.Text = "";
                                }
                            }

                            //for the last part(as current item), remove leading content till the first '>' 
                            item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"^\s*\>", dr[cl.ColumnName].ToString());
                            item.Text = global::System.Text.RegularExpressions.Regex.Replace(item.Text, @"^\s*\»", dr[cl.ColumnName].ToString());

                            FindSingleAnglebrackets = false;
                            breakFlag = false;
                            breakedFiled.Clear();
                            break;
                        }
                    }
                }
            }//end of each item
            #region go through footer
            MainDocumentPart mainPart = doc.MainDocumentPart;
            foreach (FooterPart footerPart in mainPart.FooterParts)
            {
                Footer footer = footerPart.Footer;
                var allFooterParas = footer.Descendants<Text>();
                foreach (Text item in allFooterParas)
                {
                    foreach (DataColumn cl in columns)
                    {
                        if (item.Text.Contains("«" + cl.ColumnName + "»") || item.Text.Contains("<" + cl.ColumnName + ">"))
                        {
                            item.Text = (string.IsNullOrEmpty(dr[cl.ColumnName].ToString()) ? " " : dr[cl.ColumnName].ToString());
                            FindSingleAnglebrackets = false;
                        }
                        else if (PreItem != null && (PreItem.Text == "<" || PreItem.Text == "«") && (item.Text.Trim() == cl.ColumnName))
                        {
                            FindSingleAnglebrackets = true;
                            PreItem.Text = "";
                            item.Text = (string.IsNullOrEmpty(dr[cl.ColumnName].ToString()) ? " " : dr[cl.ColumnName].ToString());
                        }
                        else if (FindSingleAnglebrackets && (item.Text == "»" || item.Text == ">"))
                        {
                            item.Text = "";
                            FindSingleAnglebrackets = false;
                        }
                    }
                    PreItem = item;
                }
            }
            #endregion

            #region replace \v to new Break()
            var body = doc.MainDocumentPart.Document.Body;

            var paras = body.Elements<Paragraph>();
            foreach (var para in paras)
            {
                foreach (var run in para.Elements<Run>())
                {
                    foreach (var text in run.Elements<Text>())
                    {
                        if (text.Text.Contains("MS_Doc_New_Line"))
                        {
                            string[] ss = text.Text.Split(new string[] { "MS_Doc_New_Line" }, StringSplitOptions.None);
                            text.Text = text.Text = "";
                            int n = 0;
                            foreach (string s in ss)
                            {
                                n++;
                                run.AppendChild(new Text(s));
                                if (n != ss.Length)
                                {
                                    run.AppendChild(new Break());
                                }
                            }
                        }
                    }
                }
            }
            #endregion

            doc.MainDocumentPart.Document.Save();
        }
    }
    public static void MergeDocuments(params string[] filepaths)
    {

        //filepaths = new[] { "D:\\one.docx", "D:\\two.docx", "D:\\three.docx", "D:\\four.docx", "D:\\five.docx" };
        if (filepaths != null && filepaths.Length > 1)

            using (WordprocessingDocument myDoc = WordprocessingDocument.Open(@filepaths[0], true))
            {
                MainDocumentPart mainPart = myDoc.MainDocumentPart;

                for (int i = 1; i < filepaths.Length; i++)
                {
                    string altChunkId = "AltChunkId" + i;
                    AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
                        AlternativeFormatImportPartType.WordprocessingML, altChunkId);
                    using (FileStream fileStream = File.Open(@filepaths[i], FileMode.Open))
                    {
                        chunk.FeedData(fileStream);
                    }
                    DocumentFormat.OpenXml.Wordprocessing.AltChunk altChunk = new DocumentFormat.OpenXml.Wordprocessing.AltChunk();
                    altChunk.Id = altChunkId;
                    //new page, if you like it...
                    mainPart.Document.Body.AppendChild(new Paragraph(new Run(new Break() { Type = BreakValues.Page })));
                    //next document
                    mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Elements<Paragraph>().Last());
                }
                mainPart.Document.Save();
                myDoc.Close();
            }
    }

並像這樣使用它們:

DataTable dt = new DataTable();
dt.Columns.Add("Date");
dt.Columns.Add("Today");
dt.Columns.Add("Addr1");
dt.Columns.Add("Addr2");
dt.Columns.Add("PreferContact");
dt.Columns.Add("TenantName");
//......

DataRow nr = dt.NewRow();
nr["Date"] = DateTime.Now.ToString("dd/MM/yyyy");
                nr["Today"] = DateTime.Now.ToString("dd/MM/yyyy");
//......
dt.Rows.Add(nr);


string sourceFile = "c:\my_template.docx"; //this is where you store your template
string filePath = "c:\final.docx"; //this is where your result file locate
Mailmerge(sourceFile, filePath, nr, dt.Columns);

您的模板(c:\\my_template.docx) 就像普通的 .docx 文件一樣,您需要在其中指定您的字段:

<field>

所以,你的模板(c:\\my_template.docx) 應該是這樣的:

<Today> 

<DebtorName>
<DebtorADDR>
<DebtorEmail>


Dear <Dear>,

Congratulations on yourr property <PlanNo>  <BuildAddress>. Your unit number is <LotNo> ...............

此外,如果您的某些字段包含換行符,請使用以下命令:

nr["Address"] = my_address_text_contains_line_breaks.Replace(Environment.NewLine, "MS_Doc_New_Line");

我也在使用同樣的東西,但我有更多的復雜性。 我還必須檢查 if 條件。 在 word 模板文件中 { IF «Installment» = Monthly “然后表格將出現”“沒有顯示”}

當我使用答案中共享的上述代碼時。 對於我寫的 c# 中的 if 條件

Range rngFieldCode = myMergeField.Code;
            String fieldText = rngFieldCode.Text.Trim();
            if (fieldText.ToUpper().StartsWith("IF"))
            {
                myMergeField.UpdateSource();}

所以輸出就像

{ IF Monthly = Monthly "那么表格會出現" "沒什么可顯示的" }

但所需的輸出只是“然后表格會出現”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM