使用AvalonDock制作WPF多标签浏览器(二)
闲话少叙,书接上文。
现在我们已经通过ReStyle给DocumentPane加上了一个加号的按钮,并且可以通过点击该按钮给DocumentPane的Items添加一个DocumentContent了。
不过每个新添加进来的DocumentContent内部都是空的,而我们需要的是每个新标签中都有一个WebBrowser,要实现这一点很简单,只要给DocumentContent的Content(DocumentContent是ContentControl的子类)属性赋值为一个WebBrowser的实例就OK了。
但是这不应该是AvalonDock的默认行为,所以我们要把这部分写到客户端--也就是引用AvalonDock.dll文件或者直接引用AvalonDock工程的Solution中去。
如何可以在客户端得知有一个新的DocumentContent被添加进DocumentPane中去了呢?自然是用事件了。
首先来写一个自定义的EventArgs吧:
public class NewContentAddedEventArgs : EventArgs
{
public NewContentAddedEventArgs(DocumentContent addedContent)
{
AddedContent = addedContent;
}
public DocumentContent AddedContent
{ get; private set; }
}
这个名为NewContentAddedEventArgs的事件参数的构造方法会要求一个DocumentContent,其实也就是被添加的那个新标签了。
这样订阅其对应事件的客户代码就可以得到一个指向新添加的标签的引用,当然就可以将其Content设置为一个新的WebBrowser的实例了。
接下来在DocumentPane中定义一个使用刚写好的事件参数的事件:
public event EventHandler<NewContentAddedEventArgs> NewContentAdded;
接下来,遵照规范写一个OnNewContentAdded方法,以免在没人订阅事件的时候试图触发事件而抛异常:
private void OnNewContentAdded(NewContentAddedEventArgs args)
{
if (NewContentAdded != null)
{
NewContentAdded(this, args);
}
}
再然后在我们之前写的AddNew方法中调用OnNewContentAdded方法就OK了,修改后的AddNew方法是这样的:
private void AddNew()
{
DocumentContent newContent = new DocumentContent();
newContent.Title = "new content";
newContent.IsFloatingAllowed = true;
Items.Add(newContent);
OnNewContentAdded(new NewContentAddedEventArgs(newContent));
}
然后就可以开始着手写客户代码了。
首先在XAML中添加对AvalonDock的引用:
xmlns:Avalon="clr-namespace:AvalonDock;assembly=AvalonDock"
然后再在主体中添加如下代码:
<Avalon:DockingManager>
<Avalon:DocumentPane Name="mainPane" NewContentAdded="DocumentPane_NewContentAdded">
<Avalon:DocumentContent Title="Default Tab" GotFocus="DocumentContent_GotFocus" IsCloseable="False" IsFloatingAllowed="True">
<WebBrowser Name="defaultBrowser" Source="http://www.google.cn/webhp?hl=zh-CN">
</WebBrowser>
</Avalon:DocumentContent>
</Avalon:DocumentPane>
</Avalon:DockingManager>
其中的DockingManager是AvalonDock中的“总管”,其详细使用方法请看:http://avalondock.codeplex.com/
上面的代码中可以看到,我们给DocumentPane新加的NewContentAdded事件已经挂到了一个叫做DocumentPane_NewContentAdded的方法上。该方法的定义很简单:
private void DocumentPane_NewContentAdded(object sender, NewContentAddedEventArgs e)
{
DocumentContent newContent = e.AddedContent;
newContent.Title = "New Tab";
newContent.GotFocus += DocumentContent_GotFocus;
newContent.Content = new WebBrowser { Source = new Uri(@"http://www.google.cn") };
}
就是把一个打开Google首页的WebBrowser实例赋值给新添加的标签页的Content属性。
里面还有一句:
newContent.GotFocus += DocumentContent_GotFocus;
其中的DocumentContent_GotFocus定义如下:
private void DocumentContent_GotFocus(object sender, RoutedEventArgs e)
{
DocumentContent selectedOne = s as DocumentContent;
if (selectedOne != null)
{
WebBrowser browser = selectedOne.Content as WebBrowser;
if (browser != null && browser.Source != null)
{
currentBrowser = browser;
url.Text = browser.Source.ToString();
}
}
}
这是做什么用的呢?
我们用浏览器打开多个标签页的时候程序中就会存在多个WebBrowser的实例,这时如果在地址栏中输入一个地址并回车的话,怎么知道到底应该把哪个WebBrowser重定向到输入的地址呢?
所以程序中应该有一个WebBrowser的引用,假设叫做currentBrowser吧,它始终指向当前选中标签中的WebBrowser实例。
上面的代码做的就是这个工作,当一个标签页得到焦点的时候,就让currentBrowser 指向该标签页中的WebBrowser实例,并把一个叫做url的TextBox(也就是地址栏了)的Text属性设置为当前Browser打开的地址。
当然,这个叫做url的地址栏的KeyDown事件的处理方法中应该把currentBrowser 重定向到输入的网址,这段代码很简单,就不贴了。
现在运行一下,似乎OK了,真的完事儿了吗?
呵呵,没有。
试一下打开两个标签,把其中的第二个拖拽到主区域的右侧去,从而将主区域一分为二:
在右侧新分化出来的区域中点击加号小按钮新建另一个标签,切换到新标签。
啊哦~~~~~
新标签是空的,不是明明已经在每个新标签被添加时给其中加上一个WebBrowser了吗?咋没有捏?这究竟是哪儿来的Bug呢?
再注意观察一下,这个新标签的标题是new content而不是New Tab,呵呵,是不是已经猜到了呢?
其实这是和AvalonDock中的另一个类--DocumentFloatingWindow有关的问题。
怎么解决这个问题明天再说吧。
Over and out!