2009-05-14 4 views
2

Ich baue ein MS Word-Add-In, das alle Kommentarballons aus einem Dokument sammeln und in einer Liste zusammenfassen muss. Mein Ergebnis wird eine Liste der ReviewItem-Klassen sein, die den Kommentar selbst, die Absatznummer und die Seitenzahl enthalten, auf der sich der kommentierte Text befindet.VSTO 2007: Wie ermittle ich die Seiten- und Absatznummer eines Bereichs?

Teil meiner Code sieht wie folgt aus:

private static List<ReviewItem> FindComments() 
    { 
     List<ReviewItem> result = new List<ReviewItem>(); 
     foreach (Comment c in WorkingDoc.Comments) 
     { 
      ReviewItem item = new ReviewItem() 
      { 
       Remark = c.Reference.Text, 
       Paragraph = c.Scope. ???, // How to determine the paragraph number? 
       Page = c.Scope. ??? // How to determine the page number? 
      }; 
      result.Add(item); 
     } 
     return result; 
    } 

Die Scope Eigentum der Comment Klasse verweist auf den aktuellen Text im Dokument der Kommentar ist und ist vom Typ Microsoft.Office.Interop.Word.Range. Ich kann nicht herausfinden, auf welcher Seite und in welchem ​​Absatz dieser Bereich liegt.

Mit Absatznummer meine ich eigentlich die "nummerierte Liste" Nummer des Absatzes, wie "2.3" oder "1.3.2".

Irgendwelche Vorschläge? Vielen Dank!

Antwort

8

Mit Hilfe Mike Regan mir in seiner Antwort gab (nochmals vielen Dank Mike), gelang es mir, eine Lösung, die ich hier teilen möchten auszuarbeiten. Vielleicht verdeutlicht dies auch, was mein Ziel war. In Bezug auf die Leistung ist dies möglicherweise nicht die schnellste oder effizienteste Lösung. Fühlen Sie sich frei, Verbesserungen vorzuschlagen.

Das Ergebnis meines Codes ist eine Liste der ReviewItem-Klassen, die an anderer Stelle verarbeitet werden. Ohne weitere Umschweife, hier ist der Code:

/// <summary> 
/// Worker class that collects comments from a Word document and exports them as ReviewItems 
/// </summary> 
internal class ReviewItemCollector 
{ 
    /// <summary> 
    /// Working document 
    /// </summary> 
    private Word.Document WorkingDoc = new Word.DocumentClass(); 

    /// <summary> 
    /// Extracts the review results from a Word document 
    /// </summary> 
    /// <param name="fileName">Fully qualified path of the file to be evaluated</param> 
    /// <returns></returns> 
    public ReviewResult GetReviewResults(string fileName) 
    { 
     Word.Application wordApp = null; 
     List<ReviewItem> reviewItems = new List<ReviewItem>(); 

     object missing = System.Reflection.Missing.Value; 

     try 
     { 
      // Fire up Word 
      wordApp = new Word.ApplicationClass(); 

      // Some object variables because the Word API requires this 
      object fileNameForWord = fileName; 
      object readOnly = true; 

      WorkingDoc = wordApp.Documents.Open(ref fileNameForWord, 
       ref missing, ref readOnly, 
       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); 

      // Gather all paragraphs that are chapter headers, sorted by their start position 
      var headers = (from Word.Paragraph p in WorkingDoc.Paragraphs 
          where IsHeading(p) 
          select new Heading() 
          { 
           Text = GetHeading(p), 
           Start = p.Range.Start 
          }).ToList().OrderBy(h => h.Start); 

      reviewItems.AddRange(FindComments(headers)); 

      // I will be doing similar things with Revisions in the document 
     } 
     catch (Exception x) 
     { 
      MessageBox.Show(x.ToString(), 
       "Error while collecting review items", 
       MessageBoxButtons.OK, 
       MessageBoxIcon.Error); 
     } 
     finally 
     { 
      if (wordApp != null) 
      { 
       object doNotSave = Word.WdSaveOptions.wdDoNotSaveChanges; 
       wordApp.Quit(ref doNotSave, ref missing, ref missing); 
      } 
     } 
     ReviewResult result = new ReviewResult(); 
     result.Items = reviewItems.OrderBy(i => i.Position); 
     return result; 
    } 

