我需要通过Microsoft.Office.Interop.Word
创建一个多级子弹列表,我目前正在努力解决其(可怕的)API(再次)。
我刚用编程语言C#在Microsoft Office Word 2010的VSTO文档级项目中创建了以下示例(不是动态的,仅用于演示目的):
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();
代码完全符合我的要求(经过大量的尝试和错误!),但在我看来这很糟糕。格式必须在非常具体的点上应用,我必须手动缩进并突出创建的段落。
所以我的问题是:通过Word.Interop
创建多级项目符号列表是否存在更好的方法,例如通过我尚未发现的简写方法?
我的目标是从XML数据创建一个多级列表(更具体的CustomXMLNode
个对象)
Stack Overflow上存在与子弹列表相关的其他两个问题,但两者都没有帮助我(上面的源代码是对第二个问题的一个答案):
编辑(2013-08-08):
我刚刚将一些东西混合在一起,输出两个数组作为一个带有两个级别的子弹列表(带有子项的数组用于每个根项目,以保持简单)。通过引入递归,可以创建具有无限级别的子弹列表(理论上)。但问题仍然存在,代码很乱......
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();
编辑(2013-08-12):
上个星期五我以为我已经实现了我想要的目标,但今天早上我发现,我的解决方案只有在插入点位于文档末尾时才有效。我创建了以下简单示例来演示(错误的)行为。要结束我的问题:我可以在文档末尾 创建多级项目符号列表。一旦我更改当前选择(例如,更改到文档的开头),列表就会被销毁。据我所知,这与Range
对象的(自动或非自动)扩展有关。到目前为止我已经尝试了很多(我几乎失去了它),但这对我来说都是货物崇拜。我唯一想做的就是一个接一个地插入一个元素(是否无法在一个段落中创建内容控件 ,以便段落的文本后跟内容控件?)<在Range
的{{1}} Document
中的强>和。我将在今天晚上用我实际的CustomXMLPart
绑定课在GitHub上创建一个Gist。最终有人可以帮我解决这个棘手的问题。
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);
}
编辑(2013-08-12):我已经设置了一个GitHub存储库here,它演示了我对Word.Range
个对象的问题。文件OnClickButton
中的Ribbon.cs
方法调用我的自定义映射器类。那里的评论描述了这个问题。我知道我的问题与参数Word.Range
对象引用有关,但我尝试的所有其他解决方案(例如修改类内部的范围)都失败了。到目前为止,我已经实现的最佳解决方案是将Document.Content
范围指定为MapToCustomControlsIn
方法的参数。这会将格式良好的多级项目符号列表(包含绑定到内容控件的自定义XML部分)插入到文档的 end 。我想要的是将自定义位置的列表插入到文档中(例如,通过Word.Selection.Range
进行当前选择)。
答案 0 :(得分:0)
弗洛里安·沃尔特斯(Florian Wolters)的例子几乎在那里,但是当我尝试使用时,第一个子项目编号始终不正确。
有人建议使用Macro和VBA脚本,然后转换为C#,这给了我启发。
下面是经过测试的示例代码在我这边工作。希望对您有所帮助。
using Microsoft.Office.Interop.Word;
using System.Reflection;
namespace OfficeUtility
{
public class NumberListGenerate
{
public void GenerateList()
{
Application app = null;
Document doc = null;
string filePath = "c:\\output.docx";
string pdfPath = "c:\\export.pdf";
try
{
app = new Application();
app.Visible = false; // Open Microsoft Office in background
doc = app.Documents.Open(filePath, Missing.Value, false);
Range range = doc.Range();
string search = "$list";
// Find in document to generate list
while (range.Find.Execute(search))
{
ListGallery listGallery =
app.ListGalleries[WdListGalleryType.wdNumberGallery];
// Select found location
range.Select();
// Apply multi level list
app.Selection.Range.ListFormat.ApplyListTemplateWithLevel(
listGallery.ListTemplates[1],
ContinuePreviousList: false,
ApplyTo: WdListApplyTo.wdListApplyToWholeList,
DefaultListBehavior: WdDefaultListBehavior.wdWord10ListBehavior);
// First level
app.Selection.TypeText("Root Item A"); // Set text to key in
app.Selection.TypeParagraph(); // Simulate typing in MS Word
// Go to 2nd level
app.Selection.Range.ListFormat.ListIndent();
app.Selection.TypeText("Child Item A.1");
app.Selection.TypeParagraph();
app.Selection.TypeText("Child Item A.2");
app.Selection.TypeParagraph();
// Back to 1st level
app.Selection.Range.ListFormat.ListOutdent();
app.Selection.TypeText("Root Item B");
app.Selection.TypeParagraph();
// Go to 2nd level
app.Selection.Range.ListFormat.ListIndent();
app.Selection.TypeText("Child Item B.1");
app.Selection.TypeParagraph();
app.Selection.TypeText("Child Item B.2");
app.Selection.TypeParagraph();
// Delete empty item generated by app.Selection.TypeParagraph();
app.Selection.TypeBackspace();
}
// Save document
doc.Save();
// Export to pdf
doc.ExportAsFixedFormat(pdfPath, WdExportFormat.wdExportFormatPDF);
}
catch (System.Exception ex)
{
LogError(ex);
}
finally
{
if (doc != null)
{
// Need to close the document to prevent deadlock
doc.Close(false);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
}
if (app != null)
{
app.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app);
}
}
}
}
}
答案 1 :(得分:0)
需要做的事情的总结。
1.) 选择范围
2.) 配置范围的 ListFormat 属性。必须设置 DefaultListBehavior 到 WdDefaultListBehavior.wdWord10ListBehavior
3.) 添加您的一级文本。
4.) 添加一个段落
5.) 添加二级文本。
6.) 将 ListLevelTier 设置为 2 级
public void GenerateMultiLevelList()
{
//A Range to write Text
Word.Range writRange = ActiveDoc.Range(0);
writRange.Text = "Tier 1 Bullet Text";
//Moving the Range to the End of Previously Entered Text
writRange = ActiveDoc.Range(writRange.End - 1);
//Formating the Range as a Bullet Point List
writRange.ListFormat.ApplyListTemplate(BulletListTemplate, true, Word.WdListApplyTo.wdListApplyToWholeList, Word.WdDefaultListBehavior.wdWord10ListBehavior);
//Adding a Paragraph
writRange.Paragraphs.Add();
writRange = ActiveDoc.Range(writRange.End - 1);
writRange.Text = "Tier 2 Bullet Text";
//Setting the List Level to 2
writRange.SetListLevel(2);
}
答案 2 :(得分:-1)
您可以尝试以下代码块:
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;
}
}
注意这一点:如果你不知道输入长度,你不能像这样定义范围结束值:
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;
}
}