管理侧栏
我曾不止一次同时查看多个打开的Excel窗口,一不小心关闭其中一个致使其它的都关闭了,然而,这种情况不会出现在使用Word的时候。究其原因,乃Andrew和Robert在他们各自的文章里讲述的,Word和Excel使用了不同的窗口模型。
还记得我们是怎样把"我的博客"侧边栏添加进Word窗口的吗?我们是在ThisAddIn_Startup() 方法中使用CustomTaskPaneCollection.Add() 方法把它添加进来的:
细心的你可能已经发现,我们并没有在这里为"我的博客"侧边栏指定父窗口,VSTO将会把它添加进当前窗口。如果这用在Excel,由于各个Excel窗口共享同一个"Document Frame Window",那么每个打开的Excel窗口也将共享这个"我的博客"侧边栏。但若这用在Word,情况将大不相同,第一个启动的Word窗口将独享"我的博客"侧边栏,其它的都无福消受了。怎么解决这个问题?
一种办法就是处理Microsoft.Office.Interop.Word.Application对象的NewDocument和DocumentOpen事件,使用CustomTaskPaneCollection.Add() 方法的重载版本为每个新建或打开的窗口添加"我的博客"侧边栏。当用户关闭窗口时,我们应该销毁与之关联的侧边栏,然而,正如Robert的文章所指出的,Word没有提供DocumentClosed事件,如果我们在DocumentBeforeClose事件的Event Handler里销毁侧边栏,那么当用户取消关闭窗口时,就会发现侧边栏已被莫名其妙地关闭了,这显然是不可接受的。对于这个问题,Robert的方法确实是一个不错的选择。值得提醒的是,如果添加侧边栏的代码只存在于NewDocument和DocumentOpen事件的Event Handler中,那么第一个新建或者打开的窗口都不会有侧边栏,因为这两个事件是在第一个窗口之后才触发的,于是,你需要把代码复制一份放在ThisAddIn_Startup() 方法里面。
增值服务区
Managing Custom Task Panes in Multiple Application Windows
另一种办法就是,当用户点击My Blogs按钮时判断当前窗口是否有与之关联的侧边栏,有则显示,无则创建并显示。而侧边栏的销毁则与上一种办法相同。
接下来,我将采用第二种办法来管理"我的博客"侧边栏,稍稍不同的是,我会把侧边栏的创建/获取和销毁等工作外包给MyBlogsPaneManager类而不是直接放在相关的Event Handler里。
// Code #02
MyBlogsPaneManager#region MyBlogsPaneManager
public class MyBlogsPaneManager
{
private MyBlogsPaneManager()
{
m_MyBlogsPanes = Globals.ThisAddIn.CustomTaskPanes;
}
private static MyBlogsPaneManager m_Instance = new MyBlogsPaneManager();
public static MyBlogsPaneManager Instance
{
get { return m_Instance; }
}
private Vsto.CustomTaskPaneCollection m_MyBlogsPanes;
public Vsto.CustomTaskPane GetMyBlogsPane()
{
Word.Window window = Globals.ThisAddIn.Application.ActiveWindow;
foreach (Vsto.CustomTaskPane ctp in m_MyBlogsPanes)
{
if (ctp.Title == "My Blogs" && ctp.Window == window)
{
return ctp;
}
}
return m_MyBlogsPanes.Add(
new MyBlogsUserControl(),
"My Blogs",
window
);
}
public void CollectMyBlogsPanes()
{
for (int i = m_MyBlogsPanes.Count - 1; i >= 0; i--)
{
if (m_MyBlogsPanes[i].Window == null)
{
m_MyBlogsPanes.RemoveAt(i);
}
}
}
}
#endregion
对于Code #02,以下几点是需要说明的:
GetMyBlogsPane() 方法会判断是否存在标题为"My Blogs"、父窗口为当前窗口的侧边栏,有则返回,无则创建。不难看出,该方法总是返回与当前窗口关联的侧边栏。
CollectMyBlogsPanes() 方法负责回收废弃的侧边栏。
考验脑力区
CollectMyBlogsPanes() 方法里的递减for循环可以改为递增for循环或者foreach吗?
之前我们在ThisAddIn类里面处理侧边栏的添加和相关事件,现在需要把这些代码迁移到BloggingRibbon类里。
首先,把MyBlogsPaneVisibleChanged() 从ThisAddIn类移到BloggingRibbon类,并做适当调整:
接着,修改MyBlogs按钮的Click事件的Event Handler:
然后,在ThisAddIn类里面处理Microsoft.Office.Interop.Word.Application.DocumentChange事件:
以及在InternalStartup() 方法里添加如下这行代码:
this.Application.DocumentChange += new Word.ApplicationEvents4_DocumentChangeEventHandler(DocumentChange);
最后,就是删除废弃/多余的代码了。然而,现在的插件处于一个中间状态,它不完整,如果运行的话将会出现一些古怪的行为,接下来将会分析并解决这个问题。
同步状态
细心观察Figure 1,你会发现,两个窗口的My Blogs按钮都处于按下状态,起初我以为这是因为它们都关联到同一个侧边栏,而此时这个侧边栏又处于显示状态,因此它们同时处于按下状态就不足为奇了。然而,事情却不是这么简单,我创建一个单独的插件项目观察多个窗口中按钮的状态,发现如下两个事实:
这些按钮的按下/释放状态是共享的。当我按下其中一个窗口的My Blogs按钮并激活其它窗口时,被激活窗口的My Blogs按钮也会变成按下状态。
这些按钮的状态同步并非立即发生的。当我按下其中一个窗口的My Blogs按钮但保持当前窗口的活动状态,其它窗口的My Blogs按钮仍然保持释放状态。
不难看出,我们可以在Microsoft.Office.Interop.Word.Application.WindowActivate事件的Event Handler里做一些手脚:
当然,你必须在InternalStartup() 方法里面把它关联该事件:
this.Application.WindowActivate += new Word.ApplicationEvents4_WindowActivateEventHandler(WindowActivate);
现在,我们来看看运行效果:
图 1
图 2
如果你来回切换Figure 2的两个窗口,你会发现My Blogs按钮在每次切换时都会"闪动",这种"闪动"其实是使用WindowActivate事件修正My Blogs按钮状态的一个小小的副作用。细心的读者可能会发现,Word自带的Research侧边栏在同样的情况下也会"闪动"。
至此,侧栏问题的探讨要告一段落了。此时,有人可能会问,Word 2007在兼容MetaWeblog API上有问题,致使我们无法使用它在博客园发布带图片的文章,那么为什么我还要花费这么多精力来对它进行扩展呢?正如很多人所知道的,这个问题是由Word 2007的一个bug所致的,坊间也流传一些有效的解决办法,这恰恰说明了大家不愿放弃Word这个强大的编辑工具。下一回,我将会从Word 2007的扩展特性入手,探讨另一种可能的解决办法。
相关文章
同类最新