    /// <summary> 
    /// Finds all comments in the document and converts them to review items 
    /// </summary> 
    /// <returns>List of ReviewItems generated from comments</returns> 
    private List<ReviewItem> FindComments(IOrderedEnumerable<Heading> headers) 
    { 
     List<ReviewItem> result = new List<ReviewItem>(); 

     // Generate ReviewItems from the comments in the documents 
     var reviewItems = from Word.Comment c in WorkingDoc.Comments 
          select new ReviewItem() 
          { 
           Position = c.Scope.Start, 
           Page = GetPageNumberOfRange(c.Scope), 
           Paragraph = GetHeaderForRange(headers, c.Scope), 
           Description = c.Range.Text, 
           ItemType = DetermineCommentType(c) 
          }; 

     return reviewItems.ToList(); 
    } 

    /// <summary> 
    /// Brute force translation of comment type based on the contents... 
    /// </summary> 
    /// <param name="c"></param> 
    /// <returns></returns> 
    private static string DetermineCommentType(Word.Comment c) 
    { 
     // This code is very specific to my solution, might be made more flexible/configurable 
     // For now, this works :-) 

     string text = c.Range.Text.ToLower(); 

     if (text.EndsWith("?")) 
     { 
      return "Vraag"; 
     } 
     if (text.Contains("spelling") || text.Contains("spelfout")) 
     { 
      return "Spelling"; 
     } 
     if (text.Contains("typfout") || text.Contains("typefout")) 
     { 
      return "Typefout"; 
     } 
     if (text.ToLower().Contains("omissie")) 
     { 
      return "Omissie"; 
     } 

     return "Opmerking"; 
    } 

    /// <summary> 
    /// Determine the last header before the given range's start position. That would be the chapter the range is part of. 
    /// </summary> 
    /// <param name="headings">List of headings as identified in the document.</param> 
    /// <param name="range">The current range</param> 
    /// <returns></returns> 
    private static string GetHeaderForRange(IEnumerable<Heading> headings, Word.Range range) 
    { 
     var found = (from h in headings 
        where h.Start <= range.Start 
        select h).LastOrDefault(); 

     if (found != null) 
     { 
      return found.Text; 
     } 
     return "Unknown"; 
    } 

    /// <summary> 
    /// Identifies whether a paragraph is a heading, based on its styling. 
    /// Note: the documents we're reviewing are always in a certain format, we can assume that headers 
    /// have a style named "Heading..." or "Kop..." 
    /// </summary> 
    /// <param name="paragraph">The paragraph to be evaluated.</param> 
    /// <returns></returns> 
    private static bool IsHeading(Word.Paragraph paragraph) 
    { 
     Word.Style style = paragraph.get_Style() as Word.Style; 
     return (style != null && style.NameLocal.StartsWith("Heading") || style.NameLocal.StartsWith("Kop")); 
    } 

    /// <summary> 
    /// Translates a paragraph into the form we want to see: preferably the chapter/paragraph number, otherwise the 
    /// title itself will do. 
    /// </summary> 
    /// <param name="paragraph">The paragraph to be translated</param> 
    /// <returns></returns> 
    private static string GetHeading(Word.Paragraph paragraph) 
    { 
     string heading = ""; 

     // Try to get the list number, otherwise just take the entire heading text 
     heading = paragraph.Range.ListFormat.ListString; 
     if (string.IsNullOrEmpty(heading)) 
     { 
      heading = paragraph.Range.Text; 
      heading = Regex.Replace(heading, "\\s+$", ""); 
     } 
     return heading; 
    } 

    /// <summary> 
    /// Determines the pagenumber of a range. 
    /// </summary> 
    /// <param name="range">The range to be located.</param> 
    /// <returns></returns> 
    private static int GetPageNumberOfRange(Word.Range range) 
    { 
     return (int)range.get_Information(Word.WdInformation.wdActiveEndPageNumber); 
    } 
} 
10

