Make a Word document containing pictures from files with names that match a pattern in C#

Occasionally I save images that I find interesting from the internet. Recently I wanted to combine all of the pictures of tree houses that I had into a single Word document. This example is the result.

Enter a directory name in the upper TextBox. Or click the ellipsis button on the right to select a directory.

Select or enter a file matching pattern and check the Include Subdirectories box if desired. Then click ListFiles. The program searches the directory (and optionally its subdirectories) for files matching the pattern and lists them in the CheckedListBox.

Initially all of the files are checked. You can individually check and uncheck files, or use the CheckBox above the ListBox to select or deselect all of the files at the same time.

Enter the name of the output file and click Create Document to make the Word document.

The two most interesting pieces of the program are those that list files matching the pattern you enter and that create the Word document. The following code lists the files.

// List files that match the pattern.
private void btnListFiles_Click(object sender, EventArgs e)
{
    // Get the directory.
    string path = txtDirectory.Text.Trim();
    if (path.Length == 0)
    {
        MessageBox.Show("Please enter a directory name");
        return;
    }

    // Get the file pattern text without parentheses.
    string pattern_text = cboPattern.Text;
    if (pattern_text.Contains("("))
    {
        int pos1 = pattern_text.IndexOf("(");
        int pos2 = pattern_text.IndexOf(")");
        pattern_text = pattern_text.Substring(pos1 + 1, pos2 - pos1 - 1);
    }

    // Get individual file matching patterns.
    List patterns = new List();
    foreach (string pattern in pattern_text.Split(
        new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
    {
        patterns.Add(pattern.Trim());
    }

    // Get the files.
    SearchOption search_option = SearchOption.TopDirectoryOnly;
    if (chkIncludeSubdirectories.Checked)
        search_option = SearchOption.AllDirectories;
    clbFiles.Items.Clear();
    chkAll.Checked = false;
    foreach (string pattern in patterns)
    {
        // Get files matching this pattern.
        foreach (string filename in
            Directory.GetFiles(path, pattern, search_option))
        {
            // See if we not yet listed this file.
            if (!clbFiles.Items.Contains(filename))
            {
                // Add the filename to the list.
                clbFiles.Items.Add(filename);
            }
        }
    }

    // Initially select all files.
    chkAll.Checked = true;
}

This code starts by getting the directory name. It then removes the file patterns from the text you entered and splits them apart. For example, one of the pre-defined patterns is "Picture Files (*.png; *.jpg; *.gif; *.bmp)." If you select this pattern, the program extracts the patterns inside the parentheses and then splits them apart into the patterns *.png, *.jpg, *.gif, and *.bmp.

Next the code loops through the patterns. For each pattern, it uses the Directory.GetFiles method to get the files matching the pattern. If the clbFiles CheckedListBox doesn't already contain a particular file, the code adds it. (The code doesn't add a file if it is already in the list because a file might match more than one pattern.)

I've posted other examples that show how to create Word documents from a C# program but here's a quick review. First open the Add References dialog. On the COM tab, select "Microsoft Word 12.0 Object Library" (or whatever version you have installed on your system).

You can make the library easier to use if you add the following using statement to your code.

using Word = Microsoft.Office.Interop.Word;

The following code shows how the program creates the Word document./

// Create the Word document.
private void btnCreateFile_Click(object sender, EventArgs e)
{
    // Get the file name.
    string filename = txtFilename.Text.Trim();
    if (filename.Length == 0)
    {
        MessageBox.Show("Please enter a document name");
        return;
    }

    // Get the Word application object.
    Word._Application word_app = new Word.ApplicationClass();

    // Make Word visible (optional).
    word_app.Visible = true;

    // Create the Word document.
    object missing = Type.Missing;
    Word._Document word_doc = word_app.Documents.Add(
        ref missing, ref missing, ref missing, ref missing);

    // Add the image files.
    Word.Paragraph para;
    foreach (string picture_file in clbFiles.CheckedItems)
    {
        // Make a paragraph.
        para = word_doc.Paragraphs.Add(ref missing);

        // Add the picture to the paragraph.
        Word.InlineShape inline_shape = para.Range.InlineShapes.AddPicture(
            picture_file, ref missing, ref missing, ref missing);

        // Format the picture.
        Word.Shape shape = inline_shape.ConvertToShape();

        // Scale uniformly by 50%.
        shape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue;
        shape.ScaleHeight(0.5f, Microsoft.Office.Core.MsoTriState.msoTrue,
            Microsoft.Office.Core.MsoScaleFrom.msoScaleFromTopLeft);
        shape.WrapFormat.Type = Word.WdWrapType.wdWrapInline;
        
        // Add the file's name.
        para.Range.InsertParagraphAfter();
        para.Range.InsertAfter(picture_file);
    }

    // Save the document.
    object filename_obj = filename;
    word_doc.SaveAs(ref filename_obj, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing);

    // Close.
    object save_changes = false;
    word_doc.Close(ref save_changes, ref missing, ref missing);
    word_app.Quit(ref save_changes, ref missing, ref missing);

    MessageBox.Show("Done");
}

This event handler first gets the name of the file you want to create. It then creates a Word application server object. This object provides all of the Word functionality that the program will use.

This example makes the Word application visible while it works. If you don't want to see the file as it is created, set the server's Visible property to false.

Calls to the Word server's methods are a bit unusual. You must provide all of the arguments expected by a method and you cannot simply pass null in for any argument that you don't want to use. Instead you must pass the special value Type.Missing and you must pass it by reference.

To make using Type.Missing a tiny bit easier and more concise, the program creates an object type variable named missing and sets it equal to Type.Missing.

Next the program calls the Word server's Documents.Add method to create a new file. It passes the value missing for the method's arguments. (Those arguments would specify the template to use and whether the document should be visible.)

The code then loops over the file names that you selected in the CheckedListBox. For each file name, the program calls the Word document object's Paragraphs.Add method to make a new paragraph. It then uses the paragraph's Range property (many operations in Word rely on Ranges). It calls the Range's InlineShapes collection's AddPicture method to create a picture in the paragraph. It passes the AddPicture method the file's name.

Next the program needs to manipulate the picture a little so it converts it into an InlineShape object. It scales the object by 50% vertically and horizontally and sets the picture's wrapping mode to place the image inline within the text.

The code then adds a paragraph at the end of the current paragraph and inserts the file's name there.

After it has finished loading all of the pictures, the program saves the Word document and closes the Word application server.

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments

  • 6/26/2013 9:54 AM Tanato wrote:
    After C#4.0 you can use named parameter instead of have to use a bunch of "ref Type.Missing". Much more elegant solution.
    Reply to this
    1. 6/26/2013 11:29 PM Rod Stephens wrote:
      I totally agree! Much easier to read.

      In fact, even if you are using a lot of the arguments, you can name them to make it easier to understand what each of the values you pass in represents.
      Reply to this
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.