|
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Integration ActiveX Controls and .NET
ActiveX Controls and .NET
By: Frank Sloane
Sep. 11, 2003 12:00 AM
ActiveX controls have been the mainstay of most Visual Basic 6 developers and have found their way into countless development tools and Windows applications. Most ActiveX controls, even the "free" controls included with Visual Basic, can be used with Visual C++, Access, Delphi, Oracle, Paradox, and many other languages. With tens of thousands of ActiveX controls available, some free, some not, it's not difficult to find a control with the exact feature that you need for your latest project. Finding a "pure" .NET managed control is not always quite that simple. .NET and Visual Studio .NET go a long way toward making ActiveX controls very easy to use, but there are some pitfalls that may cause some confusion (and possibly some bad language). These obstacles are really minor, but when you run into them the first time, they are extremely frustrating. I'll try to guide you through most of these issues. No, we won't write an actual application, but once you have seen the problems you may encounter, you should be ready to go with the assurance that you can use most ActiveX controls with .NET. To illustrate the roadblocks ahead, I'll use the demo version of SftBox/ATL 3.0, an ActiveX ComboBox control. You can download the free demo from www.windowscontrols.com, but most any ActiveX control will do and could be used instead. I'll use Visual Studio .NET 2003 in this article and for the sample code, but using Visual Studio .NET 2002 is essentially the same and raises the same issues when dealing with an ActiveX control. Both versions fully support ActiveX controls, and the techniques shown here apply to both.
So far, so good. If your ActiveX control made it this far, we'll assume that it works okay with .NET. Just keep in mind that some ActiveX controls simply won't work with all development tools. For example, most tab controls that you may be familiar with from your Visual Basic 6 days do not work with anything but Visual Basic 6; some splitter controls also suffer from this limitation. But for the most part, a well-designed control that follows the ActiveX standards should offer little trouble. At this point, you'll most likely need some help. Actually, most of us would. Unless you know the control you are using extremely well, you'll probably need a little guidance in the form of online help. Not all of us manage to remember the hundreds of properties and methods a control offers. Finding the help file is not always easy. Older ActiveX controls or controls that have not been updated specifically to support .NET will not offer online help - at least it's not easily accessible. Many current ActiveX controls do, however, have excellent help support. When a control is selected or a property is edited, the Dynamic Help window automatically offers a number of suggested topics for the control and for Visual Studio .NET in general. Dynamic Help is a great way to access context-related help for any activity. You can access Dynamic Help using Control F1. For controls that don't offer Dynamic Help, you'll have to locate the help file. Often you'll find a program group for a control in your Start menu, which has an entry for online help. If the control offers property pages, you may be able to access online help there. Otherwise, you'll have to hunt for the help file. So far I've shown you how to add a control to the form and access online help. I skipped over a little detail when I told you to drop the control onto the form. Remember, this is an ActiveX control. .NET doesn't really "talk" directly to ActiveX controls. An ActiveX control is not a managed control; it uses unmanaged code. Visual Studio .NET creates a complete class wrapper for our ActiveX control, so to your application the control looks just like any other .NET managed control would. This class wrapper is called the runtime callable wrapper (RCW). Usually, this process is completely transparent and is a nice way to bring an ActiveX control to the .NET world. An ActiveX control will now look just like a .NET control - and in fact it is. The class wrapper creates a control derived from the AxHost class, which has the Control class as its base class. Where is the class wrapper? You don't actually see it. It is created, compiled, and deleted. You get only the Interop DLLs in your output directory. But for the curious (and sometimes the desperate), you can take a look at the class wrapper. The .NET Framework SDK has a utility that can generate the source (in addition to creating the DLLs). The AxImp utility can create the class wrapper for you. Its output is also included as part of the sample source available with this article. Use this command- line code to invoke the utility:
"C:program filesMicrosoft Visual Studio .NET It creates a source file full of quite boring properties and method definitions, as seen in Listing 1. For now, we won't actually need this class wrapper, but it will come in handy when we look at events and a few odd properties.
Whether you modify properties through the IDE's Properties window or through the control's custom property pages doesn't matter. All the properties you define at design time are nicely saved in a resource and are set at runtime by the form's InitializeComponent method (see Listing 2). It looks all too easy, and as usual in this business, it's the little details that get you. Let's start by adding three entries to the combo box and selecting the first entry (see Listing 3). As you enter this code, you'll notice that IntelliSense nicely offers all available properties and methods. The project actually runs without any problems, but it does look quite boring. First, let's spruce it up a bit by using a different foreground color for the first entry. In old-style VB, we would be able to say something like: SftBox1.Cell(index, 0).ForeColor = vbRed; Basically, we're accessing the Cell object, indexed by item index and column index, so we can manipulate its ForeColor property. Because of the class wrapper generated by .NET, it's not quite that simple. This seemingly simple construct can make a seasoned developer cringe when it needs to be translated to .NET. But you'll get the hang of it once you know what to look for. Simple properties are no problem. But a property such as "Cell", which requires additional parameters (i.e., index and column number), is changed by .NET into a method. So instead of the Cell property, you'll have to use the get_Cell and set_Cell methods. Actually set_Cell doesn't exist in this case, but you get the idea. So let's try again.
C#
VB Almost, but IntelliSense says ForeColor is of type uint or UInt32, and Red is a Color type. Fortunately, .NET has a ColorTranslator class, which comes in handy to convert between these types:
C#
VB Now let's add a nice little image to the first item. To make life easier, we'll add a PictureBox control from the Toolbox to the form, load an image into it, and we'll have access to the Image property of the PictureBox. Of course there are many other ways to get an image, but this is the easiest for our example. .NET thinks of bitmaps and images in terms of an Image object. ActiveX controls use the OLE Picture object (really an IPicture or IPictureDisp COM interface). These are truly worlds apart. In "VB6-speak" you would simply assign the image to the cell as in: Set SftBox1.Cell(index, 0).Picture = PictureBox1.Picture; A VB6 PictureBox happens to have the Picture property, which uses an OLE Picture object. .NET of course doesn't. Converting an Image object to an IPicture interface pointer seems difficult, if not impossible. At first, this is a real puzzler. A look at the class wrapper may help. It translated the MouseIcon property, which also uses an OLE Picture type, but it is exposed it as a System.Drawing.Image type. Upon inspection, it reveals the use of the GetIPictureFromPicture method.
this.ocx.MouseIcon = ((stdole.IPictureDisp)(GetIPicture This method takes an object of type Image and returns an IPicture interface pointer. That's exactly what we need, but unfortunately the method is protected, meaning it can only be used in a class derived from AxHost. So, we'll have to create a small helper class for this. Fortunately, we'll only have to do this once. All controls and projects can use the same helper class. The OLEConvert.cs and OLEConvert.vb source files included with the source code for this article show how this class is implemented (see Listing 4). All of the code referenced in this article can be downloaded from www.sys-con.com/dotnet/sourcec.cfm. The ToOLEPic function is used to easily convert from an Image object to an OLE Picture type.
C#
VB The complete code to add three entries, highlighting the first with color and an image, now looks as shown in Listing 5 (Listings 5-8 can be downloaded from www.sys-con.com/dotnet/sourcec.cfm.) While entering code, you may have noticed that IntelliSense also offers a let_Picture method. Usually, a picture property offers two forms. The first form assigns a reference to a picture object (Picture property), the second form assigns a complete copy of the image to the picture property (let_Picture method). Unless you want to create actual copies of images, the use of let_Picture should be minimal. Fonts also need somewhat special treatment. .NET knows the Font type, but ActiveX controls insist on the OLE Font type (a IFontDisp COM interface). The generated class wrapper again offers some assistance and handles the control's Font property, which accepts a Font type and translates it to the ActiveX control's preferred IFontDisp interface pointer, so we don't have to worry about this case (see Listing 6). Unfortunately, other Font properties, such as the cells' Font properties, need our help. Our helper class that we implemented to better handle images can also help with fonts. The ToOLEFont function converts a Font object to an IFontDisp interface pointer (see Listing 7). Dealing with images and fonts can be difficult at first, but using these techniques should make it quite straightforward. Of course, you'll still have to write the application, but at least working with ActiveX controls is now a bit easier. Because .NET uses namespaces, even setting a simple property can end up becoming a typing effort.
axSftBox1.Items.Style = Don't forget that you can use a using or Imports statement even for ActiveX controls. This will reduce the clutter. If you add a "using SftBoxLib30;" or an "Imports SftBoxLib30" statement to your source file, assigning a property becomes: axSftBox1.Items.Style = SftBoxItemsStyleConstants.itemsStyleSftBoxVariable; Events are a bit surprising in the .NET world. If you are used to Visual Basic or Visual C++, you expect event parameters to arrive as part of the event handler. For example, in Visual Basic 6 you would see a KeyDown event defined like this: Private Sub object_KeyDown(KeyCode As Integer, ByVal Shift As Integer) In .NET, every event handler has the same "signature" with exactly two arguments:
C#
VB All the arguments that are specific to the event are passed in the "e" object. "e" is an object of class EventArgs or a derived class. You'll find all the different event classes listed in the class wrapper generated by AxImp. But generally, you really won't have to look there. If you need a specific argument, you can use IntelliSense by typing "e." and it will offer all available arguments. You should have no problem matching these up against the documented arguments in the online help. The generated class wrapper usually changes the argument's first letter to lowercase. In this example you would find an e.keyCode and an e.shift member as part of the class derived from EventArgs (see Listing 8). At last, depending on the ActiveX control you are using, you may find that a method, property, or event is missing or may be in conflict with one of its base classes. (Remember, your ActiveX control is derived from the Control and AxHost base classes.) For example, the ComboBox control's "RightToLeft" property is nowhere to be found. With the generated class wrapper in hand, you can quickly find that the property has become "CtlRightToLeft" instead. .NET prefixes conflicting or reserved names with "Ctl". If a control has a "Refresh" method, it is renamed to "CtlRefresh", to distinguish it from the Refresh method defined in the Control base class. Some events receive an "Event" suffix. For example, an event name "KeyDown" (as defined by the ActiveX control) becomes the "KeyDownEvent".
Conclusion Reader Feedback: Page 1 of 1
Your Feedback
Latest Cloud Developer Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week
Breaking Cloud Computing News
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||