Hosting WPF Content in a Java Application






4.94/5 (8 votes)
Demonstrates a simple technique for embedding WPF/.NET Components into Java GUI

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:
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 creatingjava.awt.Canvas
instance. This is the place where a native WPF/.NET Component container can be created, the nativecreate
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 ofjava.awt.Canvas
instance. In this method, WPF/.NET Component container is destroyed with the native methoddispose
.- 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. HandlehWnd
is further utilized - Create .NET Component Container instance in the native implementation of the
create
method and addshWnd
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].
This 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
:
/// <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:
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.
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
- Choong,K.,2008, Hosting WPF Content in an MFC Application [Online], CodeGuru
- Shelest,V.,2007, OOJNI Add-in .NET (C#) for VS2005/2008 [Online], Simtel
- Shelest,V.,2006, OOJNI for .NET2.0 (low-level) [Online], Simtel
- 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