Using Windows Presentation Foundation (WPF) from APLX
The following Wiki page describes the use of WPF and XAML from APLX for Windows. It is based on work contributed by APLX user Pierre Gilbert, and presented here with his permission - Thanks, Pierre |
Windows Presentation Foundation (WPF), is Microsoft's modern framework for creating GUI applications in .NET. It is intended to replace the older .NET framework System.Windows.Forms, which was a wrapper around the old Windows GDI interface.
WPF has a number of advantages. For example it is built on DirectX, so that richer graphics are possible and code will make use of GPU hardware acceleration. It is also vector-graphics based for resolution independence. But perhaps the most significant advantage of WPF is that the appearance of the GUI can now be specified through a new XML-based markup language called XAML. This makes it easy to create APL programs with sophsticated user interfaces.
XAML
Microsoft promotes XAML (short for eXtensible Application Markup Language, and pronounced "Zammel") when programming with WPF. It is a simple and general purpose language suitable for constructing and initializing .Net objects
The basic idea of XAML is to describe the appearance of the User Interface through a text file. This is separate to the business logic of the application, and so a XAML file can be used with an application written in any .NET language like C#, Visual Basic or - more significantly for us - APL. You can now use XAML to do all the buttons and labels stuff, add your APL code to handle the events and voila!
In addition, by using styles in the XAML file the UI can be extensively customised without changing the APL code. This is somewhat similar to the way that web pages can be customised using Cascading Style Sheets (CSS).
You can type XAML markup directly into a text editor, but for more complex examples you may want to use a tool like Microsoft's Visual Studio Express (free) or Expression Blend (free for 60 days) to write your XAML. These tools have the advantage that you can design your UI visually.
Learning XAML is not easy at the beginning and you need a good book to get you started. After a while you should be able to write the UI part of your application faster than with ⎕WI. One advantage of WPF is that you can throw the labels, buttons, etc. on one of the containers and never need to give a position or a dimension, the container will position everything for you. And if the size of the window is changed, everything is automatically resized to keep the same looks and proportions.
Advantages of using XAML
There's nothing that you can do with XAML that can't be done purely by writing code, but XAML has several major advantages:
- There are lots of examples of how to write XAML. For example if you need to do a toolbar in XAML, you can Google it, find an example already made on the internet, then copy/paste it.
- Because the code and the UI appearance are separate the XAML can be produced by a fancy XAML visual design tool, which means that you can create very slick user interfaces. The XAML can then be loaded by APL, which only needs to implement the 'code-behind' business logic to respond to events like button presses, menu selections, etc.
- Businesses that would like to give a fresher look to their APL applications could hire a XAML Designer to do all their user interfaces (instead of using []WI) and keep their APL code largely intact or, even better, hire an APL programmer that knows XAML.
A simple example
Here is a simple demonstration of using WPF and XAML from APLX for Windows. Although trivial it's an almost complete working example. To complete it we'll need a couple of support functions which we'll cover in a moment. You'll also need to make sure that the WPF .NET assemblies are included in the APLX .NET search path (see below).
The following XAML code will create a window containing a label and a push button:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Demo"
Height="200"
Width="300">
<StackPanel>
<Button x:Name="myButton" Width="125" Height="35">Press Me</Button>
<TextBlock x:Name="myText" TextWrapping="Wrap">
I'm a text block.
</TextBlock>
</StackPanel>
</Window>This produces a window which looks like this when displayed. It's a bit plain, but we'll improve it in a minute.
Here is the APL code to load the sample XAML above from a character vector XamlString (We could also load it from a XAML text file).
Notice that the code adds an APL event handler to the button's Click event, in this case trivially appending the word "Ouch !" to the end of the text every time the button is pressed.
DEMO;root_obj;myButton;myText root_obj ← LoadObjectFromString XamlString myButton←root_obj.FindName 'myButton' myText←root_obj.FindName 'myText' ⍝ ⍝ Add event handlers myButton.Click←'myText.Text←myText.Text, " Ouch !"' Wait root_obj
Turning a XAML string into .NET objects
The function LoadObjectFromString contains the code needed to turn the XAML into WPF objects:
root_obj←LoadObjectFromString XamlString;StringReader;XAMLR;XMLR;XmlReader ⍝ Creates a WPF Object From an APL Character Vector Representing a XAML File. ⍝ ⍝ XamlString = Vector of Characters ⍝ root_obj = .Net WPF Root Object XMLR ← '.Net' ⎕GETCLASS 'System.Xml.XmlReader,System.Xml.dll' XAMLR ← '.Net' ⎕GETCLASS 'System.Windows.Markup.XamlReader' ⍝ From PresentationFramework.dll StringReader ← '.Net' ⎕NEW 'System.IO.StringReader,mscorlib.dll' XamlString XmlReader ← XMLR.Create StringReader root_obj ← XAMLR.Load XmlReader
Processing Events
As the example above shows, providing an APL function to handle a .NET event is very simple. It's just a case of providing an APL expression that is executed every time the button's Click event is triggered.
myButton.Click←'myText.Text←myText.Text, " Ouch !"'
However, event handling in APLX does not fire unless the application is currently handling events. You need to call the Wait for Events system function ⎕WE. The following is a listing of the Wait support function we used earlier, which loops to handle events until the window closes.
Wait root_obj;sink ⍝ Function to Wait on a .Net 3.0 Window sink ← '.Net' ⎕CALL 'System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop' root_obj ⍝ Show And Wait the Window root_obj.Show ⍝ Wait while the Object is Visible :While root_obj.IsVisible sink ← ⎕WE 0.1 :EndWhile root_obj.Close
Customising appearance through XAML
We're now in a position to improve the appearance of our sample application. Note that this can be done just by changing the XAML code - we don't need to change a single line of APL (the business logic) because of the way XAML naturally separates appearance from functionality.
For this simple demo, let's improve the appearance as follows:
- Add some space around the button and text
- Change the font size and colour of the text
- Add a coloured background to the window which fades from left to right
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Demo"
Height="200"
Width="300">
<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1.5,0">
<GradientStop Color="#c04a52a0" Offset="0"/>
<GradientStop Color="#ffffff" Offset="0.9"/>
</LinearGradientBrush>
</Window.Background>
<StackPanel>
<Button x:Name="myButton" Width="125" Height="35" Margin="10">Press Me</Button>
<TextBlock x:Name="myText" FontSize="20" Margin="10" TextWrapping="Wrap">
<TextBlock.Foreground>
<SolidColorBrush Color="#ffffff"/>
</TextBlock.Foreground>
I'm a text block.
</TextBlock>
</StackPanel>
</Window>Here's a screenshot of the new version:
OK, it's probably not the last word in GUI design! But you get the idea.
Using Styles in XAML
In our simple example above we hard-coded the font size and colour directly into the TextBlock instance. In fact, for anything except a trivial application it is better to make use of XAML's support for styles. You can declare all the important attributes of a style (e.g. font face, size, colour) in a single place, and then refer to it from elsewhere. This has the advantage of ensuring a consistent look-and-feel, and also allows you to change the appearance of the application just by updating the styles.
Search Path for .NET WPF Assemblies
WPF was initially released as part of the .NET 3.0 framework.
In order to access the WPF functions, you need to make sure that the locations of the WPF assemblies are specified in the APLX .NET search path, which you can specifying with ⎕SETUP 'using'
The following is an edited listing of Pierre Gilbert's suggested function for doing this. For the full version, which also checks that the .NET 3.0 framework is installed, see the attached workspace.
SETUP;FolderEnum;Path;SE;SWFA;path30 ⍝ ADDS .NET 3.0 ASSEMBLIES TO THE SEARCH PATH ⍝ THIS FUNCTION SHOULD BE EXECUTED ONLY ONCE WHEN THE WS IS LOADED. 'Updating ⎕SETUP with Paths of .Net 3.0 Assemblies' ⍝ Path of .Net 3.0 SE ← '.Net' ⎕GETCLASS 'System.Environment' FolderEnum ← '.Net' ⎕GETCLASS 'System.Environment+SpecialFolder' path30 ← SE.GetFolderPath FolderEnum.ProgramFiles path30 ← path30,'\Reference Assemblies\Microsoft\Framework\v3.0\' ⍝ Existing Paths Path ← '.Net' ⎕SETUP 'using' ⍝ Add Reference To PresentationFramework Assembly Path ← Path, ⊂'PresentationFramework,',path30,'PresentationFramework.dll' ⍝ Add Reference To WindowsFormsIntegration Assembly Path ← Path, ⊂'WindowsFormsIntegration,',path30,'WindowsFormsIntegration.dll' ⍝ Add Reference To PresentationCore Assembly Path ← Path, ⊂'PresentationCore,',path30,'PresentationCore.dll' ⍝ Add Reference To WindowsBase Assembly Path ← Path, ⊂'WindowsBase,',path30,'WindowsBase.dll' ⍝ Add to ⎕SETUP all the Paths '.Net' ⎕SETUP (⊂'using'), Path 'Enabling Visual Styles' SWFA←'.Net' ⎕GETCLASS 'System.Windows.Forms.Application' SWFA.EnableVisualStyles '... Done',⎕R '
Sample Workspace
For further inspiration try the functions in Pierre Gilbert's sample workspace:
Here are some screenshots:
Animation Example:
Sample of WPF controls:
Font Browser:
Changing the appearance of Buttons
Learning more about WPF and XAML
Here is some useful links if you want to read more about the topics discussed:
1. Discussion about the separation of layers and XAML
2. Christian Mosers WPF Tutorial.net
3. Mike Swanson's Blog: Windows Presentation Foundation Tools and Controls
Books
I can thoroughly recommend the following book, which describes WPF and XAML in just the right level of technical detail:
Windows Presentation Foundation Unleashed by Adam Nathan (ISBN 0-672-32891-7)
Author: SimonMarsden
The Dyalog Appendix
Delving once more into the world of "real programmers", here's a partial translation (with some asides) into Dyalog. Anyone wanting to pursue XAML and Dyalog further should also look at the Dyalog Forum where Jonathan Manktelow has posted some code.
Using the same XamlString (a simple vector) as in the first example above...
DEMO;root_obj;myButton;myText ⍝ Simon Marsden's APLX example, mutated into Dyalog root_obj←LoadObjectFromString XamlString myButton←root_obj.FindName⊂'myButton' myText←root_obj.FindName⊂'myText' myButton.onClick←'#.Ow' root_obj.ShowDialog
Things to note here:
I've changed the callback so that it is the name of a function rather than the simple assignment of Simon's original, because I got a SysError 999 from a more literal translation
The ShowDialog Method makes the object modal; you could use Show for a non-modal object, but this requires myButton and myText to be globals. Hopefully, if and when I get more experience with this, a tidier solution will make itself known.
- Dyalog doesn't need any special "waiting", it does this automatically (so there's no "Wait" function in my translation).
root_obj←LoadObjectFromString XamlString;sr;XAMLR;XMLR;xr;⎕USING ⍝ Simon Marsden's APLX example, mutated into Dyalog ⍝ Creates a WPF Object From an APL Character Vector Representing a XAML File. ⍝ ⍝ XamlString = Vector of Characters ⍝ root_obj = .Net WPF Root Object ⎕USING←#.WPF.Using ⍝ Jonathan Manktelow's omnibus edition sr←StringReader.New⊂XamlString xr←XmlReader.Create⊂sr root_obj←XamlReader.Load⊂xr
Which is where the real differences show up
- I've used Jonathan's omnibus edition setting for quadUsing, at the moment I don't know whether it might have been "better" to use a more tailored version, but in my current state of ignorance I'll go with something that works.
- I was also rather daunted by the way that Simon's syntax is different from his earlier FTP example - this .NET stuff is all alien to me
And, as it's a function rather than an inline expression
Ow w myText.Text←myText.Text,' Ouch!'
Try it in APL/W 12.1 (probably earlier versions as well) and Bob's your uncle.
Finally, I'm in a bit of a quandary here about whether this is a wholly "good" direction to take for future applications. While I can agree with Simon about hooking up with mainstream tools and getting access to all sorts of fancy interfaces I also hanker for the good old days when APL (or J) was all a person (or computer) needed.
(Simon's comment: Dick, thanks for adding the Dyalog material. Regarding your last paragraph, one point to make is that adopting XAML and WPF is entirely optional. APLX will still retain the older ways of doing things like the ⎕WI interface, and I imagine Dyalog will continue with ⎕WC. However, I actually think WPF is easier to use once you've got the hang of it -- SimonMarsden 2010-06-17 08:48:47)
Translator: DickBowman
APL Wiki