c# ms-word vsto ms-office

c# - Crear una lista de viñetas de varios niveles con Word.Interop



ms-word vsto (1)

Necesito crear una lista de viñetas de varios niveles a través de Microsoft.Office.Interop.Word y actualmente estoy luchando con su (horrible) API (nuevamente).

Acabo de crear el siguiente ejemplo (no es dinámico, solo para fines de demostración) en un proyecto de nivel de documento VSTO para Microsoft Office Word 2010 en el lenguaje de programación C #:

Word.Paragraph paragraph = null; Word.Range range = this.Content; paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1"; paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior); // ATTENTION: We have to outdent the paragraph AFTER its list format has been set, otherwise this has no effect. // Without this, the the indent of "Item 2" differs from the indent of "Item 1". paragraph.Outdent(); paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1.1"; // ATTENTION: We have to indent the paragraph AFTER its text has been set, otherwise this has no effect. paragraph.Indent(); paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 1.2"; paragraph.Range.InsertParagraphAfter(); paragraph = range.Paragraphs.Add(); paragraph.Range.Text = "Item 2"; paragraph.Outdent();

El código hace exactamente lo que quiero (¡después de muchas pruebas y errores!), Pero es horrible en mi opinión. El formato se debe aplicar en un punto MUY específico y tengo que sangrar y eliminar los párrafos creados manualmente.

Entonces mi pregunta es: ¿existe un mejor enfoque para crear una lista de viñetas de varios niveles a través de Word.Interop , por ejemplo, a través de métodos abreviados que aún no he descubierto?

Mi objetivo es crear una lista de varios niveles a partir de datos XML (más específico es un objeto CustomXMLNode )

Existen otras dos preguntas relacionadas con las listas de viñetas en Stack Overflow, pero ambas no me ayudan (el código fuente anterior es una respuesta a la segunda pregunta):

EDITAR (08/08/2013):

Acabo de hackear algo que arroja dos matrices como una lista de viñetas con dos niveles (la matriz con los subtemas se usa para cada elemento raíz, para mantenerlo simple). Al introducir la recursión, uno podría crear una lista de viñetas con niveles infinitos (teóricamente). Pero el problema persiste, el código es un desastre ...

string[] rootItems = new string[] { "Root Item A", "Root Item B", "Root Item C" }; string[] subItems = new string[] { "Subitem A", "Subitem B" }; Word.Paragraph paragraph = null; Word.Range range = this.Content; bool appliedListFormat = false; bool indented = false; for (int i = 0; i < rootItems.Length; ++i) { paragraph = range.Paragraphs.Add(); paragraph.Range.Text = rootItems[i]; if (!appliedListFormat) { paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior); appliedListFormat = true; } paragraph.Outdent(); paragraph.Range.InsertParagraphAfter(); for (int j = 0; j < subItems.Length; ++j) { paragraph = range.Paragraphs.Add(); paragraph.Range.Text = subItems[j]; if (!indented) { paragraph.Indent(); indented = true; } paragraph.Range.InsertParagraphAfter(); } indented = false; } // Delete the last paragraph, since otherwise the list ends with an empty sub-item. paragraph.Range.Delete();

EDITAR (2013-08-12):

El viernes pasado pensé que había logrado lo que quería, pero esta mañana noté que mi solución solo funciona si el punto de inserción está al final del documento. Creé el siguiente ejemplo simple para demostrar el comportamiento (erróneo). Para concluir mi problema: puedo crear listas de viñetas de varios niveles al final del documento solamente . Tan pronto como cambio la selección actual (por ejemplo, al comienzo del documento), la lista se destruye. Por lo que puedo ver, esto está relacionado con la extensión (automática o no automática) de los objetos Range . Lo he intentado mucho hasta ahora (casi lo estoy perdiendo), pero para mí es todo un culto a la carga. Lo único que quiero hacer es insertar un elemento después de otro (¿es imposible crear un control de contenido dentro de un párrafo para que el texto del párrafo sea seguido por el control de contenido?) Y para eso en cualquier Range de un Document . CustomXMLPart un Gist en GitHub con mi clase de enlace CustomXMLPart real esta noche. Eventualmente, alguien puede ayudarme a solucionar ese molesto problema.

