he phenomenal popularity of the new Firefox browser from Mozilla.org is no random happenstance. Web users around the world have discovered what dozens of reviewers pointed out back in its beta: Firefox’s tabbed UI, where multiple browser pages are consolidated into one application instance using tabs, is a great way to surf. Not only is it more convenient to change between browser instances but it saves tons of room on the Windows toolbar.
Microsoft hasn’t (yet) updated IE to copy this great functionality but you can beat Redmond to the punch using new features of Windows Forms 2.0. As part of the .NET Framework 2.0 and Visual Studio 2005 (Whidbey) release, WinForms 2.0 is currently still in beta but the new controls and features demonstrated in this article are ready for you to put them to work in an IE transformation.
In this article, you’ll get acquainted with WinForms 2.0’s WebBrowser control, which will be the magic behind this tabbed browsing feature.
|Figure 1. Movable: Toolbars and menu bars in EnhancedIE can be moved and customized.
As I am going to build a relatively feature-complete Web browser, I shall walk through the features set before I explain how to build the application, which I’ve named EnhancedIE.
MenuStrip and ToolbarStrips
EnhancedIE has movable toolbars, just like the normal IE. In my application, you can move the menubar, toolbars, and address bar (see Figure 1).
EnhancedIE contains an address bar where you can enter the URL of the Web site you wish to visit (see Figure 2). This address bar supports the Autocomplete feature, which completes previously visited URLs as you type. You can also click on the drop-down arrow in the address bar to see a list of URLs that you have typed previously (see Figure 3).
Figure 2. Autocomplete: EnhancedIE finishes familiar URLs for you.
Figure 3. Good Memory: The application displays a list of previously typed URLs.
|Figure 4. Viewing Live Bookmarks: The content shown is dynamically fed from three RSS feeds. Users of EnhancedIE can choose any RSS feed they like.
EnhancedIE will support a new feature known as Live Bookmark (another feature available in Firefox). Using a live bookmark, you can reference an RSS news feed and display its content title just like a normal bookmark. Because it dynamically reads from RSS, content in live bookmarks is always up-to-date.
Figure 4 shows three live bookmarks in EnhancedIE:
- CNET Reviews?Most Recent Reviews
- DevX: Latest Published Articles
- MSDN Just Published
To read a particular piece of news, simply select the relevant menu item and it will display in the current tab page. To display all the news items in multiple tab pages, select “Open in tabs.” If you click on the tabs to move between pages:
- The current URL of the page is displayed in the address bar
- The current URL of the page is also displayed in the status bar
To close a tab page, right-click on the tab you want to close and select “Close Tab.”
|Figure 5. Subscribing to a News Feed: To add a live bookmark you just need the URLs of your favorite RSS feeds.
To subscribe to live bookmarks select “Add Live Bookmark …” from the Favorites menu (see Figure 5). You’ll be prompted to enter a news feed URL.
Live bookmarks are stored in the “My Documents” folder on the user’s computer. Each live bookmark is saved as a plain text file with a “.live” extension. The content of each file simply contains the URL of the feed.
Writing the Code
|Figure 6. Action: You have a lot of choices for customizing your MenuStrip control using the Actions tag.
Now that you have a good idea of how the EnhancedIE application works, let’s get to work. First, create a new Windows application using Visual Studio 2005 (beta 1 was used for this article) and name the project EnhancedIE. In the default Form1, change the Text property of the form to “Enhanced Internet Explorer.”
In the Toolbox, double-click on the MenuStrip control to add a menu bar to the form. If you click on the Actions arrow on the right of the control, you will see a list of commonly used properties and shortcuts (see Figure 6).
Set the following properties for the MenuStrip control:
|Figure 7. Menu: The completed menu bar is shown.
Click on the “Insert Standard Items” link in the Actions menu to insert a list of commonly available menu items. To add your own item to the menu, click on the Edit Items… link. I added the View and Favorites menus (see Figure 7).Note in Figure 7 that the Add Live Bookmark…menu item has a star icon next to it. You can add an icon to a menu item by clicking on the blue column next to the menu item. You will then be asked to supply a resource (image) to use for that menu item.
Creating the ToolStrips
The next step is to create the toolbar. While a menu is used to group related options in an organized format, a toolbar provides shortcuts to commonly used tasks. In the Toolbox, double-click on the ToolStrip control. Like with the MenuStrip control, you can insert a list of commonly available toolbar items on the ToolStrip (see Figure 8).
|Figure 8. Adding Tools: As in Figure 6, adding items to a ToolStrip control is easy using the Actions tag.
For simplicity, I will just show some standard Office icons on the ToolStrip, rather than an IE-specific toolbar. But this shows how easily you can offer commonly used items to your ToolStrip. You can add more using the “Edit Items…” link.
I’d like to add a second ToolStrip control to the form. Using separate ToolStrips lets you group tasks so that related items can be moved together. This second ToolStrip will house the address textbox as well as the Go button. Add the following controls to the ToolStrip (use the “Edit Items…” link on the Actions tag):
Set the “Stretch” property of the second ToolStrip control to true. This will cause it to fill up the entire width of the window. Set the Alignment property of the Go button to “Tail” (so that it will be right-justified). The form now looks like Figure 9.
|Figure 9. Two ToolStrips: The completed address bar is shown underneath the first “standard” ToolStrip.
|Figure 10. Three ToolStrips: The live bookmarks ToolStrip completes the control bar for the browser.
The address bar is a ComboBox control named cbbURL. Set its properties as follows:
- AutoCompleteMode to SuggestAppend
- AutoCompleteSource to HistoryList
This will cause the ComboBox control to automatically fill in the textbox as you type (with information stored in IE’s history list).
Add a third ToolStrip control to the form and to it add a ToolStripLabel control. This third ToolStrip will display the live bookmarks. Set the text of the ToolStripLabel control to “Live Bookmarks,” and again set the “Stretch” property of the ToolStrip to “true” (see Figure 10).
Creating the StatusStrip
Once the ToolStrip controls are added, the next step is to add the StatusStrip control to the bottom of the form. The StatusStrip control appears at the bottom of the window and is commonly used to display information pertaining to the current operation. In the Toolbox, double-click on the StatusStrip control and add a StatusStripPanel to the control. The StatusStripPanel represents a panel in a StatusStrip control. You display status information within a StatusStripPanel control.
Adding the TabControl
Now we can get down to the fun part. A TabControl will handle the tab switching functionality that is the hallmark of this browser. In the Toolbox, double-click on the TabControl control. In the Actions page, click on the “Dock in parent container” link to fill up the form with the control (see Figure 11).
Figure 11. Adding Tabs: Here is the new TabControl as it looks when newly added to the form.
Figure 12. Close Tab: To allow users to close a tab, use a ContextMenuStrip.
By default, there are two TabPage controls in the control, but this application will create its own tabs dynamically during runtime, therefore you don’t need them at design time. Click on the “Remove Tab” link twice to remove them.
Adding the ContextMenuStrip
Users will need a control that allows them to close a tab page when they right-click on one. We’ll add this functionality with a ContextMenuStrip control. In the Toolbox, double-click on the ContextMenuStrip control, and add a ToolStripMenuItem to it. Set the text property of the ToolStripMenuItem to “Close Tab.” The form now looks like Figure 12. Set the ContextMenuStrip property of the TabControl to “ContextMenuStrip1.” This step is required so that when the user right-clicks on a tab control, the context menu is displayed. It is used to associate a context menu with a control.
Coding the Application
Now that I have all of the controls in place on the form I’m ready to start coding the business logic. First, I’ll import the following namespaces:
Imports System.xmlImports System.Net
These two namespaces contain classes to retrieve XML documents over the Web. In particular, I will use them when downloading content of live bookmarks (RSS feeds). I will also make use of XPath expressions to extract the relevant sections of the document to display. Declare a global variable?a string array to store the list of URLs that the user has typed:
Dim URLsTyped() As String
Define the ResizeAddressBar() method so that the address ComboBox can be stretched when the form resizes:
Private Sub ResizeAddressBar() '===Resize the Address combobox when the form is ' resized Dim size As Size size.Height = cbbURL.Size.Height size.Width = Me.Size.Width - btnGo.Size.Width _ - lblAddress.Size.Width - 25 cbbURL.Size = size End Sub
The Form1_Resize event will be fired whenever the form resizes:
Private Sub Form1_Resize(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles Me.Resize '===When the form resizes=== ResizeAddressBar() End Sub
|Author’s Note: Due to a bug in beta 1, the address bar is hidden when the form is first loaded. Maximizing the form will reveal the address bar.
I will define two methods: DisplayNewTab() and DisplayinCurrentTab(). The DisplayNewTab() method first dynamically creates a new instance of the WebBrowser control and then creates a new tab page within the TabControl control.
The WebBrowser control in WinForms 2.0 is a managed wrapper for the WebBrowser ActiveX control. And thus it is now much easier to use the WebBrowser control than it was in .NET 1.1.
Add the WebBrowser control to the new tab page. Note that when the Web page is being loaded, I set the title of the tab page to “Loading…” (because the title of the document is not yet available at this stage). When the WebBrowser control has finished loading the document, the DocumentTitleChanged event will fire. This is serviced by the WebBrowser_DocumentTitleChanged() method (I will discuss this later). Also note that I have made use of the ToolTipText property of the tab page to store the URL for the page. This is important because you need to be able to display the URL of the current page in the address bar when a tab is selected.
Private Sub DisplayNewTab(ByVal URL As String) '===Display a page in a new tab page=== '---create a new WebBrowser control instance Dim wb As New WebBrowser wb.Dock = System.Windows.Forms.DockStyle.Fill wb.Location = New System.Drawing.Point(3, 3) wb.Navigate(URL) '---update the tab title when the document title ' has finished loading AddHandler wb.DocumentTitleChanged, AddressOf _ WebBrowser_DocumentTitleChanged '---create a new tab page and then add to the tab ' control Dim tp As New TabPage tp.Text = "Loading..." tp.ToolTipText = URL tp.Controls.Add(wb) TabControl1.TabPages.Add(tp) '---always display the latest tab TabControl1.SelectTab(TabControl1.TabCount - 1) End Sub
The DisplayinCurrentTab() method displays a Web page in the current selected page. Note that I need to first check if there is a current a tab page selected; if not, I will need to call the DisplayNewTab() method.
Private Sub DisplayinCurrentTab(ByVal URL As String) '===Display a page in the current tab page=== If TabControl1.SelectedTab Is Nothing Then DisplayNewTab(URL) Exit Sub End If Dim wb As WebBrowser = _ TabControl1.SelectedTab.Controls(0) TabControl1.SelectedTab.Text = "Loading..." TabControl1.SelectedTab.ToolTipText = URL wb.Navigate(URL) '---update the tab title when the document title ' has finished loading AddHandler wb.DocumentTitleChanged, AddressOf _ WebBrowser_DocumentTitleChanged End Sub
As mentioned previously, the WebBrowser_DocumentTitleChanged() method will handle the DocumentTitleChanged event. One limitation that I faced when using this event is that it passes in a reference from the WebBrowser control that fires the event?not the tab page. Hence I needed to write code to cycle through all the tab pages to look for the container (i.e. tab page) of this WebBrowser control. Once the particular tab page is located, I will update its page title as well as the address bar and status bar.
Private Sub WebBrowser_DocumentTitleChanged( _ ByVal sender As Object, _ ByVal e As System.EventArgs) '===Update the title of a tab page when the page ' has finished loading=== '--locate which tab page contains the webbrowser ' control Dim wb As WebBrowser = CType(sender, WebBrowser) Dim i As Integer = 0 While Not TabControl1.TabPages(i).Contains(wb) i += 1 End While '---Set the tab page title TabControl1.TabPages(i).Text = wb.DocumentTitle TabControl1.TabPages(i).ToolTipText = wb.Url '---Display the URL in the Address combobox and ' Statusbar cbbURL.Text = wb.Url StatusStripPanel1.Text = wb.Url End Sub
There is another TabControl event that I need to service and that is the SelectedIndexChanged event. This event is fired whenever a tab page is selected. What I want to do in this event is to display the URL of the currently selected tab page in the address bar and the status bar:
Private Sub TabControl1_SelectedIndexChanged( _ ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles TabControl1.SelectedIndexChanged '===Update the URL of the selected tab page in ' the Address combobox and Statusbar Dim tp As TabControl = CType(sender, TabControl) If tp.SelectedTab IsNot Nothing Then cbbURL.Text = tp.SelectedTab.ToolTipText StatusStripPanel1.Text = _ tp.SelectedTab.ToolTipText End If End Sub
When the user types in a URL and clicks the Go button, I want to display the Web page in a new tab page and then save the typed URL into the global variable string array:
Private Sub btnGo_Click(ByVal sender As _ System.Object, _ ByVal e As System.EventArgs) _ Handles btnGo.Click '===GO button=== '---display page in a new tab DisplayNewTab(cbbURL.Text) '---save the typed URL into a string array If URLsTyped Is Nothing Then ReDim URLsTyped(0) Else ReDim Preserve URLsTyped(URLsTyped.Length) End If URLsTyped(URLsTyped.Length - 1) = cbbURL.Text End Sub
Instead of clicking the Go button to load a page, it is much easier for the user to type the URL and then press the Enter key. This can be trapped using the KeyPress event of the ComboBox control:
Private Sub cbbURL_KeyPress(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyPressEventArgs) _ Handles cbbURL.KeyPress '===When the enter types in the Address ' combobox=== '---bug: this event did not fire when the Enter ' key is pressed If e.KeyChar = Microsoft.VisualBasic.ChrW(13) _ Then e.Handled = True '---display page in a new tab DisplayNewTab(cbbURL.Text) '---save the typed URL into a string array If URLsTyped Is Nothing Then ReDim URLsTyped(0) Else ReDim Preserve _ URLsTyped(URLsTyped.Length) End If URLsTyped(URLsTyped.Length - 1) = cbbURL.Text End If End Sub
|Author’s Note: The KeyPress event did not fire correctly when the Enter key was pressed in Beta 1. This should be fixed with Beta 2, which isn’t far off.
The user can also view a list of URLs that he has typed previously by clicking on the dropdown arrow of the address ComboBox control:
Private Sub cbbURL_DropDown(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles cbbURL.DropDown '===Display in the Address combobox the list=== ' of last few web sites URL typed=== If URLsTyped IsNot Nothing Then cbbURL.Items.Clear() cbbURL.Items.AddRange(URLsTyped) End If End Sub
|Author’s Note: For simplicity I did not persist the list of URLs. Using this code, the list will contain only URLs typed by the user in the current run of the application.
When the user selects the menu Favorites -> Add Live Bookmark…, the application prompts the user to supply the feed URL and then calls the LoadLiveFavorite() method. This method will load the RSS feed from the supplied URL and then return the title of the feed. The feed title is used to create a file in the “My documents” directory where the URL is written to the content. The file will have a .live extension.
Private Sub AddlivefavoriteToolStripMenuItem_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles _ AddlivefavoriteToolStripMenuItem.Click '===Add a live favorite to the browser=== Dim RSSfeedURL As String = _ Microsoft.VisualBasic.InputBox( _ "Enter feed", , , , ) Dim RSSTitle As String = _ LoadLiveFavorite(RSSfeedURL) '---save live favorite to file Try Dim filePath As String filePath = System.IO.Path.Combine( _ My.Computer.FileSystem.SpecialDirectories._ MyDocuments, _ RSSTitle.Replace(":", "") & ".live") My.Computer.FileSystem.WriteAllText( _ filePath, RSSfeedURL, True) Catch fileException As Exception Throw fileException End Try End Sub
|Figure 13. Bookmarks: The “live bookmark” content from an RSS feed is now available to the application.
The LoadLiveFavorite() method takes in the feed URL and then makes a request over the Web and loads the return document using an XmlTextReader object. I then apply XPath expressions to extract the title of the feed as well as the title of the various news items. This information is then used to create menus in the Live Bookmark ToolStrip (see Figure 13).
Each menu item can be clicked to display its page. Again, I make use of the ToolTipText property of each ToolStripMenuItem control to store its URL and I’ve also added an event handler to each menu item to service the Click event when the user selects one (see Listing 1).
To load all the live bookmarks subscribed, the user can select the menu item: Favorites -> Load all live bookmarks. This loads all “.live” files from “My documents” onto the live bookmarks bar:
Private Sub _ LoadallLiveBookmarksToolStripMenuItem_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles _ LoadallLiveBookmarksToolStripMenuItem.Click '===load all live bookmarks from disk=== '---look for all files with .live extension For Each foundFile As String In _ My.Computer.FileSystem.GetFiles _ (My.Computer.FileSystem. _ SpecialDirectories.MyDocuments, _ True, "*.live") '---read the content of each file, i.e. the RSS ' Feed URL Dim filePath As String Dim RSSURL As String Try filePath = System.IO.Path.Combine( _ My.Computer.FileSystem. _ SpecialDirectories.MyDocuments, _ foundFile) RSSURL = _ My.Computer.FileSystem. _ ReadAllText(filePath) '---display the live bookmark in the toolbar LoadLiveFavorite(RSSURL) Catch fileException As Exception Throw fileException End Try Next End Sub
When the user selects a menu item in a live bookmark, two behaviors can result: If one page is selected, it will display in the current tab page or if all items are selected, each will display in a series of new tab pages:
Private Sub menu_Click(ByVal sender As _ System.Object, _ ByVal e As System.EventArgs) '===Display the selected links in a live bookmark ' menu=== Dim menuitem As ToolStripMenuItem = _ CType(sender, ToolStripMenuItem) '---display all links in tabs If menuitem.Text = "Open in tabs" Then If TabControl1.TabPages.Count > 0 Then TabControl1.TabPages.Clear() End If For i As Integer = 0 To _ menuitem.Owner.Items.Count - 3 DisplayNewTab(menuitem.Owner. _ Items(i).ToolTipText) Next Else '---display link in current tab DisplayinCurrentTab(menuitem.ToolTipText) End If End Sub
Finally, when the user right-clicks on a tab page, the context menu appears to allow the user to close the tab:
Private Sub CloseTabToolStripMenuItem_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles CloseTabToolStripMenuItem.Click '===Close tab context menu to close the current ' tab page=== TabControl1.TabPages.Remove( _ TabControl1.SelectedTab) End Sub
And there you have it. I began with a simple form using WinForms 2.0’s WebBrowser control, among others, and then walked you through each step to write the code that makes these form controls come to life in the form of an enhanced Firefox-like browser. If you’ve followed along, you should now be able to run your application and try out your own next-generation tabbed browser.
There are still lots areas that you can enhance yourself. I strongly suggest you download the sample code and customize the application for your own use and according to your whims. For me, the ability to use live bookmarks is a good reason for this application to stay on my desktop. And I finally have a Web browser to call my own.