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;
public class WPFContainer extends Canvas {
static{
System.loadLibrary("oojni.net20");
System.loadLibrary("JavaHostWPF");
}
int ref = 0;
public void addNotify() {
super.addNotify();
ref = create(this);
}
public void removeNotify() {
dispose(ref);
super.removeNotify();
}
public void dispose()
{
if(ref != 0)
dispose(ref);
ref = 0;
}
void updateClock()
{
updateClock(ref);
}
native void updateClock(int ref);
native int create(Object parent);
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].
| 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
:
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)
{
ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy canvas =
new ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy(parent);
System.Windows.Interop.HwndSourceParameters sourceParams =
new System.Windows.Interop.HwndSourceParameters("JavaWPFApp");
sourceParams.PositionX = 0;
sourceParams.PositionY = 0;
sourceParams.Height = canvas.getHeight();
sourceParams.Width = canvas.getWidth();
sourceParams.ParentWindow = this.Handle;
sourceParams.WindowStyle = WS_VISIBLE | WS_CHILD;
hwndSource = new System.Windows.Interop.HwndSource(sourceParams);
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);
public override void updateClock(int peer)
{
GlobalReference gr = new GlobalReference(peer, true);
object o = gr.Object;
if (((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).InvokeRequired)
((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).Invoke
(new UpdateClock(updateClock), new object[] { peer });
else
{
((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