# Monday, January 16, 2006

IMPORTANT NOTE: More information regarding the proper registration of this control is provided in my next blog entry.

Not Supported
Creating ActiveX controls in Visual Studio 2005 for use in Visual FoxPro is not officially supported by Microsoft. Now with that out of the way, let's see what we can do (see screen shots at the bottom of this blog entry).

Superior + Superior = 2XSuperior
Visual FoxPro is generally superior to .NET in working with data. .NET is generally superior to Visual FoxPro when it comes to controls and visual eye candy. So, the idea is to marry the two and enjoy the benefits that each has to offer. Now before either side starts flaming me about how VFP can be visually appealing or .NET can work with data, you should know that I don't care (LOL). The arguments are played out, and a waste of time. Better to spend our time sharing really cool code, extending Visual FoxPro in every way possible, and getting ready for Sedna.

Great articles on this topic
There are some really good articles about .NET interop that will hopefully help those of you not familiar with the topic to come up to speed. The first three links are articles that were done by Rick Strahl and relate specifically to .NET interop as it pertains to Visual FoxPro. The last two articles were the basis for what I'm about to show.

  1. Passing objects between FoxPro and .NET COM Components
  2. Handling .NET Events in FoxPro via COM Interop
  3. Creating multi-threaded components .NET for COM Interop with Visual FoxPro
  4. HOW TO: Sink Managed C# Events in Internet Explorer Script
  5. Exposing Windows Forms Controls as ActiveX controls

Adventures of trying to create a .NET UserControl for use in Visual FoxPro
One of the things that I like about .NET is its toolstrip and menustrip controls, and given the popularity of the ARG-Software's Command Bars Library among Visual FoxPro developers I'd say I'm not the only one. They're just really professional looking. So, I decided that I wanted to create a menustrip and a toolstrip in Visual Studio .NET and use it on a Visual FoxPro form. The first thing I ran into was a bunch of information on the internet that said it wasn't possible to create a visual ActiveX control in .NET and use it in an environment like Visual FoxPro or Visual Basic 6.0. But as the search continued, I finally uncovered the page that is link #5 in the list above.

So it is possible. Next thing to work out was how to get events to fire over in Visual FoxPro. Unfortunately I ran into the page that is link #4 above before I ran into Rick Strahl's article on it (link #2 above). Oh well, while Rick's article would have saved me some time, it was fairly straight forward to implement looking at the steps and C# code at link #4 above even if the content was not purported to be for Visual FoxPro. Same concept, same end result.

