0

I have a C#/WPF application that allows the user to export information into a Word document. At the moment it works - and creates the document as expected - however the UI locks and the moment I try to thread this method I get varying errors.

The document creation takes in a list of custom items, then builds sections in the Word document based on each item. It creates a table for each image, and in those tables I insert an image placeholder. Once this is done I traverse the document and replace the placeholders with their associated image.

I believe the threading issue is due to the way images are inserted into the document - utilising Clipboard.Clear() and Clipboard.SetDataObject(img).

Is there a cleaner way for me to insert JPG's from disk into the document, or is there a nice way to thread such a method? Here is the offending method:

private static void InsertImagesTables(string document, List<Record> allRecords)
    {
        Document oDoc = oWord.Documents.Open(document);
        Object oMissing = Missing.Value;
        object NormalStyle = "Normal";
        oWord.Visible = false;
        foreach (Record record in allRecords)
        {
            foreach (RecordImage rImage in record.Images)
            {
                //insert over placeholder
                var range = oDoc.Content;
                if (range.Find.Execute("[[" + record.Title + rImage.ImagePath + "]]"))
                {
                    try
                    {
                        //insert the image
                        var prevRange = range.Previous(WdUnits.wdCharacter);
                        Table imageTable;
                        imageTable = oDoc.Tables.Add(range, 1, 1, ref oMissing, ref oMissing);
                        imageTable.Borders.InsideLineStyle = WdLineStyle.wdLineStyleNone;
                        imageTable.Borders.OutsideLineStyle = WdLineStyle.wdLineStyleNone;

                        Image img = Image.FromFile(rImage.ImagePath + ".jpg");
                        Clipboard.Clear();
                        Clipboard.SetDataObject(img);
                        imageTable.Cell(1, 1).Range.Paste();
                        imageTable.Cell(1, 1).Range.set_Style(ref NormalStyle);
                        imageTable.Cell(1, 1).Range.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter;

                        InlineShape inlineShape = imageTable.Cell(1, 1).Range.InlineShapes[1];

                        imageTable.Rows.Alignment = WdRowAlignment.wdAlignRowCenter;

                        string caption = rImage.Caption;
                        inlineShape.Range.InsertCaption(Label: "Figure", Title: " - " + caption, Position: WdCaptionPosition.wdCaptionPositionBelow);

                        range.Expand(WdUnits.wdParagraph);
                    }
                    catch // no image for record - do nothing
                    { }
                }
            }
        }

        oDoc.Close(true);
    }

I've tried BackgroundWorkers, Dispatchers, async Tasks and Threads (with and without ApartmentState.STA) with varying outcomes. Most just raise an error, but a few run and complete, without placing every image in the document - such as the STA approach.

Any help here is much appreciated,

Mike

Mike
  • 61
  • 5
  • Can you specify errors? – vernou Aug 18 '21 at 08:46
  • So using Thread thread = new Thread(() => WordStart(record)); thread.Start(); the task completes, but only some of the images get inserted. Same if I specify thread.SetApartmentState(ApartmentState.STA); (this also requires a reboot as reverting my code continues with the same problem) Same as above happens with Task wordThread = Task.Factory.StartNew(() => WordStart(finding)); Task.WaitAll(wordThread); – Mike Aug 18 '21 at 08:57
  • BackgroundWorker does exactly the same. I have a feeling it's the Clipboard interaction, reading things like this: https://stackoverflow.com/questions/20407114/strange-behaviour-with-clipboard-in-c-sharp-console-application I'll have a play with that – Mike Aug 18 '21 at 09:10
  • Same error when wrapping the Clipboard interaction via the method in the above link - not all images added – Mike Aug 18 '21 at 09:26
  • In fact, it seems that no images are getting added with these approaches. I've just tried ThreadPool.QueueUserWorkItem with the same outcome – Mike Aug 18 '21 at 10:02
  • What do `WordStart(record)`? – vernou Aug 18 '21 at 10:09
  • You can't parallelized the operation because Clipboard, you need do encapsulate the full `InsertImagesTables` in a asynchrone task. – vernou Aug 18 '21 at 10:12
  • thanks, I'll give that a try if my next attempt doesn't work. I'm getting completely rid of clipboard interaction and am using: Range cellRange = imageTable.Cell(1, 1).Range; cellRange.InlineShapes.AddPicture(rImage.ImagePath + ".jpg", ref oMissing, ref oMissing, ref oMissing); – Mike Aug 18 '21 at 10:21

1 Answers1

1

Fixed. Thanks vernou for taking the time. I solved it by removing the Clipboard interaction and instead, properly utilising Word interop with a backgroundworker:

Range cellRange = imageTable.Cell(1, 1).Range;
cellRange.InlineShapes.AddPicture(rImage.ImagePath + ".jpg", ref oMissing, ref oMissing, ref oMissing);

instead of:

Clipboard.Clear();
Clipboard.SetDataObject(img);
                        
imageTable.Cell(1, 1).Range.Paste();
Mike
  • 61
  • 5