Внедрение содержимого WPF в приложение Java
· Скачать демонстрационный проект - 130.98 Кб
· Скачать исходный код - 37.93 Кб
Введение
Это очередная статья об объединении Java/.NET.
Вводные сведения
Здесь дается пример кода для внедрения компонентов .NET GUI в Java GUI. Если читатель не знаком с объектно-ориентированным JNI (интерфейс для прямого доступа из Java) для .NET, желательно сначала прочитать эту статью.
Описание основы презентаций Windows (WPF) можно найти в MSDN (сеть для разработчиков Microsoft), специальных руководствах и в данной статье не обсуждается. Здесь излагается только задача внедрения компонента WMF в Java GUI.
Написание кода Java
С точки зрения Java любой компонент WPF/.NET – кусок внутреннего кода, который должен быть обернут некоторыми классами Java. Специальный интерфейс в Java имеет дело с компонентами WPF/.NET. Интерфейс должен включать методы для:
• создания экземпляров компонента (создать)
• уничтожения экземпляров, когда код Java завершает работу (удалить)
В приложенном примере кода компонент часов WPF [1] внедрен в Java GUI. Метод updateClock устанавливает новую дату/время. Произвольный внутренний элемент GUI может быть внедрен в компоненты пользовательского интерфейса Java с классом java.awt.Canvas в качестве базового класса компонента пользовательского интерфейса Java.
java.awt.Canvas - компонент Java с окнами, т.е. тяжелый компонент, не реализующий метод paint. Класс com.oojni.WPFContainer наследуется от java.awt.Canvas. Любой компонент WPF не имеет окон и должен быть добавлен в контейнер .NET, связанный с окном класса java.awt.Canvas.
В предоставленном примере кода Java есть метки-заполнители, используемые в качестве функциональной совместимости interop для компонентов .NET. Функциональная совместимость interop для компонента WPF - класс com.oojni.WPFContainer:
package com.oojni;
import java.awt.Canvas;
/**
* Интероп для контейнера WPF
*
* @автор Vitaly Shelest
*/
public class WPFContainer extends Canvas {
static{
System.loadLibrary("oojni.net20");
System.loadLibrary("JavaHostWPF");
}
/**
* хранит ссылку на контейнер .NET
*/
int ref = 0;
/**
* здесь создается контейнер .NET
*/
public void addNotify() {
super.addNotify();
ref = create(this);
}
/**
* используется для удаления контейнера .NET
*/
public void removeNotify() {
dispose(ref);
super.removeNotify();
}
/**
* удаляет контейнер .NET
*/
public void dispose()
{
if(ref != 0)
dispose(ref);
ref = 0;
}
/**
* обновляет часы WPF с помощью новой выборки время/дата
*/
void updateClock()
{
updateClock(ref);
}
/**
* обновляет часы WPF
* @параметр ref ссылается на контейнер .NET
*/
native void updateClock(int ref);
/**
* создает контейнер .NET
* @параметр родительский компонент Java как метка-заполнитель для контейнера .NET
* @возврат ссылки на контейнер .NET
*/
native int create(Object parent);
/**
* удаляет контейнер .NET
* @параметр ref ссылка на контейнер .NET
*/
native void dispose(int ref);
}
Данный класс переопределяет два метода java.awt.Canvas:
• addNotify, вызываемый JVM (виртуальная машина Java) при создании экземпляра java.awt.Canvas. Это место, в котором может быть создан контейнер внутреннего компонента WPF/.NET, внутренний метод create (создать) вернет некоторое целое значение (называемое ссылкой на компонент .NET). В коде .NET это значение преобразуется в экземпляр контейнера компонента WPF/.NET, когда выполняется внутренний метод.
• removeNotify, вызываемый JVM при уничтожении экземпляра java.awt.Canvas. В данном методе контейнер компонента WPF/.NET уничтожается с помощью внутреннего метода dispose.
• Такой же интероп был разработан для контейнера календаря - com.oojni.CalendarContainer. Остальная часть кода реализует обычный SWING GUI приложения Java.
Конструкция модуля .NET JNI
В коде .NET JNI реализованы методы, которые:
• Извлекает дескриптор окна hWnd из компонента Java. Дескриптор hWnd используется позднее
• Создает экземпляр контейнера компонента .NET во внутренней реализации метода create и добавляет hWnd данного контейнера компонента .NET в качестве дочернего окна компонента Java
• Заполняет экземпляр контейнера компонента .NET компонентами .NET GUI
Для разработки внутреннего модуля JavaHostWPF.dll был использован.
Данная схема показывает связь между компонентами Java и .NET/WPF. В модуле .NET внутренние методы класса Java WPFContainer реализованы как стандартные методы .NET. Созданный метод связывает HWND контейнера Java WPF с экземпляром класса .NET WPF Container. Этот класс может принимать компоненты .NET. Размещение компонента WPF в .NET WPF Container реализовано в соответствии с описанным в MSDN: .NET WPF Container -> HwndSource -> WpfComponent. Единственная проблема в том, что компоненты WPF должны выполняться в контексте потока STA. Чтобы выполнить данное требование, экземпляр контейнера .NET WPF выполняется в отдельном STA. Вызовы кода Java выполняются в контексте данного потока.
Ниже приводится реализация внутреннего метода create com.oojni.WPFContainer:
/// <summary(аннотация)>
/// Реализует внутренний метод create, создающий ссылку на контейнер компонента
/// </summary>
/// <param name="parent">экземпляр com.oojni.CalendarContainer</param>
/// <returns>ссылка на контейнер часов WPF</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();
}
В потоке STA вызывается метод create экземпляра класса ObjectOrientedJNIInterop.com.oojni.WPFCreator, в котором создается контейнер .NET WPF (класс ObjectOrientedJNIInterop.com.oojni.WPFControl). В событии OnLoad (после загрузки) ObjectOrientedJNIInterop.com.oojni.WPFControl создается компонент часов WPF:
private void WPFControl_Load(object sender, EventArgs e)
{
// здесь создается экземпляр компонента часов WPF.
// сначала обертывается HWND метки-заполнителя Java.
ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy canvas =
new ObjectOrientedJNIInterop.java.awt.CanvasJNIProxy(parent);
// инициализируются параметры create System.Windows.Interop.HwndSourceParameters sourceParams =
new System.Windows.Interop.HwndSourceParameters("JavaWPFApp");
// задаются размер и местоположение часов WPF
sourceParams.PositionX = 0;
sourceParams.PositionY = 0;
sourceParams.Height = canvas.getHeight();
sourceParams.Width = canvas.getWidth();
sourceParams.ParentWindow = this.Handle;
sourceParams.WindowStyle = WS_VISIBLE | WS_CHILD;
// обертывается EmbeddedFrame HWND, этот компонент внедряется в метку-заполнитель Java
hwndSource = new System.Windows.Interop.HwndSource(sourceParams);
// присваивается дата/время часам WPF, и компонент часы WPF вставляется в 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;
}
Данный кусок кода .NET показывает, как вызывать внутренний метод Java в контекст потока STA.
delegate void UpdateClock(int peer);
/// <summary>
/// реализация внутреннего метода updateClock
/// </summary>
/// <param name="peer">ссылка на контейнер часов WPF</param>
public override void updateClock(int peer)
{
// восстанавливается экземпляр контейнера часов WPF
GlobalReference gr = new GlobalReference(peer, true);
object o = gr.Object;
// вызывается updateClock в контексте потока STA
if (((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).InvokeRequired)
((ObjectOrientedJNIInterop.com.oojni.WPFControl)o).Invoke
(new UpdateClock(updateClock), new object[] { peer });
else
{
// инициализируется новыми значениями компонент часов WPF ((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;
}
}
Необходимые условия для запуска приложенного примера
Средства разработки
• Microsoft Visual Studio 2005 или выше
• Microsoft .NET Framework 3.0 или выше
• Java SUN/IBM 1.6 и выше
• Java SUN/IBM 1.3 и выше для перекомпиляции исходников Java
Операционные системы
• Windows Vista
• Windows XP SP2
• Windows Server 2003 SP1