Abridged version of what to do in C#

  1. Create a new Class Library project that consists of a UserControl
  2. Throw a toolstrip and a menustrip in your UserControl
  3. Set the modifier property for each to Public so you can see them from Visual FoxPro
  4. At the top of your source file add the following using statments:

    using System;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Reflection;
    using Microsoft.Win32;
  5. In the namespace block of your control code define delegates for the events like this:

    namespace DotNetControl
    {
    [
    ComVisible(false)]
    public delegate void MyClick1EventHandler();
    [ComVisible(false)]
    public delegate void MyClick2EventHandler();
    ...
  6. Define a public interface to expose the events of your UserControl class in Visual FoxPro like this (Notice the Guid, InterfaceType, and DispId attributes - you'll need to generate your own Guid):

    [GuidAttribute("F11EC17C-59E1-42D1-AE96-712AB79F20AB")]
    [
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ControlEvents
    {
    [
    DispIdAttribute(1)]
    void MyClick1();
    [DispIdAttribute(2)]
    void MyClick2();
    }
  7. The beginning of your class definition should look something like the following (Notice the Guid, ProgId, ClassInterface and ComSourceInterfaces - generate your own Guid and make sure the parameter sent to typeof is the same as the public interface you created in step 6) :

    [Guid("2DEFE6F9-6738-454C-BEF1-CF944E7F5F06")]
    [
    ProgId("DotNetControl.DotNetToolbar")]
    [
    ClassInterface(ClassInterfaceType.AutoDual), ComSourceInterfaces(typeof(ControlEvents))]
    public partial class DotNetToolbar : UserControl
    {
    public DotNetToolbar()
    {
    InitializeComponent();
    }
    ...
  8. Specify your events

    event DotNetControl.MyClick1EventHandler MyClick1;
    event DotNetControl.MyClick2EventHandler MyClick2;

  9. In the designer double-click on the controls that you are exposing events for and you will end up with some method code blocks like the following being added (Notice I have added some simple if condition blocks to call the events we defined in step #8) :

    private void myToolStripButton_Click(object sender, EventArgs e)
    {
    if (MyClick1 != null)
    {
    MyClick1();
    }
    }

    private void myToolStripMenuItem_Click(object sender, EventArgs e)
    {
    if (MyClick2 != null)
    {
    MyClick2();
    }
    }

  10. Override the Dispose method:

    protected override void Dispose(bool disposing)
    {
    if (disposing)
    {
    if (components != null)
    components.Dispose();
    }
    base.Dispose(disposing);
    }

  11. Add the following method to your class (Notice since it has been attributed with ComRegisterFunction this code will fire when the assembly is registered for COM interop)

    //copyright Morgan Skinner, 2001
    [ComRegisterFunction()]
    public static void RegisterClass(string key)
    {
    // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
    StringBuilder sb = new StringBuilder(key);
    sb.Replace(
    @"HKEY_CLASSES_ROOT\", "");
    // Open the CLSID\{guid} key for write access
    RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);
    // And create the 'Control' key - this allows it to show up in
    // the ActiveX control container
    RegistryKey ctrl = k.CreateSubKey("Control");
    ctrl.Close();
    // Next create the CodeBase entry - needed if not string named and GACced.
    RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
    inprocServer32.SetValue(
    "CodeBase", Assembly.GetExecutingAssembly().CodeBase);
    inprocServer32.Close();
    // Finally close the main key
    k.Close();
    }

  12. Add the following method to your class (Notice since it has been attributed with ComUnRegisterFunction this code will fire when the assembly is unregistered)

    //copyright Morgan Skinner, 2001
    [
    ComUnregisterFunction()]
    public static void UnregisterClass ( string key )
    {
    StringBuilder sb = new StringBuilder ( key ) ;
    sb.Replace(
    @"HKEY_CLASSES_ROOT\","") ;
    // Open HKCR\CLSID\{guid} for write access
    RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);
    // Delete the 'Control' key, but don't throw an exception if it does not exist
    k.DeleteSubKey ( "Control" , false ) ;
    // Next open up InprocServer32
    RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true ) ;
    // And delete the CodeBase key, again not throwing if missing
    k.DeleteSubKey ( "CodeBase" , false ) ;
    // Finally close the main key
    k.Close ( ) ;
    }

  13. In your solution's properties do the following:

  • Checkmark the "Register for COM interop" checkbox on the Build page
  • Checkmark the "Make assembly COM-visible" checkbox in the Assembly Information screen accessible from the Application page (alternately you can just open up the AssemblyInfo.cs file and make sure it says:
    [assembly: ComVisible(true)]

After you've completed these steps, you can build your assembly and you should have a useable ActiveX control for use in environments such as Visual FoxPro and Visual Basic 6.0. You may see some warning regarding non COM visible value types during the build, but there shouldn't be any errors and the assembly should be in your bin directory. If you get stuck on any of the steps above, you can always refer to C# solution I am including in the download at the bottom of this page.

How to use the ActiveX in Visual FoxPro
Once you have built your ActiveX control you can then proceed to use it in Visual FoxPro. If you aren't building the control (which will register the control for COM interop on your system) and just want to use one like I made, you'll have to register the DLL with REGASM. The same holds true for when you distribute the control. You'll either need to include it in an installer package that was created with a program that understands how to register .NET assemblies or you'll have to use regasm on the user's system. If you've never heard of Regasm before this, you can think of it as very much like RegSvr32.

In any event, for the control that I've created you'll want to follow the following steps in Visual FoxPro:

  1. Make sure the .NET assembly (DLL) has been registered on your system
  2. Go to Tools->Options and on the resultant Options screen go to the Controls page, select ActiveX and scroll down through the list of ActiveX controls until you find the .NET control you want. In the case of the control I built, it will be named DotNetControl.DotNetToolbar. Place a checkmark next to it and then click OK
  3. Create a new form and on the Form Controls toolbar click the View Classes dropdown button (looks like library books) and select ActiveX controls.
  4. Select the button that is provided for the DotNetToolbar and place an instance of it on your form
  5. Resize and position the resultant menu/toolbar at the top of your form and set its Anchor property to 11.
  6. Go into the methods of the control and add whatever code you want to the ToolClick and MenuClick events (such as toolnewclick, toolsaveclick, menuexitclick, menuhelpclick, etc.)
  7. Run your form.

I am providing a sample form in the download for this blog entry. I haven't done much with those events except add messageboxes to show that they fire. Also, I've provided an optiongroup that will set the RenderMode of the toolstrip and menustrip just to show that we aren't limited to events that we have exposed through the interface. Public objects and their properties are accessible as well.

Just the beginning of what's possible
In this blog entry I am merely showing that it is possible to create .NET ActiveX controls for use in Visual FoxPro. More is possible, such as starting with an empty menustrip and empty toolstrip and allowing the Visual FoxPro developer to add items to the .NET controls dynamically. Plus, the toolstrip and menustrip are but two examples of many other controls that could be made for use in Visual FoxPro. Extending Visual FoxPro by utilizing the .NET framework is powerful, and the chief reason I wholeheartedly support the Sedna initiative put forward by the Micorosft Fox Team. It was, in my opinion, the right decision based on the resources available and the benefits to be gained. Coupling Visual FoxPro's unmatched data handling abilities with the best that .NET has to offer is a very powerful combination.

Source download and the screen shots

Download Source and Example VFP Form (51 KB approx.)

Note: In the download the example form is contained in the VFPExampleForm subdirectory and the .NET control is contained in the bin\Release subdirectory. The .NET control MUST be registered using Regasm before you can run the VFP example form.

 


.NET menustrip and toolstrip look professional


Menus have great theme support


Resizing the form will cause toolstrip to show dropdown button


Using the optiongroup the RenderMode can be changed
Monday, January 16, 2006 2:17:10 PM (GMT Standard Time, UTC+00:00)  #    Comments [18]

 

Archive

<July 2014>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789