ActiveX control (OCX) in a WPF window
Posted by binglongx on October 25, 2010
Putting an ActiveX, a.k.a, OCX control, to a WPF window is somewhat similar to an OCX in a Visual C# Windows Form, but there are some differences.
Unlike a Windows Form, a WPF window cannot embed an OCX directly, because arbitrary OCX does not follow the WPF rules in sharing the screen real estate.
You must create a System.Windows.Forms.Integration.WindowsFormsHost object as a host, put your OCX as a child to the host, then add the host as a child to a content container, such as a Grid object, in a WPF window.
However, the WindowsFormsHost host object only takes System.Windows.Forms.Control objects as a child. To make your OCX a System.Windows.Forms.Control, you will need to create a dummy Windows Forms Control Library project as hoster project, and let Visual Studio create this .NET ActiveX wrapper from OCX by using the OCX, i.e., putting an OCX in the custom control in that project. Then you can find generated the .NET ActiveX wrapper dll.
- OCX project: This is the project featuring high performance, low level, omni power ….(add whatever words here). In another word, you can do whatever your WPF stuff couldn’t do here.
- I create a new project from Visual C++ | MFC | MFC ActiveX Control. You can use any other choices, for example, another language. The type information is conveyed externally through the COM system only. There is no direct link between this project and the rest in your Visual Studio solution. You many still want to set dependency between the projects for the building order in the same solution. In my case, I use Class Wizard to add a few methods and properties to the _D<OCX> interface and implement them in the corresponding <OCX>Ctrl.cpp. Building this project will also registers the OCX to the Windows COM registry, and that’s enough.
- Create a new project from Visual C# | Windows | Windows Forms Control Library.
- As a good habit, rename UserControl1.cs to a better name (I use the project name, since this is the only important file in the project anyway)
- Add a reference to the OCX in COM category (not to the OCX project, you cannot add to C# project a reference to C++ project). You should see something reference like <OCX>Lib in your Solution Explorer. If you check the file system, you should find Interop.<OCX>Lib.dll. This .NET assembly has the .NET representation of your OCX type information. Unfortunately, the types have nothing to do with System.Windows.Forms.Control yet.
- Bring the design view of your WFCL control (empty right now), in the Toolbox, right click | Choose Items…, then choose COM Components and add the OCX to the Toolbox.
- Drag the OCX in the Toolbox to your WFCL control. You should see Ax<OCX>Lib is added as a reference to your hoster project and you can find corresponding obj\AxInterop.<OCX>Lib.dll. That’s the thing we really want. If you check the reference, you should find Ax<OCX>Lib.Ax<OCX>, which is derived from System.Windows.Forms.Control through AxHost.
- As a good habit, set the OCX control’s Dock property to Fill in your WFCL control, so in the future you can always see it there.
- Build this project, and you will get bin\Debug\AxInterop.<OCX>Lib.dll, or bin\Release\AxInterop.<OCX>Lib.dll copied from obj\.
- Add reference to WindowsFormsIntegration (sort by Component Name in .NET category to find it). Its WindowsFormsHost is the glue between WPF and Windows Forms.
- Add reference to System.Windows.Forms (sort by Component Name in .NET category to find it). Its Control class is the base class of your OCX wrapper class.
- Add reference to AxInterop.<OCX>Lib.dll, by Browsing to WFCL\bin\Debug (or WFCL\bin\Release, they are the same since they only have the type information). This has the .NET wrapper class of your OCX.
- Design your WPF window to have a content container that would be occupied by the OCX, for example a Grid.
- In your WPF window’s Load event handler, add the code to put the OCX to the container. Since I have different OCXs, I write a generic AddAxObject() method for all kinds of OCXs, see code below.
3 // add an ActiveX object to the container with a WindowsFormsHost hoster
4 private Ax addAxObject<Ax>(Grid container)
5 where Ax : System.Windows.Forms.Control, new()
7 //Create the ActiveX/OCX control
8 Ax ax = new Ax();
9 //Create the interop host control.
10 System.Windows.Forms.Integration.WindowsFormsHost hoster
11 = new System.Windows.Forms.Integration.WindowsFormsHost();
12 //Assign the ActiveX control as the host control’s child.
13 hoster.Child = (System.Windows.Forms.Control)ax;
14 //Add the interop host control to Grid control’s collection of child controls
16 return ax;
19 public AxCameraCaptureLib.AxCameraCapture m_axCamera;
20 public AxModel3DLib.AxModel3D m_axModel3d;
22 private void Window_Loaded(object sender, RoutedEventArgs e)
24 m_axCamera = addAxObject<AxCameraCaptureLib.AxCameraCapture>(gridLiveCamera);
25 m_axModel3d = addAxObject<AxModel3DLib.AxModel3D>(gridModel3D);
26 m_axCamera.Start(); // now you can call OCX’s methods.
Changing OCX Implementation
If you change the implementation of the OCX without changing its IDL, you may see error messages when the linker tries to delete and write to the ocx DLL. This is because the OCX is loaded by Visual Studio if some design window housing the OCX is opened. Simply closing these design windows does not unload the OCX. To be able to build the OCX, you will have to close all the involved design views in Visual Studio (otherwise when the solution restarted these views will be opened automatically), then close Visual Studio and restart it.
Changing OCX Interface
If you change your OCX’s interface (i.e., the IDL file, no matter through Class Wizard or not), in addition to do the work above in changing OCX implementation, you will need to manually update the Ax<OCX>Lib and <OCX>Lib references. This is because an OCX’s interface is considered to be relatively stable. Visual Studio does not monitor the COM registry entries. If you change the IDL, the .NET wrapper must reflect the change. What you need to do is:
- OCX project:
- Rebuild and/to register your new OCX interface to COM system.
- Delete the OCX from your custom control in design view
- Delete the Ax<OCX>Lib and <OCX>Lib references from the project
- Delete the OCX from Toolbox
- Add OCX as COM reference to the project
- Add OCX as COM Component to Toolbox
- Drag the OCX to the custom control in design view
Due to the work involved in WFCL hoster project, you should try to design the OCX interface as stable as possible. You will not like to do the steps again and again, in the middle you can often see Visual Studio show nasty error messages when you try to see the design views but it could not load the OCX.