Wednesday, July 04, 2007

WPF development using Java and Eclipse Europa (SWT)

One of the much hyped features of .NET 3 is Windows Presentation Foundation (WPF), a graphical subsystem framework, which includes (amongst other things) a markup language binding called XAML (which is really a rip off of XUL which has been the core of Mozilla/Firefox for years, but that's another story :)).

A couple of days ago the Eclipse foundation released a new version of Eclipse called Europa. It has some cool new features, one of which that caught my eye is a port of the Eclipse native widget set SWT to WPF.

For fun I thought it would be interesting to see how hard it would be to "port" a WPF example to SWT/WPF.

The Application Resources Sample is a really simple "pure" XAML application (i.e. does not mix code with presentation markup) that consists of two XML files:

page1.xaml

<!--<SnippetConsumingPage>-->
<StackPanel
Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Button Height="50" Width="250" Style="{StaticResource GelButton}" Content="Button 1" />
<Button Height="50" Width="250" Style="{StaticResource GelButton}" Content="Button 2" />
</StackPanel>


and

app.xml

<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="page1.xaml"
>
<Application.Resources>
<Style TargetType="Button" x:Key="GelButton" >
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Canvas>
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
x:Name="mainRect"
RadiusX="10" RadiusY="10">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LimeGreen" Offset="0.0" />
<GradientStop Color="Green" Offset="1.0" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle
Margin="1,1,2,2"
RadiusX="10" RadiusY="10"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Opacity="0.6">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="Transparent" Offset="0.5" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel HorizontalAlignment="Center" >
<ContentPresenter Content="{TemplateBinding Content}" Margin="15,10,15,5"/>
</StackPanel>
</Canvas>
<ControlTemplate.Triggers >
<Trigger Property="Button.IsFocused" Value="True" >
<Setter TargetName="mainRect" Property="Fill">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LightSkyBlue" Offset="0.0" />
<GradientStop Color="RoyalBlue" Offset="1.0" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>


which displays two buttons that change color when focus changes.

So, what does the equivalent SWT/WPF look like?

SWT/WPF currently has support for configuring Resources via XAML, but top level Controls like Button, Label, etc must be defined using the equivalent SWT widgets.

Prerequisties
Refactor app.xaml slightly so that the style markup is wrapped by a ResourceDictionary instead of Application.Resources

ButtonStyle.xaml

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Canvas>
<Rectangle
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
x:Name="mainRect"
RadiusX="10" RadiusY="10">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LimeGreen" Offset="0.0" />
<GradientStop Color="Green" Offset="1.0" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle
Margin="1,1,2,2"
RadiusX="10" RadiusY="10"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Opacity="0.6">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="Transparent" Offset="0.5" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel HorizontalAlignment="Center" >
<ContentPresenter Content="{TemplateBinding Content}" Margin="15,10,15,5"/>
</StackPanel>
</Canvas>
<ControlTemplate.Triggers >
<Trigger Property="Button.IsFocused" Value="True" >
<Setter TargetName="mainRect" Property="Fill">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="LightSkyBlue" Offset="0.0" />
<GradientStop Color="RoyalBlue" Offset="1.0" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>


and the Java code to create and layout the buttons would look something like:


package robertmaldon;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SwtWpfExampleMain {

public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setSize(300, 150);
shell.setLayout(new RowLayout());

final Button button1 = new Button(shell, SWT.FLAT);
button1.setData("ResourceDictionary", "C:\\workspace\\swt-wpf-example\\config\\ButtonStyle.xaml");
button1.setText("Button 1");
button1.setSize(250, 50);
button1.setBounds(0, 0, 250, 50);

final Button button2 = new Button(shell, SWT.FLAT);
button2.setText("Button 2");
button2.setSize(250, 50);
button2.setBounds(0, 50, 250, 50);

shell.open();

// Set up the event loop.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
// If no more entries in event queue
display.sleep();
}
}

display.dispose();
}
}


In the example above I XAMLize the style of the first button and leave the second button with the default style.

The trick here is that Button eventually extends org.eclipse.swt.widgets.Widget that supports XAML loading via the setData(key, value) method. The key can be one of two strings:
  • "XAML" - the value must be a String containing XAML markup
  • "ResourceDictionary" - the value is a URI to a XAML file
SWT/WPF support is a bit raw right now but shows a lot of promise.

5 comments:

Pop Catalin Sever said...

"...language binding called XAML (which is really a rip off of XUL which has been the core of Mozilla/Firefox for years, but that's another story :))..."

Well, there was a experiment in .Net when .Net 1.0 was in beta 1998, that described "creating interfaces in .net using declarative xml language", don't really know who ripped who, and I', sure there was someone before that using a SGML document to define interfaces.... those remarks x ripped y are simply bias and usually hold no truth ... lay down the fanboyism a bit ...

Robert Maldon said...

Fair enough.

I'm sure Netscape's idea for XUL wasn't original at the time (as you said, SGML pre-dates HTML and XML), it was just the first large-scale application written to use XML for 'widget' layout that I'm aware of.

So yes, my statement was biased. You can't hide on the internet :)

Thanks for your comment.

Christian Campo said...

Hi Robert,

did u ever try to extend your example to even more ?

I played with your and other examples and figured out that not only can specify a ResourceDictionary on the button level with
button1.setData(....)

but you can also specify the same on any parent up to the Shell like
shell.setData(.....)

then all button childs get the xaml rendering that you specify in the ResourceDictionary.

My next test was to do that for labels and text fields. Also no problem really.

But I couldnt figure out was how to combine a button and a text or label specification in one resource dictionary so that I could specify that on the Shell level and sort of use XAML for theming a whole application. That would be really cool.

I can do that if I set an individual resource dictionary for every instance of button,lable and text but that is a bit resource expensive.

Also I am looking for the list what XAML element is mapped to what SWT control. So i.e. what is right XAML style for a Composite (say the view composite).

maybe you played around with it a little more and have some insight or some pointers.

thanks
christian

Robert Maldon said...

Hi Christian,

Unfortunately I haven't had the time to explore the SWT/XAML integration beyond the simple example in this blog post.

I would recommend you either read through the source code (which is how I was able to figure out stuff for this example) or I bet there is an SWT forum you could pose a question to.

If you discover more about the SWT/XAML integration please let me know and I'll be happy to link to anything you blog about it!

Pradeep said...

Hi Robert,

Really nice article..

I was trying to modify the button text alignment to center using the ResourceDictionary.
Found that "HorizontalContentAlignment" is the style property to set the alignment.
any one has a idea why this is not working?
Thanks in advance !
Pradeep