Search code examples
c#vstoadd-in

Can't delete all comments in VSTO C# Word Add In


I'm trying to loop through all comments in a Word Doc and Clear all the comments that contain a substring of "CME" in the comment text. The application is a VSTO Add-In written in C# and it's using .NET 4.8.

I have 2 problems.

  1. The delete method isn't in the list of methods I can choose here Globals.ThisAddIn.Application.ActiveDocument.Comments[i]
  2. When I use the DeleteRecursively() Method, it doesn't delete all the comments, it leaves one and I have to click the clear comments button on the ribbon again to get rid of the last one.

What I have tried.

  1. I have installed office interop library via nuget.
  2. I have tried looking for other methods on the Comment in the collection, but haven't found anything that works.
  3. Searched the web.

Here is the method

private void btnClearComments_Click(object sender, RibbonControlEventArgs e)
        {
            if (Globals.ThisAddIn.Application.ActiveDocument.Comments.Count != 0)
            {
              //  Globals.ThisAddIn.Application.ActiveDocument.DeleteAllComments();                                                 
              //  MessageBox.Show("Clearing All Comments ");
              for(int i = 1; i <= Globals.ThisAddIn.Application.ActiveDocument.Comments.Count; i++)
                {
                    if (Globals.ThisAddIn.Application.ActiveDocument.Comments[i].Range.Text.Contains("CME"))
                    {
                        Globals.ThisAddIn.Application.ActiveDocument.Comments[i].DeleteRecursively();   
                    }
                }
                
            }
            else
            {
               
                MessageBox.Show("There are No Comments to Delete");                

            }            

        }

Any Help would be appreciated, thanks.


Solution

    1. The Comment object does not have a Delete method, see docs.

    2. It probably also depends on the number of comments? The Comments collection in Word is 1-based. That is confusing from a C# point of view, which is always 0-based. When there is 1 comment left, clicking will remove it. But, depending on the total number of comments, several clicks may be necessary? That is, if I understand your code correctly.

    There are a few things I would like to point out.

    • There is no need to check for the number of comments <= 0.
    • it is a common mistake to alter a collection while iterating over it.

    Consider your code:

    // imagine we have 3 things, and as with Comments they start counting at 1
    for (counter = 1; i <= things.Count; i++)
    {
        remove things[counter]; // remove the thing at counter. 
    } 
    

    Let's take it step by step. after the first loop, counter is 2 now, and there are also 2 things left. So the next loop will remove another thing, leaving 1, but counter will be 3 so it will never be processed.

    • @Dmitry is therefore correct in suggesting doing the deletion in reverse order.
    • That would have the added benefit of having to call the Count property just once. In this case it is a call to a COM object, which is particularly expensive. But it is always good to avoid unnecessary calls.

    Consider this:

    private void btnClearComments_Click(object sender, RibbonControlEventArgs e)
    {
        int nComments = Globals.ThisAddIn.Application.ActiveDocument.Comments.Count; // single call
        for (int i = nComments; nComments >= 1 ; i--) // keeping with the index 1 theme
        {
            if (Globals.ThisAddIn.Application.ActiveDocument.Comments[i].Range.Text.Contains("CME"))
            {
                Globals.ThisAddIn.Application.ActiveDocument.Comments[i].DeleteRecursively();   
            }
        }
    }