无敌的Word CommandBar和它的Control们

原创 2007年10月05日 21:56:00

                Word 2007在外观上和Word 2003比,改动很大。一个叫Ribbon的控件容器取代了过去Office版本中的菜单和工具栏。在Word 2003中,我们可以使用VBA, VSTO, Office Automation等等各种各样的技术,在菜单或者工具栏上添加自定义的按钮,实现我们想要的功能。C#版本的Automation代码,大致如下:

            //Initial and show Word Application

            Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();

            wordApp.Visible = true;

 

            //Create a Command Bar

            Office.CommandBar commandBar = wordApp.CommandBars.Add("My Bar",

                Office.MsoBarPosition.msoBarTop, false, true);

            commandBar.Visible = true;

 

            //Add a Command Bar button

            Office.CommandBarButton btn = commandBar.Controls.Add(Office.MsoControlType.msoControlButton,

                missing, missing, missing, true) as Office.CommandBarButton;

            btn.Caption = "My Button";

            btn.Tag = "MyButton";

            btn.Style = MsoButtonStyle.msoButtonCaption;

            btn.Click += new _CommandBarButtonEvents_ClickEventHandler(btn_Click);

 

            void btn_Click(CommandBarButton Ctrl, ref bool CancelDefault)

            {

                MessageBox.Show("My button is clicked!");

            }

代码很简单,需要注意的地方是,buttonTag属性一定要赋值,还有添加button方法的最后一个参数设置为true,否则,当Word关闭的时候,添加的button将留在Word的工具栏上,下次打开Word会添加新的buttonbutton越来越多,这会让人想砸电脑。(可以通过删了Normal.dot把这些残留button删掉,当然还有其它方法)

以上的代码在Word 2003里跑,毫无疑问,工作正常。但是如果在只安装了Word 2007的机器上跑呢?Word 2007是没有MenuToolbar的,只有一个叫Ribbon的东西,Ribbon里面又有很多的TabTab里面再分GroupGroup里才是各种各样的控件。测试的结果是,在2007里面,以上的代码运行完全正常!看到的结果是,在Ribbon上多了一个叫Add-InsTabTab里是Custom Toolbars组,最里面就是我们添加的My Button按钮了。可见微软兼容了2007Ribbon和过去版本中的Toolbar。事实上,对于CommandBarWord对象模型中的结构,20072003没有什么大的区别,这也是上面的代码可以在两个Word版本中正常工作的原因。我们看到的Ribbon,只不过基于xml的方式,将工具栏中的控件重新组织并展现出来。如果用VSTO做过Ribbon的扩展开发,就会发现,其实Ribbon的实现,就是用一个xml文件去描述,如何将各个控件显示出来,Label是什么,图片是什么,描述是什么,以及响应事件的回调函数又是什么。这个话题比较远,我以后还会写其他的文章来介绍。

在做Office扩展开发的时候,我们很多很多很多情况下会,必须和工具栏上的按钮打交道。这样,知道每个工具栏,还有按钮的名字就很重要。微软提供了Office2007System Control ID列表供下载,这极大地方便了程序员。(下载的地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=4329D9E9-4D11-46A5-898D-23E4F331E9AE&displaylang=en ) 这些列举了所有控件IDexcel文件,极其有用,但是也有它的局限性,它是针对于Ribbon的。所以在用xml扩展Ribbon的时候,这是一个很好的参考手册。但是当我们想直接通过CommandBar来实现一些功能的时候,很多控件的名字是对不上号的。

自己动手,丰衣足食,写一个很简单,却又很有用的小程序,把Office 2007CommandBar和它里面的控件都挖出来,看看它们的真面目:

            //Create Word Application

            Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();

 

            //Create a new txt file to record controls' list

            StreamWriter sw = System.IO.File.CreateText(@"C:/Word Command Bar Control List.txt");

 

 

            //loop through wordApp.CommandBars to get all CommandBars

            foreach (Office.CommandBar cb in wordApp.CommandBars)

            {

                sw.WriteLine(cb.Name);

                //loop through each CommandBar's Controls collection to get all controls

                foreach (Office.CommandBarControl cbc in cb.Controls)

                {

                    sw.WriteLine("/t" + cbc.Caption);

                }

            }

        上面的代码在C盘生成了一个文本文件,结构式的显示出了所有的CommandBar的名字,还有他们所包含的控件的名字。一共16K,不可能贴在这儿了。得到这些CommandBarControl的名字有什么用呢?下面举两三个例子来看看,都是在论坛上被问到的问题:

1. Office2007里面,我想通过CustomTaskPane(VSTO SE提供的一个新功能,可以将WinformUserControl填充到Word内的一个板上,实现功能的扩展,该blog上将有文章介绍)上的一个按钮,来最小化WordRibbon,无论Ribbon当前的状态怎样。Word 2007的对象模型中,ActiveWindow提供了一个叫ToggleRibbon的方法,但是它只是将Ribbon的状态转换,最小化时最大化,最大化时最小化。怎么办?

自然的想法是要去判断Ribbon现在的状态是什么样的。但是找遍了整个WOM(Word对象模型),都找不到任何属性,指示了现在Ribbon的状态。绝望之中,我loop CommandBars,发现最后一个CommandBarRibbon。原来Ribbon也是CommandBars中的一员啊!把这个CommandBar的高矮胖瘦属性读出来一看,果然,最大化的时候Height属性值为147,最小化的时候值为56。哈哈,问题解决了,主要代码如下。CommandBar真好,真强大!

    In ThisAddIn.cs

private static Office.CommandBars cbs = null;

 

                private void ThisAddIn_Startup(object sender, System.EventArgs e)

                {

                        this.CustomTaskPanes.Add(new UserControl1(),"test").Visible = true;

                        cbs = this.Application.CommandBars;

                }

 

                public static Office.CommandBar returnRibbon()

                {

                    foreach (Office.CommandBar cb in cbs)

                    {

                        if (cb.Name == "Ribbon")

                        {

                            return cb;

                        }

                    }

                    return null;

                }

In CustomTaskPanes UserControl1.cs

private void button1_Click(object sender, EventArgs e)

                {

                        int i = ThisAddIn.returnRibbon().Height;

                        if (i == 147)

                        {

                                Globals.ThisAddIn.Application.ActiveWindow.ToggleRibbon();

                        }

                }

2. 客户说,他用VSTO开发了一个文档级应用的Excel文件。在这个Excel文件的工具栏上加了一个按钮,按钮的功能是调用SaveCopyAs方法,将当前的Workbook存一个备份到指定的目录下。但,如果用户将其中的一个Worksheet删除掉,然后直接调用SaveCopyAs(),新存的Workbook打开时报错,代码不被装载了。这个问题和这儿的主题关系不大,详细地解释见:http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2220299&SiteID=1 这里就不多说了。总之,就是因为SaveCopyAs前,当前的Workbook没有保存导致。

自然的想法,SaveCopyAs之前,用代码帮助用户保存一下Workbook。使用Workbook.Save()是不行的,因为它只保存Workbook的内容,不更新Workbook内嵌的manifest。于是我建议说,用SendKeys.Send(“^s”)Excel发个组合键,让Excel保存当前的文件。他回道:我的程序需要地区差别友好化,MSDN上说了,在Local Setting不一样的情况下,SendKeys.Send会有不可预测的行为。说得也是!虽然根据我们平时的经验,英文版和中文版的Office,保存热键都是Ctrl+S,应该不会存在问题。但是如果键盘不是美式键盘布局呢,也许会出错!这个方法确实有问题。想了想,还是CommandBar来帮忙。打开刚刚生成的文本文件,会看到Save按钮的真名叫&Save,位于Standard工具栏里。得到它的句柄,调用Execute()就搞定了。CommandBar真好!真强大!

this.Application.CommandBars["Standard"].Controls["&Save"].Execute();

3. 第三个例子是今天遇到的新问题。某人想在Word的第一页的页眉插入一个图片,在第一页的页脚插入一条横线。而第一页的页眉页脚,要求和后面页的页眉页脚不同。这个看似很容易办到,因为WOM中提供了一些属性让我们设置,还有添加页眉页脚。他用的代码如下:

PageSetup.DifferentFirstPageHeaderFooter = -1;

      Sections[1].Headers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterFirstPage].Shapes.AddPicture(@"C:/Image/abc.jpg", ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);

      Sections[1].Footers[Microsoft.Office.Interop.Word.WdHeaderFooterIndex.wdHeaderFooterFirstPage].Shapes.AddLine(35f, 500f, 400, 500f, ref missing);

代码看上去完全没有问题,但是很奇怪,跑起来就和预期。页眉的图片插在wdHeaderFooterFirstPage但是页脚的横线插在了wdHeaderFooterPrimary上。所以现象是:第一页看不见页脚的横线,按Ctrl+Enter多建几个空白页面,会发现后面的页面都会有横线页脚。但,如果代码是在页脚中写入Text或者插入图片,又工作正常,就是用代码不能把横线插进去!下午调试了2个多小时,也没找到为什么。嘿,最后只能替他找个workaround,绕开这个问题。