private void buttonTestStatic_Click(object sender, RibbonControlEventArgs e) { Word.Range range = Globals.ThisDocument.Application.Selection.Range; Word.ListGallery listGallery = Globals.ThisDocument.Application.ListGalleries[Word.WdListGalleryType.wdBulletGallery]; Word.Paragraph paragraph = null; Word.ListFormat listFormat = null; // TODO At the end of the document, the ranges are automatically expanded and inbetween not? paragraph = range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Root Item A"; this.ApplyListTemplate(listGallery, listFormat, 1); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Child Item A.1"; this.ApplyListTemplate(listGallery, listFormat, 2); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Child Item A.2"; this.ApplyListTemplate(listGallery, listFormat, 2); paragraph.Range.InsertParagraphAfter(); paragraph = paragraph.Range.Paragraphs.Add(); listFormat = paragraph.Range.ListFormat; paragraph.Range.Text = "Root Item B"; this.ApplyListTemplate(listGallery, listFormat, 1); paragraph.Range.InsertParagraphAfter(); } private void ApplyListTemplate(Word.ListGallery listGallery, Word.ListFormat listFormat, int level = 1) { listFormat.ApplyListTemplateWithLevel( listGallery.ListTemplates[level], ContinuePreviousList: true, ApplyTo: Word.WdListApplyTo.wdListApplyToSelection, DefaultListBehavior: Word.WdDefaultListBehavior.wdWord10ListBehavior, ApplyLevel: level); }

EDITAR (2013-08-12): He creado un repositorio GitHub aquí que demuestra mi problema con los objetos Word.Range . El método OnClickButton en el archivo Ribbon.cs invoca mi clase de asignador personalizado. Los comentarios allí describen el problema. que mis problemas están relacionados con el argumento Word.Range referencia de objeto, pero todas las demás soluciones que probé (por ejemplo, modificar el rango dentro de la clase) fallaron aún más. La mejor solución que he logrado hasta ahora es especificar el rango Document.Content como argumento para el método MapToCustomControlsIn . Esto inserta una lista de viñetas de varios niveles con buen formato (con partes XML personalizadas vinculadas a controles de contenido) al final del documento. Lo que quiero es insertar esa lista en una posición personalizada en el documento (por ejemplo, la selección actual a través de Word.Selection.Range ).


Puedes probar a continuación el bloque de código:

static void Main(string[] args) { try { Application app = new Application(); Document doc = app.Documents.Add(); Range range = doc.Range(0, 0); range.ListFormat.ApplyNumberDefault(); range.Text = "Birinci"; range.InsertParagraphAfter(); ListTemplate listTemplate = range.ListFormat.ListTemplate; //range.InsertAfter("Birinci"); //range.InsertParagraphAfter(); //range.InsertAfter("İkinci"); //range.InsertParagraphAfter(); //range.InsertAfter("Üçüncü"); //range.InsertParagraphAfter(); Range subRange = doc.Range(range.StoryLength - 1); subRange.ListFormat.ApplyBulletDefault(); subRange.ListFormat.ListIndent(); subRange.Text = "Alt Birinci"; subRange.InsertParagraphAfter(); ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate; Range subRange2 = doc.Range(subRange.StoryLength - 1); subRange2.ListFormat.ApplyListTemplate(sublistTemplate); subRange2.ListFormat.ListIndent(); subRange2.Text = "Alt İkinci"; subRange2.InsertParagraphAfter(); Range range2 = doc.Range(range.StoryLength - 1); range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true); WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate); range2.Text = "İkinci"; range2.InsertParagraphAfter(); Range range3 = doc.Range(range2.StoryLength - 1); range3.ListFormat.ApplyListTemplate(listTemplate); range3.Text = "Üçüncü"; range3.InsertParagraphAfter(); string path = Environment.CurrentDirectory; int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count(); path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1)); app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument); doc.Close(); Process.Start(path); } catch (Exception exception) { throw; } }

Atención a este punto: si no conoce la longitud de entrada, no debe definir el valor de fin de rango de la siguiente manera:

static void Main(string[] args) { try { Application app = new Application(); Document doc = app.Documents.Add(); Range range = doc.Range(0, 0); range.ListFormat.ApplyNumberDefault(); range.Text = "Birinci"; range.InsertParagraphAfter(); ListTemplate listTemplate = range.ListFormat.ListTemplate; //range.InsertAfter("Birinci"); //range.InsertParagraphAfter(); //range.InsertAfter("İkinci"); //range.InsertParagraphAfter(); //range.InsertAfter("Üçüncü"); //range.InsertParagraphAfter(); Range subRange = doc.Range(range.StoryLength - 1, range.StoryLength - 1); subRange.ListFormat.ApplyBulletDefault(); subRange.ListFormat.ListIndent(); subRange.Text = "Alt Birinci"; subRange.InsertParagraphAfter(); ListTemplate sublistTemplate = subRange.ListFormat.ListTemplate; Range subRange2 = doc.Range(subRange.StoryLength - 1, range.StoryLength - 1); subRange2.ListFormat.ApplyListTemplate(sublistTemplate); subRange2.ListFormat.ListIndent(); subRange2.Text = "Alt İkinci"; subRange2.InsertParagraphAfter(); Range range2 = doc.Range(range.StoryLength - 1, range.StoryLength - 1); range2.ListFormat.ApplyListTemplateWithLevel(listTemplate,true); WdContinue isContinue = range2.ListFormat.CanContinuePreviousList(listTemplate); range2.Text = "İkinci"; range2.InsertParagraphAfter(); Range range3 = doc.Range(range2.StoryLength - 1, range.StoryLength - 1); range3.ListFormat.ApplyListTemplate(listTemplate); range3.Text = "Üçüncü"; range3.InsertParagraphAfter(); string path = Environment.CurrentDirectory; int totalExistDocx = Directory.GetFiles(path, "test*.docx").Count(); path = Path.Combine(path, string.Format("test{0}.docx", totalExistDocx + 1)); app.ActiveDocument.SaveAs2(path, WdSaveFormat.wdFormatXMLDocument); doc.Close(); Process.Start(path); } catch (Exception exception) { throw; } }