在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
本文转自http://www.codeproject.com/KB/books/0764549146_8.aspx
Chapter 8: Creating Front Ends with the WebBrowser Component
In This Chapter
Chapter 8: Creating Front Ends with the WebBrowser ComponentA billion here, a billion there — sooner or later it adds up to real money. Senator Dirksen’s concept of real money may have been slightly different from yours or mine, but one thing is certain: If you fail to take advantage of existing components when building the front end of your application, you’ll find your development costs adding up to real money. The Microsoft WebBrowser ComponentOne of the most powerful reusable UI components I know of is the Since I’ll call the first browser application MyWebBrowser and give it a tool bar, to show how to control some of the common functions of Figure 8-1: Using MyWebBrowser to display an HTML page Figure 8-2: Using MyWebBrowser to display a Word document Figure 8-3: Using MyWebBrowser to display an Excel spreadsheet Figure 8-4: Using MyWebBrowser to display a Powerpoint document Figure 8-5: Using MyWebBrowser to display an XML document Figure 8-6: Using MyWebBrowser to display a PDF document These screen shots hopefully give you some sense of how flexible Not only does
I’ll show you how to make these and other kinds of customizations to Designing MyWebBrowserSince almost all the functionality you need is already built into WebBrowser, the design of an application based on Windows Forms that embeds the component is fairly trivial. Figure 8-7 shows the salient parts of the class diagram for MyWebBrowser. Figure 8-7: The class diagram for MyWebBrowser Like I said, in this first example, I won’t add any special customizations to Developing MyWebBrowserEnough said about the glories of I created MyWebBrowser with the New Project Wizard, choosing Windows Application as the project type. I renamed the main form class from Form1 to MainForm, and set the project name to MyWebBrowser. The next step was to add the Importing the WebBrowser ActiveX ComponentThere is no The easy wayThe easy way is with the Customize Toolbox dialog box. First select the Windows Forms page on the Toolbox and then right-click on the Toolbox and choose the Customize Toolbox command on the pop-up menu. Select the COM Components tab, scroll down to the Microsoft Web Browser item, and check its checkbox, as shown in Figure 8-8. Figure 8-8: Importing the ShDocVw.WebBrowser control into the VS .NET Toolbox The Figure 8-9: The WebBrowser component after installing it on the Toolbox Now drop an instance of
A nice piece of work, saving you precious time and money — perhaps not billions, but hey, no one said life is fair. In case you’re wondering, much of the process was performed under the covers by a command-line utility called aximp, described later. At this point the Solution Explorer looks like Figure 8-10. Figure 8-10: The Solution Explorer, showing the newly imported files That was the easy way to import the The hard wayI always get a kick out of watching people open DOS boxes and furiously type in long commands that could be replaced with a couple of mouse clicks. Force of habit is a powerful thing. But using command-line utilities for the import process is not always a bad thing, because they create the necessary import files without adding anything to the Toolbox. Although you may want to have a useful component like In any case, there are two command-line utilities available for converting COM types into .NET-compatible types that can be referenced in a VS .NET project. Which one to use depends on how you want to use the imported components. Using TlbImpThe lowest level command-line utility for ActiveX importing is For example, to use shdocvw.dll (containing the
You can inspect the .NET metadata in the DLL generated by
Figure 8-11 shows some of the contents of the metadata file displayed by Figure 8-11: Inspecting the metadata in the DLL produced by running TlbImp on c:\winnt\system32\shdocvw.dll The metadata makes it possible for your code to interact with the unmanaged code of COM components using exactly the same syntax you would use with native C# components. Using AxImpIf you plan to use an ActiveX component in a Windows Form, as I did with To create the wrapper for
The utility will generate two files, called SHDocVw.dll and AxSHDocVw.dll. The first file contains the .NET metadata that describes the COM types contained in Figure 8-12: The wrapper class created for SHDocVw Basically, Runtime Callable Wrappers (RCW)When you instantiate a COM type in your C# code, using code like this:
there is more going on than meets the eye. What actually happens is this: The compiler looks at the metadata describing the class Figure 8-13: The Runtime Callable Wrapper as a proxy for COM components The RCW manages all those pesky COM details that you don’t want to deal with, such as reference counting, deleting the component when it is no longer used, marshalling parameters in method calls, and so on. If you create multiple instances of the same COM component in your code, all the instances will share a single RCW. Keep in mind that all this RCW business is generally completely transparent to you. It’s there to help you, making it as easy to access an ActiveX component as any other managed component. Adding a toolbarBefore I completely lose track of where I am, let me finish describing the code for MyWebBrowser. First I’ll discuss the toolbar buttons. Most UIs containing I added a toolbar to MyWebBrowser by dropping a Toolbox component of type
To make the buttons look like those used by Internet Explorer, I used a screen capture utility to get the IE button images, and then saved them as bitmap files in the folder containing the source code for MyWebBrowser. I set them all to have a green background. I added an Figure 8-14: The finished toolbar Notice the flat look of the toolbar, with the buttons showing no borders. This look was achieved by setting the toolbar’s Listing 8-1:: The Toolbar Event Handler
Copy Code
object sender, System.WinForms.ToolBarButtonClickEventArgs e) { Cursor.Current = Cursors.WaitCursor; try { if (e.button == toolBarButtonBack) axWebBrowser1.GoBack(); else if (e.button == toolBarButtonForward) axWebBrowser1.GoForward(); else if (e.button == toolBarButtonStop) { axWebBrowser1.Stop(); toolBarButtonStop.Enabled = false; } else if (e.button == toolBarButtonSearch) axWebBrowser1.GoSearch(); else if (e.button == toolBarButtonPrint) PrintPage(); else if (e.button == toolBarButtonRefresh) { object REFRESH_COMPLETELY = 3; axWebBrowser1.Refresh2(ref REFRESH_COMPLETELY); } else if (e.button == toolBarButtonHome) axWebBrowser1.GoHome(); } finally { Cursor.Current = Cursors.Default; } } The method The method Adding support for printingAll of the buttons except Print are handled by calling a method in the embedded Listing 8-2:: Printing the HTML Page Displayed in the WebBrowser
Copy Code
bool IsPrinterEnabled() { int response = (int) axWebBrowser1.QueryStatusWB(SHDocVw.OLECMDID.OLECMDID_PRINT); return (response & (int) SHDocVw.OLECMDF.OLECMDF_ENABLED) != 0 ? true : false; } private void PrintPage() { object o = ""; // constants useful when printing SHDocVw.OLECMDID Print = SHDocVw.OLECMDID.OLECMDID_PRINT; // use this value to print without prompting // SHDocVw.OLECMDEXECOPT PromptUser = // SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER; SHDocVw.OLECMDEXECOPT DontPromptUser = SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER; if (!IsPrinterEnabled() ) return; // print without prompting user axWebBrowser1.ExecWB(Print, DontPromptUser, ref o, ref o); // to prompt the user with printer settings // axWebBrowser1.ExecWB(Print, PromptUser, ref o, ref o); } The last two lines in the listing show two ways to print: The first way prints silently, the second way prints after prompting the user for printer settings. I created two constants called Adding navigation supportTo navigate to a Web site, you call the Listing 8-3:: Setting the URL of the Document to Load
Copy Code
String theURL) { try { Cursor.Current = Cursors.WaitCursor; Object o = null; axWebBrowser1.Navigate(theURL, ref o, ref o, ref o, ref o); } finally { Cursor.Current = Cursors.Default; } } To allow the user to type a URL in the Listing 8-4:: The TextBox Event Handler
Copy Code
object sender, System.WinForms.KeyEventArgs e) { if (e.KeyCode == Keys.Return) GotoURL(textBoxAddress.Text); } To display an hourglass cursor while a page is being loaded, I set the cursor in the Listing 8-5:: Setting the Cursor to an Hourglass during Navigation
Copy Code
object sender,
AxSHDocVw.DWebBrowserEvents2_BeforeNavigate2Event e)
{
toolBarButtonStop.Enabled = true;
Cursor.Current = Cursors.WaitCursor;
}
Once a page is loaded, I need make sure the cursor is eventually restored to an arrow pointer. Navigation commands can end in one of two ways: If the page can’t be loaded, the Listing 8-6:: The NavigateError Handler that Restores the Mouse Cursor
Copy Code
object sender, AxSHDocVw.DWebBrowserEvents2_NavigateErrorEvent e) { Cursor.Current = Cursors.Default; toolBarButtonStop.Enabled = false; toolBarButtonHome.Enabled = true; toolBarButtonSearch.Enabled = true; toolBarButtonRefresh.Enabled = true; } If navigation to a site is successful, the Listing 8-7:: The NavigateComplete2 Handler
Copy Code
object sender, AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e) { Cursor.Current = Cursors.Default; toolBarButtonStop.Enabled = false; toolBarButtonHome.Enabled = true; toolBarButtonSearch.Enabled = true; toolBarButtonRefresh.Enabled = true; // update the URL displayed in the address bar String s = e.uRL.ToString(); textBoxAddress.Text = s; // update the list of visited URLs int i = urlsVisited.IndexOf(s); if (i >= 0) currentUrlIndex = i; else currentUrlIndex = urlsVisited.Add(s); // enable / disable the Back and Forward buttons toolBarButtonBack.Enabled = (currentUrlIndex == 0) ? false : true; toolBarButtonForward.Enabled = (currentUrlIndex >= urlsVisited.Count-1) ? false : true; // set the state of the Print button toolBarButtonPrint.Enabled = IsPrinterEnabled(); } In the Disabling the Back and Forward buttons is more than just a cosmetic exercise: If you call the The last feature to add is one that will make MyWebBrowser go to the Home page when it is run. All you need to do is call the method The complete codeThe next section will deal with ways to customize the Listing 8-8:: The Code for MyWebBrowser
Copy Code
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace MyWebBrowser { public class MainForm : System.Windows.Forms.Form { private System.Windows.Forms.Panel panel1; private System.Windows.Forms.ToolBar toolBar1; private System.Windows.Forms.ImageList imageList1; private System.Windows.Forms.ToolBarButton toolBarButtonBack; private System.Windows.Forms.ToolBarButton toolBarButtonForward; private System.Windows.Forms.ToolBarButton toolBarButtonStop; private System.Windows.Forms.ToolBarButton toolBarButtonRefresh; private System.Windows.Forms.ToolBarButton toolBarButtonHome; private System.Windows.Forms.ToolBarButton toolBarButtonSearch; private System.Windows.Forms.ToolBarButton toolBarButtonPrint; private AxSHDocVw.AxWebBrowser axWebBrowser1; private System.Windows.Forms.TextBox textBoxAddress; private System.ComponentModel.IContainer components; ArrayList urlsVisited = new ArrayList(); int currentUrlIndex = -1; // no sites visited initially public MainForm() { InitializeComponent(); toolBarButtonBack.Enabled = false; toolBarButtonForward.Enabled = false; toolBarButtonStop.Enabled = false; toolBarButtonRefresh.Enabled = false; toolBarButtonHome.Enabled = false; toolBarButtonSearch.Enabled = false; toolBarButtonPrint.Enabled = false; axWebBrowser1.GoHome(); } protected override void Dispose( bool disposing ) { // standard wizard-created code } private void InitializeComponent() { // standard wizard-created code } [STAThread] static void Main() { Application.Run(new MainForm()); } private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) { Cursor.Current = Cursors.WaitCursor; try { if (e.Button == toolBarButtonBack) axWebBrowser1.GoBack(); else if (e.Button == toolBarButtonForward) axWebBrowser1.GoForward(); else if (e.Button == toolBarButtonStop) { axWebBrowser1.Stop(); toolBarButtonStop.Enabled = false; } else if (e.Button == toolBarButtonSearch) axWebBrowser1.GoSearch(); else if (e.Button == toolBarButtonPrint) PrintPage(); else if (e.Button == toolBarButtonRefresh) { object REFRESH_COMPLETELY = 3; axWebBrowser1.Refresh2(ref REFRESH_COMPLETELY); } else if (e.Button == toolBarButtonHome) axWebBrowser1.GoHome(); } finally { Cursor.Current = Cursors.Default; } } private bool IsPrinterEnabled() { int response = (int) axWebBrowser1.QueryStatusWB(SHDocVw.OLECMDID.OLECMDID_PRINT); return (response & (int) SHDocVw.OLECMDF.OLECMDF_ENABLED) != 0 ? true : false; } private void PrintPage() { object o = ""; // constants useful when printing SHDocVw.OLECMDID Print = SHDocVw.OLECMDID.OLECMDID_PRINT; // use this value to print without prompting // SHDocVw.OLECMDEXECOPT PromptUser = // SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER; SHDocVw.OLECMDEXECOPT DontPromptUser = SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER; if (!IsPrinterEnabled() ) return; // print without prompting user axWebBrowser1.ExecWB(Print, DontPromptUser, ref o, ref o); // to prompt the user with printer settings // axWebBrowser1.ExecWB(Print, PromptUser, ref o, ref o); } public void GotoURL(String theURL) { try { Cursor.Current = Cursors.WaitCursor; Object o = null; axWebBrowser1.Navigate(theURL, ref o, ref o, ref o, ref o); } finally { Cursor.Current = Cursors.Default; } } private void textBoxAddress_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) { if (e.KeyCode == Keys.Return) GotoURL(textBoxAddress.Text); } private void axWebBrowser1_BeforeNavigate2(object sender, AxSHDocVw.DWebBrowserEvents2_BeforeNavigate2Event e) { toolBarButtonStop.Enabled = true; Cursor.Current = Cursors.WaitCursor; } private void axWebBrowser1_NavigateComplete2(object sender, AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e) { Cursor.Current = Cursors.Default; toolBarButtonStop.Enabled = false; toolBarButtonHome.Enabled = true; toolBarButtonSearch.Enabled = true; toolBarButtonRefresh.Enabled = true; // update the URL displayed in the address bar String s = e.uRL.ToString(); textBoxAddress.Text = s; // update the list of visited URLs int i = urlsVisited.IndexOf(s); if (i >= 0) currentUrlIndex = i; else currentUrlIndex = urlsVisited.Add(s); // enable / disable the Back and Forward buttons toolBarButtonBack.Enabled = (currentUrlIndex == 0) ? false : true; toolBarButtonForward.Enabled = (currentUrlIndex >= urlsVisited.Count-1) ? false : true; // set the state of the Print button toolBarButtonPrint.Enabled = IsPrinterEnabled(); } private void axWebBrowser1_NavigateError(object sender, AxSHDocVw.DWebBrowserEvents2_NavigateErrorEvent e) { Cursor.Current = Cursors.Default; toolBarButtonStop.Enabled = false; toolBarButtonHome.Enabled = true; toolBarButtonSearch.Enabled = true; toolBarButtonRefresh.Enabled = true; } } } In the next section, I’ll switch gears and get into advanced topics with COM Interop programming. If you’re not experienced in COM, you may want to skip the rest of the chapter entirely. Creating a Customized Web BrowserMyWebBrowser has all the basic browser features, such as navigation, printing, and so on. It also has features you may want to change, such as how accelerator keys are handled or what commands are available on the context menu. To make these kinds of changes, things get a bit more complicated with Customizing the
That would have been way too simple! Besides, each time the parent The moral of the story is this: MainForm needs to implement a number of COM interfaces to support Figure 8-15: The class diagram for a fully customized WebBrowser host Windows Form Let’s take a look at what the COM interfaces are used for. The first step in any type of Listing 8-9:: Setting Up MainForm as the Controlling Host of WebBrowser
Copy Code
object obj = axWebBrowser1.GetOcx(); IOleObject oc = obj as IOleObject; oc.SetClientSite(this); These three deceptively simple lines of code are critical. The method Importing and wrapping COM interfacesMyCustomWebBrowser implements the two COM interfaces IOleObject and IOleClientSiteYou sometimes have to do a bit of digging to locate the file that declares an interface. In the case of
I created C# interfaces to wrap the two COM interfaces, as shown in Listings 8-10 and 8-11. Listing 8-10:: The Interface that Wraps the COM Interface IOleObject
Copy Code
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace MyCustomWebBrowser { [ComImport, Guid("00000112-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown) ] public interface IOleObject { void SetClientSite(IOleClientSite pClientSite); void GetClientSite(IOleClientSite ppClientSite); void SetHostNames(object szContainerApp, object szContainerObj); void Close(uint dwSaveOption); void SetMoniker(uint dwWhichMoniker, object pmk); void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk); void InitFromData(IDataObject pDataObject, bool fCreation, uint dwReserved); void GetClipboardData(uint dwReserved, IDataObject ppDataObject); void DoVerb(uint iVerb, uint lpmsg, object pActiveSite, uint lindex, uint hwndParent, uint lprcPosRect); void EnumVerbs(object ppEnumOleVerb); void Update(); void IsUpToDate(); void GetUserClassID(uint pClsid); void GetUserType(uint dwFormOfType, uint pszUserType); void SetExtent(uint dwDrawAspect, uint |
请发表评论