介于,不通过代码,我们可以很轻松的人工完成上述目的,人工完成无非就是点button。自然的想法,又是CommandBar和它的Control们登场的时候了。在刚刚生成的文本文件搜索Header,会发现所有这个过程中要用到的button,按照顺序去调用它们的Execute方法就可以了。CommandBar真好,真强大!

this.Application.ActiveDocument.Sections.First.PageSetup.DifferentFirstPageHeaderFooter = -1;    //Set Page to make Firstpages Header and Footer different

           

                //Edit the Header

                this.Application.CommandBars["Header Area Popup"].Controls["Edit &Header"].Execute();

                this.Application.Selection.InlineShapes.AddPicture(@"C:/test.jpg", ref missing, ref missing, ref missing);

                this.Application.CommandBars["Header and Footer"].Controls["&Close"].Execute();

 

                //Edit the Footer

                this.Application.CommandBars["Footer Area Popup"].Controls["&Edit Footer"].Execute();

                Word.InlineShape line = this.Application.Selection.InlineShapes.AddHorizontalLineStandard(ref missing);

                  this.Application.CommandBars["Header and Footer"].Controls["&Close"].Execute();

恩,说了这么多,相信CommandBar的强大性已经逐渐显示出来了。尤其是第三个例子,表现得极为明显。可以看出,几乎所有手动能干的事情,不使用WOM,都可以用CommandBar和它的Control们替我们做。虽然,通常情况下,我不建议,甚至反对这么干,因为效率很低。但是,当遇到WOM有些信息不告诉我们,如1,或者WOM出现诡异Bug的时候,如3CommandBar是不是让生活美好了很多呢?

 

【VSTO】创建 Excel 2007 AddIn (1. CommandBar 以及如何自定义Icon)

之前写过不少VBA来提高工作效率(比如:批量进行设计书格式化等等),但到了Office2007上,VBA就风光不再了,由于安全方面的考虑,VBA需要支持启动宏的Excel(扩展名:xlsm)才能使用。...

VSTO对象操作 二

四、如何定义菜单Office.CommandBarPopup cmdbar = null; Office.CommandBarButton menuInsertGraphics; privat...

pandas 增加一行数据

import pandas as pd df = pd.DataFrame(columns=('lib', 'qty1', 'qty2')) >>> for i in range(5): >>> ...
  • jiede1
  • jiede1
  • 2017年06月13日 16:01
  • 1432

C#中编程创建、调用Excel 宏

C#中编程创建、调用Excel 宏Wonsoft在面向Office应用开发中,有时需要在C#中执行 VBA 宏,甚至有时还需要在执行了 VBA 宏之后,获取返回值再进行相应的处理。文章收集了一些在 E...
  • wonsoft
  • wonsoft
  • 2008年12月02日 17:12
  • 4557

在Word中创建CommandBar并显示winform窗体

前段时间有一个需求需要在一个Word文档中选定文字后在右键菜单中添加一个按钮点击后弹出一个WinForm窗体,由于在实际做的过程中 遇到了两个问题因此记录下来,主要就是CommandBar按钮事件只...

TX Text Control文字处理教程(12)MS Word中字段的导入导出操作

Text Control 中的域可以与MS Word中的域进行相互的导入和导出操作,Text Control中的ApplicationField类为获取/设置域的数据/参数提供了相应的公共接口。下面将...

使用FPU Control Word寄存器控制计算整形与浮点型混合相加的结果以及对浮点数异常相关位Mask的处理

FPU 特殊作用寄存器中有一个控制寄存器,该寄存器的作用是控制浮点数计算结果的四舍五入以及对相关异常的静默处理 Control word寄存器含有16位,可以将其内容保存在一个WORD 大小的内存变...

把Web Control导出为Excel或Word

  • 2007年04月08日 18:17
  • 26KB
  • 下载

CommandBar

  • 2009年09月29日 18:35
  • 119KB
  • 下载

Windows Phone8.1中的CommandBar

开篇之前: 若对Windows 8.1的相关Appbar和CommandBar控件感兴趣,强烈推荐王磊老师的博客园 链接:重新想象 Windows 8.1 Store Apps (72) - 新增控件...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:无敌的Word CommandBar和它的Control们
举报原因:
原因补充:

(最多只允许输入30个字)