Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Hosting WPF Content in a Java Application

4.94/5 (8 votes)
26 Apr 2009CPOL4 min read 74.3K   3.1K  
Demonstrates a simple technique for embedding WPF/.NET Components into Java GUI
Image 1

Introduction

This is yet another article on Java/.NET integration.

Background

The main ideas of Java /.NET integration are described in [4]. There I provide a code sample for embedding .NET GUI components into Java GUI. If the reader is not familiar with Object-Oriented JNI for .NET, I recommend reading this article first. For this demo project, I used the same WPF component as [1].

Integrating Java Application with WPF/.Net Component Modules

The main ideas of Java and .NET code integration described in [4] where I showed an example of embedding .NET GUI components into Java interface. For anybody who is not familiar with Object-Oriented JNI for .NET, I recommend reading this article first. Description of Windows Presentation Foundation (WPF) can be found in MSDN, special manuals and is also not discussed in this article. WPF Module used in my example (without sources) was borrowed from [1]. Here I set out only with the task of embedding WMF Component into Java GUI.

Writing Java Code

From Java side, any WPF/.NET component is a piece of native code that should be wrapped with some Java classes. Special interface in Java is dealing with WPF/.NET components. Interface must include methods for:

  • creating of component instances (create)
  • destroying instances when Java code exits (dispose)

In the attached sample code, I embed WPF Clock component [1] into Java GUI. Method updateClock sets new Date/Time. Arbitrary native GUI element can be embedded into Java user interface components with java.awt.Canvas class as a base Java UI component class. java.awt.Canvas is a Java Component with Windows, i.e. a heavy-weight component, that does not implement the paint method. Class com.oojni.WPFContainer inherits from java.awt.Canvas. Any WPF component is windowless and must be added into a .NET Container, which is bound to window of java.awt.Canvas class.
In the provided sample of Java code, there are placeholders used as interops for .NET components. The interop for WPF Component is com.oojni.WPFContainer class:

Java
package com.oojni;
import java.awt.Canvas;

/**
 * The interop for WPF Container
 * 
 * @author Vitaly Shelest
 */
public class WPFContainer extends Canvas {
    static{
        System.loadLibrary("oojni.net20"); 
        System.loadLibrary("JavaHostWPF");
    }
    /**
     * Stores a reference to .NET Container
     */
    int ref = 0;
    /**
     * The place to create .NET Container
     */
    public void addNotify() {
        super.addNotify();
        ref = create(this);
    }
    /**
     * Used to dispose .NET Container
     */
    public void removeNotify() {
        dispose(ref);
        super.removeNotify();
    }
    /**
     * Disposes .NET Container
     */
    public void dispose()
    {
        if(ref != 0)
            dispose(ref);
        ref = 0;
    }
    /**
     * Updates WPF Clock with a new time/date selection
     */
    void updateClock()
    {
        updateClock(ref);
    }
    /**
     * Updates WPF Clock
     * @param ref reference to .NET Container
     */
    native void updateClock(int ref);
    /**
     * Creates .NET Container
     *   @param parent Java component as a placeholder for .NET Container
     * @return reference to .NET Container
     */
    native int create(Object parent);
    /**
     * Disposes .NET Container
     * @param ref reference to .NET Container
     */
    native void dispose(int ref);
}

This class overrides two java.awt.Canvas methods:

  • addNotify, called by JVM while creating java.awt.Canvas instance. This is the place where a native WPF/.NET Component container can be created, the native create method returns some integer value (I call it a .NET component reference). In .NET code this value is converted into WPF/.NET Component container instance when a native method is executed.
  • removeNotify, called by JVM on destroy of java.awt.Canvas instance. In this method, WPF/.NET Component container is destroyed with the native method dispose.
  • The same interop was designed for Calendar Container - com.oojni.CalendarContainer. The rest of the code implements the usual SWING GUI of Java application.

.NET JNI Module Design

In .NET JNI code, I implement methods that:

  • Acquire handle of window hWnd from Java component. Handle hWnd is further utilized
  • Create .NET Component Container instance in the native implementation of the create method and adds hWnd of this .NET Component Container as a child window of the Java component
  • Fill .NET Component Container instance with .NET GUI components

To develop native module JavaHostWPF.dll, I used [3].

Image 2This chart demonstrates the relation between Java and .NET/WPF components. In .NET module, native methods of Java WPFContainer class are implemented as regular .NET methods. The method created binds HWND of Java WPF Container to .NET WPF Container class instance. This class can accept .NET Components. The hosting of WPF Component in .NET WPF Container is implemented as described in MSDN: .NET WPF Container -> HwndSource -> WpfComponent. The only problem is that WPF Components must run in STA Thread Context. To meet this requirement, .NET WPF Container instance is running in separate STA. Java code calls are executed in the context of this thread.