Versuchen Sie, diese für Seitennummer:

Page = c.Scope.Information(wdActiveEndPageNumber); 

Welche sollten Sie für den Endwert des Bereichs eine Seitenzahl geben. Wenn Sie die Seite Wert für den Anfang wollen, versuchen Sie dies zuerst:

Word.Range rng = c.Scope.Collapse(wdCollapseStart); 
Page = rng.Information(wdActiveEndPageNumber); 

Für Absatznummer finden Sie, was Sie von diesem erhalten kann:

c.Scope.Paragraphs; //Returns a paragraphs collection 

Meine Vermutung ist, das erste Absatzobjekt in der Sammlung die oben genannten zu nehmen dreht sich vom Ende dieses Absatzes zu Beginn des Dokuments einen neuen Bereich erhalten und den ganzzahligen Wert dieser greifen:

[range].Paragraphs.Count; //Returns int 

Dies sollte die genaue Absatznummer des Beginns des Kommentars Bereich geben.

+1

Danke für Ihre Antwort, es gab mir einen Starthilfe. Ich würde es abstimmen, aber ich habe noch nicht 15 Reputation :-) Eine Anmerkung: Ihre Code-Beispiele erforderten einige Neuschreiben in meinem C# VSTO AddIn-Projekt. Objektrichtung = WdCollapseDirection.wdCollapseStart; Bereich. Kollabieren (Ref Richtung); int pageNumber = (int) range.get_Information (WdInformation.wdActiveEndPageNumber); Die Absatznummer funktionierte nicht wirklich für mich, aber ich sehe jetzt, dass meine Frage ein wenig zu diesem Thema umformuliert werden muss. Danke jedenfalls! – Roy

1

Ich denke, es gibt einen einfacheren Weg. Sie können es von dem Objekt Range selbst erhalten. Die gibt Ihnen die Seite Nr., Zeile Nr. Usw. Informationen, außer Sie haben erfahren, wie viele Seiten oder Zeilen der Bereich überspannt. Das ist der Haken, ein Bereich muss nicht auf einer Seite sein.

So können Sie die Start- und Endpunkte einer Reihe, und dann berechnen Sie die Seite nicht, oder die Leitung nicht usw. Das tun sollten:

public static void GetStartAndEndPageNumbers(Word.Range range, out int startPageNo, 
              out int endPageNo) 
{ 
    Word.Range rngStart; 
    Word.Range rngEnd; 
    GetStartAndEndRange(range, rngStart, rngEnd); 

    startPageNo = GetPageNumber(rngStart); 
    endPageNo = rngEnd != null ? GetPageNumber(rngEnd) : startPageNo; 
} 

static void GetStartAndEndRange(Word.Range range, out Word.Range rngStart, 
           out Word.Range rngEnd) 
{ 
    object posStart = range.Start, posEnd = range.End; 

    rngStart = range.Document.Range(ref posStart, ref posStart); 

    try 
    { 
     rngEnd = range.Document.Range(ref posEnd, ref posEnd); 
    } 
    catch 
    { 
     rngEnd = null; 
    } 
} 

static int GetPageNumber(Word.Range range) 
{ 
    return (int)range.get_Information(Word.WdInformation.wdActiveEndPageNumber); 
} 

Sie können für Zeilennummern das gleiche tun zu ähnlich :

public static void GetStartAndEndLineNumbers(Word.Range range, out int startLineNo, 
               out int endLineNo) 
{ 
    Word.Range rngStart; 
    Word.Range rngEnd; 
    GetStartAndEndRange(range, rngStart, rngEnd); 

    startLineNo = GetLineNumber(rngStart); 
    endLineNo = rngEnd != null ? GetLineNumber(rngEnd) : startLineNo; 
} 

static int GetLineNumber(Word.Range range) 
{ 
    return (int)range.get_Information(Word.WdInformation.wdFirstCharacterLineNumber); 
} 
Verwandte Themen