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!
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#.
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.
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.
Once added, controls from these libraries can be loaded as interactive controls for form applications or referenced for access to browser functionality.
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).
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.
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.
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.
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.
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).
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).
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.
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.
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.
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.
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).
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.
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.
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.
3.135.195.249