Chapter 11. Developing Applications with the WebBrowser Control

The web platform (HTML, JavaScript, etc.) is supplanting native-code platforms not only for browsers, but for general application development as well. Standalone applications, mobile phone applications, and a myriad of other non-browser-based programs are using markup, styles, and script as a base for their core functionality and UIs.

Applications using web technologies depend on access to and interpretation of web content; however, it wouldn't make any sense for every single application to provide its own networking stacks, protocol handlers, parsers, or layout engines. A number of companies have provided this offering to developers, but the most used and well known of these is Microsoft's WebBrowser control. This component is a set of COM-based libraries that can be hosted by an application to render web content. The WebBrowser control can do almost everything that IE can, from downloading and loading a web page to enforcing security policy on remote objects.

In this chapter I discuss ways you can use the WebBrowser control and the Microsoft .NET Framework to integrate the features of IE into your application. I begin with a simple overview of how this control can be imported and used in a basic C# project. Next, I cover topics such as event handling and access to a page's object model. Integration techniques are next, and it is there that I show you how your application can be informed of and control detailed aspects of this control. I finish up the chapter providing examples of IE's public API, allowing you to implement niche features that are not be exposed directly in the WebBrowser component. Let's open up our IDEs and get started!

Building a Simple WebBrowser Application

The WebBrowser control is a very powerful component that is very simple to integrate into almost any application. Developers can integrate this control into any language that supports the Windows COM-based programming model. The following sections highlight how a developer can get up and running with the WebBrowser control and related components using Visual Studio and C#.

Preparing to Use the WebBrowser Control

The WebBrowser control is a native assembly exposed through COM whose components can be interpreted as ActiveX controls or understood from type libraries. In .NET, the WebBrowser control is a wrapper for public APIs exposed by IE. This wrapper automatically marshals between types understood by managed and unmanaged code, making it easy for developers to include IE's APIs without complicated conversion algorithms.

The first step in using the WebBrowser control is to prepare the ActiveX controls and type libraries of SHDocVw and MSHTML for .NET interop. The SHDocVw DLL file, located in the System32 directory, must be imported and analyzed for use in managed code. The aximp application, supplied with the .NET SDK, allows a developer to import functionality from a native COM control into an interop library wrapper. Listing 11-1 shows the command for converting this DLL.

Example 11.1. Commmand for Converting a COM ActiveX DLL to a .NET Interop Assembly

aximp %windir%system32shdocvw.dll
tlbimp %windir%system32mshtml.tlb

Figure 11-1 shows the results: two generated assemblies, SHDocVw.dll and AxSHDocVw.dll. These libraries can be referenced in a Visual Studio project needing the control.

Results from running aximp on SHDocVw.dll

Figure 11.1. Results from running aximp on SHDocVw.dll

Type libraries, like ActiveX controls, must be wrapped before they can be understood by a managed application. MSHTML, the core DLL of IE's layout and rendering engine, Trident, falls into this category and must imported. Listing 11-1 (shown previously) shows the use of tlbimp for MSHTML.

These libraries can be added as a reference to a .NET project once converted. Figure 11-2 shows the process of adding references to these three files—SHDocVw.dll, AxSHDocVw.dll, and MSHTML.dll—to a project.

Visual Studio Add Reference context menu and dialog

Figure 11.2. Visual Studio Add Reference context menu and dialog

Once added, controls from these libraries can be loaded as interactive controls for form applications or referenced for access to browser functionality.

Creating an Instance of the WebBrowser Control (AxWebBrowser)

The WebBrowser control (AxWebBrowser, a component of AxSHDocVw) imported from the libraries in the previous section must be added to a Windows Form or a class programmatically. AxWebBrowser is a thin layer around the native-code COM objects exposed by IE.