Here is the implementation of the com.oojni.WPFContainer native method create:

Java
/// <summary>
/// Implements Native Method create that creates Component Container reference
/// </summary>
/// <param name="parent">com.oojni.CalendarContainer instance</param>
/// <returns>WPF Clock Container reference</returns>
public override int create(ObjectOrientedJNI.JObject parent) {
    WPFCreator creator = new WPFCreator(parent);
    thread = new System.Threading.Thread
		(new System.Threading.ThreadStart(creator.Create));
    thread.SetApartmentState(System.Threading.ApartmentState.STA);
    thread.Start();
    creator.autoEvent.WaitOne();
    GlobalReference gref = new GlobalReference(creator.Control, true);
    return gref.Reference.ToInt32();
}

In STA Thread, I call create method of ObjectOrientedJNIInterop.com.oojni.WPFCreator class instance where .NET WPF Container (class ObjectOrientedJNIInterop.com.oojni.WPFControl) is created. In OnLoad Event of ObjectOrientedJNIInterop.com.oojni.WPFControl I create WPF Clock component:

Java
private void WPFControl_Load(object sender, EventArgs e)
{
    // Create WPF Clock Component instance here.
    // First wrap HWND of Java Placeholder.
    ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy canvas = 
	new ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy(parent);
    // Initialize create parameters
    System.Windows.Interop.HwndSourceParameters sourceParams = 
	new System.Windows.Interop.HwndSourceParameters("JavaWPFApp");
    // Set Size and Location of WPF Clock
    sourceParams.PositionX = 0;
    sourceParams.PositionY = 0;
    sourceParams.Height = canvas.getHeight();
    sourceParams.Width = canvas.getWidth();
    sourceParams.ParentWindow = this.Handle;
    sourceParams.WindowStyle = WS_VISIBLE | WS_CHILD;
    // Wrap EmbeddedFrame HWND, this component is embedded into Java Placeholder
    hwndSource = new System.Windows.Interop.HwndSource(sourceParams);
     // Set Date/Time to WPF Clock and put WPF Clock component into HwndSource
    DateTime tm = DateTime.Now;
    clock = new WPFControls.AnimClock();
    clock.ChangeDateTime(tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second);
    System.Windows.FrameworkElement myPage = clock;
    hwndSource.RootVisual = myPage;
}

This piece of .NET code shows how to call a Java native method in STA Thread context.

Java
delegate void UpdateClock(int peer);
/// <summary>
/// Implementation of native method updateClock
/// </summary>
/// <param name="peer">WPF Clock Container reference</param>
public override void updateClock(int peer)
{
    // Restore the instance of WPF Clock Container
    GlobalReference gr = new GlobalReference(peer, true);
    object o = gr.Object;
    // Call updateClock in STA Thread Context
    if (((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).InvokeRequired)
        ((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).Invoke
	(new UpdateClock(updateClock), new object[] { peer });
    else
    {
        // Initialize WPF Clock Component with new values
        ((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).clock.ChangeDateTime
	(CalendarCreator.currentDateTime.Year, CalendarCreator.currentDateTime.Month, 
	CalendarCreator.currentDateTime.Day, CalendarCreator.currentDateTime.Hour, 
	CalendarCreator.currentDateTime.Minute, 
	CalendarCreator.currentDateTime.Second);
        System.Windows.FrameworkElement myPage = 
		((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).clock;
        		((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).
					hwndSource.RootVisual = myPage;
    }
}

Prerequisites to Run the Example Attached

Development Tools

  • Microsoft Visual Studio 2005 or above
  • Microsoft .NET Framework 3.0 or above
  • Java SUN/IBM 1.6 and above
  • Java SUN/IBM 1.3 and above to recompile Java sources

Operating Systems

  • Windows Vista
  • Windows XP SP2
  • Windows Server 2003 SP1

References

  1. Choong,K.,2008, Hosting WPF Content in an MFC Application [Online], CodeGuru
  2. Shelest,V.,2007, OOJNI Add-in .NET (C#) for VS2005/2008 [Online], Simtel
  3. Shelest,V.,2006, OOJNI for .NET2.0 (low-level) [Online], Simtel
  4. Shelest,V.,2006, Java/.NET Integration as Simple as Possible [Online], The Code Project

Thanks

My big thanks to Ilya Shpilberg who gave me valuable advice while writing this article.

History

  • 26th April, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)