Listing 11-2 shows the start of a basic application that will be used as the basis for the remaining examples in this chapter. It is a Windows Form called BrowserBasic, a very simple "browser" that can access web sites and demonstrate the functionality of the control. The class begins by defining a variable called Browser, an initially unset instance of the AxWebBrowser component. The constructor is used to call two methods: InitializeComponent(), generated by the form designer, and InitializeWebBrowser(). We'll implement the latter function to create a new instance of the browser component.

Example 11.2. Form Object Constructor Calling a Helper Method to Set Up a WebBrowser Instance

public partial class BrowserBasic : Form
{
    public AxWebBrowser Browser;
    // ... other objects

    public BrowserBasic()
    {
        // Initialize the component (designer)
        InitializeComponent();
        // Initialize the WebBrowser control
        InitializeWebBrowser();
    }
    ...

Listing 11-3 reveals InitializeWebBrowser(). This function sets a new instance of AxWebBrowser to the variable Browser. It starts initialization of the control by calling Browser.BeginInit(). The new object is added to the main form and docked to it.

Example 11.3. Function to Initialize and Configure a WebBrowser Control Instance

public void InitializeWebBrowser()
{
    // Create a new instance of the WebBrowser control
    Browser = new AxWebBrowser();

    // Add the control to the main form and dock it inside
    // of a panel
    this.Controls.Add(Browser);
    Browser.Parent = this;
    Browser.Dock = DockStyle.Fill;

    // Finish initializing the ActiveX
    Browser.EndInit();
}

The function ends by closing out the ActiveX initialization process for that control, calling Browser.EndInit(). This final call enables the control to be loaded and displayed by its owner form.

The remainder of this application's design was done using Visual Studio's form designer. Figure 11-3 shows the form's design. At the top is a toolbar, CommandBar, containing a text box for URLs (AddressBox), a Go button (GoButton), Forward button (ForwardButton), Home button (HomeButton), Stop button (StopButton), and Search button (SearchButton). These buttons are named for and intended to perform the same functions as their counterparts in IE and other major browsers (Figure 11-3).

The BasicBrowser application, form components, and WebBrowser control

Figure 11.3. The BasicBrowser application, form components, and WebBrowser control

Users of this application can access a web site by entering a URL into the AddressBox text box and then pressing the Enter key (AddressBox_KeyPress) or clicking the Go button (GoButton_Click). Both events trigger the WebBrowser's Navigate() function. This function is used to navigate a WebBrowser control to the URL passed to it. Listing 11-4 shows the GoButton_Click and AddressBox_KeyPress event handlers, both of which call the Navigate() function and pass to it the contents of AddressBox.

Example 11.4. Event Handlers for Navigating a WebBrowser Instance to an Address

private void GoButton_Click(object sender, System.EventArgs e)
{
    // Navigate to the address found in the address text box
    Browser.Navigate(AddressBox.Text);
}

private void AddressBox_KeyPress(object sender, KeyPressEventArgs e)
{
    // If enter was pressed, tread as if the "go" button was hit
    if (e.KeyChar == (char)Keys.Enter)
        Browser.Navigate(AddressBox.Text);
}

The remaining toolbar items access other major functions on the WebBrowser control instance. The Back button click handler (BackButton_Click) commands the WebBrowser control to go backward one step in its browsing history. The Forward button does the opposite—the click event (ForwardButton_Click) moves forward in the travel log. The Home button click event (HomeButton_Click) commands the browser to go to the current user's home page. Lastly, the Stop button references the StopButton_Click click event handler; it calls the WebBrowser object's Stop() function, halting any in-progress navigations occurring in the current WebBrowser instance (Listing 11-5).

Example 11.5. Button Event Handlers Used for Calling WebBrowser Functions

private void BackButton_Click(object sender, System.EventArgs e)
{
    // Go back in the travel log
    Browser.GoBack();
}

private void ForwardButton_Click(object sender, System.EventArgs e)
{
    // Go forward in the travel log
    Browser.GoForward();
}

private void HomeButton_Click(object sender, System.EventArgs e)
{
    // Go to the user's home page
    Browser.GoHome();
}

private void StopButton_Click(object sender, System.EventArgs e)
{
    // Stop the current navigation
    Browser.Stop();
}

The form's navigational elements provide the most basic functionality to access, display, and navigate web content. The application, however, remains primitive because while it allows the user to command the browser to perform a task, the application remains unaware of the events occurring in the WebBrowser.

Handling Basic Events

IE's API provides an event-driven system that gives developers the chance to tailor an application's functionality based events that occur within the WebBrowser object. The control exposes a subset of those events directly, and another subset through objects reachable from the WebBrowser object. These events can be configured to notify applications of events such as the start of a navigation, the completion of a download, and the creation of a new window.

The sample in Listing 11-6 builds on the basic browser constructed in the previous section. Unlike its base class, this application will play an interactive role in the WebBrowser control's activities by handling the events it exposes.

Example 11.6. Form for Handling WebBrowser Events, Based on the BrowserBasic Class

public class BrowserEventHandling : BrowserBasic
{
    public BrowserEventHandling(): base()
    {
        // Latch onto the DWebBrowserEvents2 events omitted by the
        // WebBrowser control
        HandleBasicEvents();
    }

The constructor here calls into a function called HandleBasicEvents(). When called, this function opts-into receiving callback notifications from a number of events on the WebBrowser instance (Listing 11-7).

Example 11.7. Method for Adding Handlers to WebBrowser Instance Events

private void HandleBasicEvents()
{
    // Add handlers to some of the basic web browser events
    Browser.BeforeNavigate2 += Browser_BeforeNavigate2;
    Browser.DocumentComplete += Browser_DocumentComplete;
    Browser.FileDownload += Browser_FileDownload;
    Browser.NavigateComplete2 += Browser_NavigateComplete2;
    Browser.NavigateError += Browser_NavigateError;
    Browser.NewProcess += Browser_NewProcess;
    Browser.NewWindow2 += Browser_NewWindow2;
}

Each event is handed a delegate instance that can be used when a specific event occurs. For instance, this function points to a private function, Browser_BeforeNavigate2, to be called when BeforeNavigate2 fires (Listing 11-8).

Example 11.8. Series of WebBrowser Event Handlers Writing to the Debug Stream

void Browser_NewWindow2(object sender,
   DWebBrowserEvents2_NewWindow2Event e)
{
    Debug.WriteLine("Event: DWebBrowserEvents2 NewWindow2");
}

void Browser_NewProcess(object sender,
   DWebBrowserEvents2_NewProcessEvent e)
{
    Debug.WriteLine("Event: DWebBrowserEvents2 NewProcess");
}
...

Listing 11-9 shows some output from the Visual Studio Debug window, captured while navigating to web pages using this application.

Example 11.9. Output from Simple WebBrowser Event Handlers

The thread '<No Name>' (0x10a8) has exited with code 0 (0x0).
Event: DWebBrowserEvents2 NewWindow2
Event: DWebBrowserEvents2 NewProcess

Accessing the Object Model

Web content that is successfully processed and parsed by Trident (IE's layout and rendering engine) is exposed to developers through a myriad of COM objects. Together, these objects form a hierarchy called the object model. When an instance of the WebBrowser control successfully completes the construction of a document, instances of these objects pertaining to a specific page are exposed through its members. The following sections detail how these objects may be accessed and modified when using the WebBrowser control to load a web page.

Attaching to Document and Window Objects

The availability of most top-level properties, methods, and events on the WebBrowser control are available as long as the WebBrowser has initialized. Other objects, such as those based on IHTMLDocument and IHTMLWindow, are only available when the WebBrowser control has loaded a page parsed, laid out, and rendered by MSHTML (Trident).

The WebBrowser control is similar to a full IE window in that both can host documents other than HTML documents (IHTMLDocument objects). Any document object (Active Document Server) can be loaded into the WebBrowser control. This means that not every document will have an IHTMLDocument object present. Developers looking to attach to this specific object should test that the document type is an HTML document and that the IHTMLDocument object is in fact present within the loaded document instance.

The following sample demonstrates a new WebBrowser container based on the one developed in the last section. This application performs two functions: analyzing the HTML parsed by the browser, and handling events from objects generated by that process. To show the first task, it generates a report on all links (IHTMLAnchorElement) present within a document. For the second, the application takes over the JavaScript error dialog in favor of a custom one.

Listing 11-10 is the constructor for this application. This function can't start work on either of the tasks intended for the application because no page has been loaded into the object yet. To solve this problem, the constructor sinks the control's NavigateComplete2 event, pointing the event to a callback that can run with assurance that the objects in question exist.

Example 11.10. Constuctor Adding NavigateComplete2 Handler to WebBrowser Control Instance

public class BrowserModelAccess : BrowserEventHandling
{
    TextBox PageReport;
    bool eventsSunk = false;
    public BrowserModelAccess() : base()
    {
        // Register for the NavigateComplete event for both the
        // link report and the script error handling
        Browser.NavigateComplete2 += Browser_NavigateComplete2;
    }
    ...

Listing 11-11 shows the initialization of both the reporting and error handling portions of the application. This is handled by the NavigateComplete2 event handler, Browser_NavigateComplete2. This function kicks off the process for both building the link report and sinking error events.

Example 11.11. Waiting for Available Window and Document Objects Using NavigateComplete2

void Browser_NavigateComplete2(object sender, DWebBrowserEvents2_NavigateComplete2Event e)
{
    // Pass the valid document and window objects and build
    // a link report on the document
    BuildLinkReport();

    // Sink script errors for this window instance
if(!eventsSunk) {
       eventsSunk = true;
       SinkScriptErrorEvents();
    }
}

Unlike the constructor, NavigateCompete2 indicates that a WebBrowser instance has requested, downloaded, parsed, and loaded a document into a valid window and document object under a certain WebBrowser instance. These valid objects can now be used by BuildLinkReport() and SinkScriptErrorEvents() to read and receive callbacks from target window and document objects.

Accessing the Browser Object Model

The markup, script, and styles interpreted by IE and its components are exposed through a large set of objects. These items together form the object model, a traversable and hierarchical model used by IE to display and interact with the web page.

Objects are available for access as children of an object implementing the IHTMLDocument interface. The WebBrowser control exposes such an instance through its public member Document. This member (and thus its children) is only available when the browser is displaying an HTML document.

Listing 11-12 shows BuildLinkReport(), a function called during the NavigateComplete2 event handler shown in the last section. This function begins by grabbing and casting the document object instance from the WebBrowser instance. If this instance is valid (not null), it starts the "report" by writing some document metadata to the debugging window.

Example 11.12. Function to Report All Links on an IHTMLDocument2 Object

void BuildLinkReport()
{
    // Grab the document object from the WebBrowser control
    IHTMLDocument2 document = Browser.Document as IHTMLDocument2;
    if (document == null) return;

    // Write the title and indicate the URL
    Debug.WriteLine("

LINK REPORT for " + document.url);

    // Report the page URL, file size, and total number of links
    Debug.WriteLine("URL: " + document.url);
    Debug.WriteLine("Filesize: " + document.fileSize.ToString());
    Debug.WriteLine("Total Links: " + document.links.length.ToString());

    // Display all the links on the current page
    foreach (IHTMLAnchorElement link in document.links)
        Debug.WriteLine("  >> href=" + link.href);
}

The last two tasks of this function are to report on the number of links and the href values of those links. The links on a page are exposed by a links member on the document object. This object implements IHTMLElementCollection and represents a collection of objects derived from a direct or indirect implementation of IHTMLElement.

The link count is written to the debugger using a string conversion of IHTMLElementCollection's length property. The actual link href values are displayed by iterating through each IHTMLAnchorElement object within the links collection; each URL is recorded using that interface's href value (Figure 11-4).

Link report results after traversing link objects from news.google.com

Figure 11.4. Link report results after traversing link objects from news.google.com

Sinking Object Model Events

Just like the base WebBrowser object, the objects present under a WebBrowser's IHTMLDocument implementation instance each have events that can be accessed and handled by a WebBrowser container application. These objects provide insight beyond basic document information exposed up through the WebBrowser object; applications can go deep, and even implement the same events used and thrown by JavaScript on a page.

The SinkScriptErrorEvents() function in Listing 11-13 is called during the NavigateComplete2 event (as the previous helper was). This function replaces IE's JavaScript error dialog with its own. To accomplish this feat, it adds an OnError handler to the Window object of a WebBrowser instance's child IHTMLDocument.

The first step is to turn off JavaScript errors in the current WebBrowser instance. A simple way of achieving this is through the WebBrowser control's Silent property. The Silent property controls such reporting; when true, it does not display error dialogs, when false, it does (as long as script debugging is not disabled either in the Internet Settings Control Panel or by IE policies). The function then attempts to grab the document object and, subsequently, that document's window object. If those attempts are valid, the function will cast a variable to the event interface on IHTMLWindow and attach a new handler to that object (Browser_HTMLWindowEvents2_OnError).

Example 11.13. Turning Off JavaScript Errors and Adding a Handler to Display Custom Ones

void SinkScriptErrorEvents()
{
    // Turn off default script errors
    Browser.Silent = true;

    // Grab the document object from the WebBrowser control
    IHTMLDocument2 document = (IHTMLDocument2)Browser.Document;
    if (document == null) return;

    // Grab the window object from the document object
    HTMLWindow2 window = (HTMLWindow2)document.parentWindow;
    if (window == null) return;

    // Cast the window object to the window events interface
    HTMLWindowEvents2_Event windowEvents = (HTMLWindowEvents2_Event)window;
    if (windowEvents == null) return;

    // Attach to the error event on the window object; this
// will be sent a notification when a script error occurs
    windowEvents.onerror += Browser_HTMLWindowEvents2_OnError;
}

The last piece is the handler itself. This is a very simple event, receiving three important error items: the error description, the URL of the file causing the error, and the line number of the offending script (Listing 11-14).

Example 11.14. IHTMLWindow OnError Handler Replacing IE-JavaScript Error Dialogs

void Browser_HTMLWindowEvents2_OnError(string description, string url, int line)
{
    // Show a custom message box that displays the javascript
    // error to the user
    MessageBox.Show(String.Format(
        "Description: {0}

Line: {1}",
        description,
        line.ToString()),
        "JavaScript Error",
        MessageBoxButtons.OK,
        MessageBoxIcon.Warning
        );
}

This function proceeds to show a warning-style message box displaying error information returned from the event handler when invoked (Figure 11-5).

Custom JavaScript error dialog triggered by the HTML Window OnError event

Figure 11.5. Custom JavaScript error dialog triggered by the HTML Window OnError event

Achieving Tight Integration with IE

The full IE application is full of features that link the browser frame with the web page running inside of it. The title bar displays the page title; the status bar shows the progress of composing the page; a lock icon appears for secure pages, and so on. Display of this information relies directly on the browser window having a relationship with the internal browser engine, the system networking stack, and so forth.

Many of the methods and data used by the IE browser frame to convey information are already available in .NET through the WebBrowser control and other .NET interoperable libraries. The WebBrowser control makes this data easily accessible to applications hosting the component. The following sections highlight examples of how an application hosting the WebBrowser control can use this information.

Setting Up the Application

The previous section in this chapter highlighted ways an application using the WebBrowser control can access and manipulate the object model. It also tied together the earlier section on events by hooking into a number of events exposed by those objects.

Listing 11-15 demonstrates an application built on the previous example that integrates IE features and navigation information into its UI. It begins by making the previous example its base class.

Example 11.15. Class and Constructor for a TightLy Integrated WebBrowser Host

public class BrowserIntegration : BrowserModelAccess
{
    public BrowserIntegration() : base()
    {
        // Integrate UI with IE notifications
        BuildInterfaceIntegration();
        // Integrate with the NewWindow event
        BuildWindowIntegration();
        // Add menu and command features
        BuildMenuAndCommands();
    } ...

The constructor for this class handles three major integration points. First, it integrates the application's form and UI with data and events coming back from the WebBrowser control. Next, it enables the application to handle pop-up windows "in-house" instead of having those windows opened outside of this application or in an external browser. Last, it creates some basic UI code that demonstrates how IE commands such as Save As and Print can be invoked using a wrapped form of IOleCommandTarget command execution.

Integrating WebBrowser Events with the IE UI

Web browsers typically convey information about the process of a request to a user through their UI. When loading a page, users can see loading information in the page title, animated wait cursors, and information about the loading process in the status bar. This is the same for commands and actions outside of viewing a page; cleaning out history, downloading a file, or saving a page to disk all result in progress information conveyed to the user.

The WebBrowser control offers a rich set of information concerning its progress performing a set of actions and regarding objects and pages loaded within it. The properties and events exposed in this assembly can be used by a container application to convey this same information to users.

Listing 11-16 highlights the first integration example in this section. The BuildInterfaceIntegration() function sets properties and hook events in order to gather this information from the control and convey it to the user. The constructor runs this function, and it in turn sinks three events on the WebBrowser control instance for the class: NavigateComplete2, TitleChange, and StatusTextChange.

Example 11.16. Registering Event Handlers That Will Convey Progress to Application UI

private void BuildInterfaceIntegration()
{
    // Sink key events that convey progress information to
    // page or object progress
    Browser.DocumentComplete += Browser_DocumentComplete;
Browser.TitleChange += Browser_TitleChange;
    Browser.StatusTextChange += Browser_StatusTextChange;
}

These event callbacks are defined next. First in Listing 11-17 is the DocumentComplete event. This event is fired when a document download, navigation, and load is finished; this means that the page in the main window is the page that the user has finally arrived at. This event is a good time to get the current address, and has an accurate address to display in the address bar. When this callback is fired, it takes the current location URL of the WebBrowser instance and displays it in the address bar text box AddressBox.

Example 11.17. Event Callbacks for WebBrowser Events That Convey Progress Information

void Browser_DocumentComplete(object sender,
    DWebBrowserEvents2_DocumentCompleteEvent e)
{
    // Set the address box text to the current URL
    AddressBox.Text = Browser.LocationURL;
}

void Browser_TitleChange(object sender,
    DWebBrowserEvents2_TitleChangeEvent e)
{
    // Set window title to the page title
    this.Text = e.text + " - Custom WebBrowser";
}

void Browser_StatusTextChange(object sender,
    DWebBrowserEvents2_StatusTextChangeEvent e)
{
    // Change the status text to the text emitted by IE
    BrowserStatusText.Text = e.text;
}

Next is the TitleChange event. This event is fired every time the title of the main document (or frame) changes. In IE and other browsers, this change is reflected in the main application title, and if it's using tabs, the tab in which the page is loaded. This function provides that behavior for our application. When the TitleChange event fires, this function changes the title of the application window to include the title of the current page.

The last event handled by this class is the StatusTextChange event. IE uses its status bar pane to convey status information from the loading of the page. This data isn't just shown in the IE frame window, however—it is emitted to any application using the WebBrowser control. This function taps into that information and applies the current status text data to this application's own status bar each time that data is changed and the event is fired.

Mimicking Window Behavior of IE

Developers looking to use a wrapped WebBrowser control as a browser substitute might find that some web sites or user activities open pages in IE or their default browser. By default, a pop-up or user command to open a link in a new window will force a WebBrowser control instance to launch that page outside of its owner application. Pop-up navigations can be canceled through the BeforeNavigate2 event, but that is not the only option.

Listing 11-18 shows a function called BuildWindowIntegration(). This is called by its class's constructor and is tasked with enabling this setting on the control and kicking off the event handling process for windows.

Example 11.18. Function to Register WebBrowser Instance As Window Handler and Attach Event

private void BuildWindowIntegration()
{

    // Sink the NewWindow2 event
    Browser.NewWindow2 += Browser_NewWindow2;
}

The NewWindow2 event callback definition follows in Listing 11-19. This event will be fired and this function called whenever a pop-up or user action requests an external window. The function begins by creating a new instance of this application; just as IE would launch another instance of IE, this application will clone itself to host the request.

Example 11.19. New Window Event Handler for the WebBrowser Control Registered As a Browser

void Browser_NewWindow2(object sender,
    DWebBrowserEvents2_NewWindow2Event e)
{

    // Create a new instance of this form
    BrowserIntegration newBrowserForm;
    newBrowserForm = new BrowserIntegration();

    // Return the new WebBrowser control instance on the new application
    // instance as the new client site
    e.ppDisp = newBrowserForm.Browser.Application;

    // Show the new window
    newBrowserForm.Visible = true;

}

The new WebBrowser control instance on the new form instance is applied to the outbound variable e.ppDisp; this allows the WebBrowser to navigate the new WebBrowser instance rather than sending the navigation request to the system default browser. Finally, the function shows the newly created form to the user.

Surfacing and Executing OLE Commands

Much of IE's functionality is exposed as commands that can be targeted at IE's application site. In the past (and still today in many cases), applications who wished to run commands on IE or other applications would implement an interface called IOleCommandTarget. This interface defines two functions: QueryStatus, which lets an application find out if a command is available, and Exec, which executes a command.

The WebBrowser control simplifies the process of using OLE commands by implementing and wrapping the IOleCommandTarget interface.

The code in Listing 11-20 allows the application to handle two of these commands: Print and Save As. The first part of the process is to make these commands available to users; the save-as functionality is added to a new menu, and the Print button is added to the application's toolbar.

Example 11.20. Constuction of Save As and Print UI and Event Handler Registration

// Declare new menu and button objects
public MenuStrip Menu;
public ToolStripMenuItem MenuFile;
...
public ToolStripItem PrintButton;

private void BuildMenuAndCommands()
{
    // Add a Print button to the toolbar
    PrintButton = CommandBar.Items.Add("Print");
    ...
    PrintButton.Click += new System.EventHandler(PrintButton_Click);

    // Create the File > Save As menu item
    MenuFileSaveAs = new ToolStripMenuItem("Save As...");
    MenuFileSaveAs.Click += MenuFileSaveAs_Click;

    // Create the File menu and add children
    MenuFile = new ToolStripMenuItem("&File");
    MenuFile.Click += MenuFile_Click;
    MenuFile.DropDownItems.Add(MenuFileSaveAs)
    ...

    // Create a new main menu strip and add all the items to it
    Menu = new MenuStrip();
    Menu.Items.AddRange(new ToolStripItemCollection(
        Menu, new ToolStripItem[] { MenuFile, ...}));
    Menu.Dock = DockStyle.Top;
    Controls.Add(Menu);
}

The toolbar button for Print has a click event handler attached (PrintButton_Click). Likewise, the menu item used for Save As is given an event handler of MenuFileSaveAs_Click and its parent menu item is attached to MenuFile_Click. These handlers will not only launch the related functionality but also play a role in determining when those commands are available (Figure 11-6).

Print button and dialog using Exec commands

Figure 11.6. Print button and dialog using Exec commands

The first handler here (Listing 11-21) is that controlling the print functionality. When a user clicks this toolbar button, this function is called. Before printing, however, the application needs to see if printing is available for the current state of the WebBrowser control instance. For example, when a page is sitting idle, it would make sense for IE to make printing available; on the other hand, if IE is in the middle of navigating, the command will be disabled because complete page data is not available.

Example 11.21. Click Event for the Print Button OLE Command

void PrintButton_Click(object sender, EventArgs e)
{

    // Query the status of the Print command
    SHDocVw.OLECMDF printQuery =
        Browser.QueryStatusWB(SHDocVw.OLECMDID.OLECMDID_PRINT);

    // If the command is enabled, display the Print dialog
    if ((printQuery & SHDocVw.OLECMDF.OLECMDF_ENABLED) != 0)
        Browser.ExecWB(
            SHDocVw.OLECMDID.OLECMDID_PRINT,
            SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER
            );

}

QueryStatusWB() is called to determine whether this command is ready to be used. If the preceding if statement is true, meaning that the return value of the command query contained an OLECMDF_ENABLED (enabled) tag, then the command is available and may be called through ExecWB().

The printing function protects against errors; however, it does not provide useful feedback if the command isn't available. Figure 11-7 shows an example application that makes another piece of browser functionality available: the Save As menu item.

Save As menu item and Save As window

Figure 11.7. Save As menu item and Save As window

The Save As menu item and its parent, the File menu, work with QueryStatusWB() to convey availability of this command to the user. The MenuFile_Click handler enables or disables this command to save based on availability. The function begins by querying for the availability of the Save As command and persisting it to a variable. Next, it enables or disables the child menu item based on whether its associated command is enabled or disabled (Listing 11-22).

Example 11.22. Click Events for the File Menu and Save As Items Using the Save As OLE Command

void MenuFile_Click(object sender, EventArgs e)
{
    // Query the status of the Save As command
    SHDocVw.OLECMDF saveAsQuery =
        Browser.QueryStatusWB(SHDocVw.OLECMDID.OLECMDID_SAVEAS);
// Enable the Save As menu item only
    // if that command is currently available
    MenuFileSaveAs.Enabled =
        ((saveAsQuery & SHDocVw.OLECMDF.OLECMDF_ENABLED) != 0);
}

void MenuFileSaveAs_Click(object sender, EventArgs e)
{
    // Query the status of the Save As command
    SHDocVw.OLECMDF saveAsQuery =
    Browser.QueryStatusWB(SHDocVw.OLECMDID.OLECMDID_SAVEAS);

    // Execute the Save As action and prompt the user
    // with a Save As dialog
    if ((saveAsQuery & SHDocVw.OLECMDF.OLECMDF_ENABLED) != 0)
        Browser.ExecWB(
            SHDocVw.OLECMDID.OLECMDID_SAVEAS,
            SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER
            );
}

The Save As menu item click event, MenuFileSaveAs_Click, acts much like that of the Print command. The function first gathers the status of the command from QueryStatusWB(). Next, if the value contains the OLECMDID_ENABLED flag, it allows the command to proceed.

Summary

The WebBrowser control is a great way for developers to interface their applications with the Internet. On the surface it presents itself as a bare-bones, wrappable instance of IE that is useful in a wide variety of applications: basic browser windows, kiosks, application start pages, and so on. However, underneath this basic exterior lies a great system that exposes the underlying application and object model in a simple way. The .NET Framework ups the ante even more by simplifying the process of accessing IE's internal interfaces and events.

This chapter demonstrated how you can use the WebBrowser control in a number of ways. We got started with the basics: finding the libraries, preparing them for interop, referencing them, and spinning them into a C# project. Next, I discussed basic integration points and built a simple web browser using this control, an address bar, and some buttons. Event handling methods followed; I demonstrated how you can tailor your code to react to IE's application and page events. After getting these basics down, I talked about a deeper integration with IE's features: handling windows, displaying information such as download progress, and executing commands through OLE. I followed with a presentation of the object model and examples of how objects exposed from the browser window and documents can be accessed and traversed by your code. Finally, I broke into some more advanced examples of how IE features may be used through invocation of the browser's public API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.195.249