Load the XAML

Create the (APL) Objects

Define Callbacks

Initialise

Show the Window




WPF Tutorial Contents

APL  Home Page


Here's a rough skeleton (note that the detail gets refined as we explore)...

#.frm←#.UTIL.XAML.LoadXAML <filename>' ⍝ Create form from a XAML file
#.frm←SetObjects #.frm                 ⍝ Create the objects (for APL to latch onto)
#.frm←SetCallbacks #.frm               ⍝ Define the event/callback relationships
#.frm←Initialise #.frm                 ⍝ Miscellaneous (application-specific) initialisation
{}#.frm.ShowDialog                     ⍝ Show the form and wait...

Load the XAML


We're keeping the XAML on file, and replacing placeholders with (workspace-held) values...

  ∇ (form names callbacks)←LoadXAML w;⎕IO;⎕ML;xaml
⍝ Load XAML from file, substitute placeholders and make the instance
  ⎕IO ⎕ML←0 3
  xaml←#.UTIL.FILE.ReadUnicodeFile w
  xaml←ReplaceXAML xaml
  xaml names callbacks←ExtractNames xaml
  form←#.WPF.LoadXaml xaml
 ∇
 ∇ z←ReplaceXAML w;⎕IO;⎕ML
⍝ Standard XAML substitutions
 ⎕IO ⎕ML←0 3
 z←#.UTIL.STRINGS.ss w'%homefolder%'#.GLOBALS.homefolder
 ∇

As ever, it's assumed that Dyalog's WPF namespace is present.

Create the (APL) Objects


We need something for APL to fasten onto, at its simplest a series of statements like...
 
 form.FileRefresh←form.FindName⊂'FileRefresh'


Which, with a lot of controls, is tedious, so we can apply a bit of discipline and include event/callback links inside the XAML, like this...

  <MenuItem Header="Cancel" Name="mniCancel" Click="∇#.DOGBUFFALO.ANALYSIS.Cancel">

there's a bit of naming discipline, using a convention that the XAML will contain the name of the APL callback function prefixed by (of course, we've made our XAML files unusable outside of APL with this notion, so we need a little stripper (included in the next bit of code)  if we want to present them to WPF and other tools.

So we have ExtractNames to pluck names and callbacks out of the xaml...

 ∇ (xaml names callbacks)←ExtractNames w;⎕IO;⎕ML;controls;sel;xml
⍝ Extract control names from a xaml vector
      ⎕IO ⎕ML←0 3
      xml←⎕XML w
      controls←3 #.ex.(⌷axis 1)xml
      names←{((,1 0/⍵)∊'Name' 'x:Name')/,0 1/⍵}¨controls
      sel←{(⎕UCS 226)=↑∘↑¨#.ex.(⊂axis 1)0 1/⍵}¨controls
      callbacks←sel{⍺⌿⍵}¨controls
      controls←(~sel){⍺⌿⍵}¨controls
      (3⌷[1]xml)←controls
      sel←×↑∘⍴¨callbacks
      callbacks←⊃,/,¨⍕¨¨(⊂¨(×sel)/names),¨(×sel)/callbacks
      callbacks←{(3/1+⍳(⍴⍵)÷3)⊂⍵}callbacks
      callbacks←{0 0 3↓¨⍵}¨callbacks
      names←∊¨{(∊⍴¨⍵)/⍵}names
      xaml←⎕XML xml
    ∇


Apologies for the clunky code in the middle (brain-freeze), but we come out of this with cleaned-up XAML, a list of object names and a list of  control/event/callback relations, which we can use thusly...

form←names SetObjects form

where...

 ∇ z←a SetObjects w;⎕IO;⎕ML;control
    ⍝ Set the WPF objects to be reachable from APL
      ⎕IO ⎕ML←0 3
      z←w
      :For control :In a
          {⍎'z.',⍵,'←z.FindName⊂''',⍵,''''}control
      :EndFor
    ∇


Thus reducing the amount our application code needs to "know" about the user  interface; although there is a potential issue in so far as the XAML contains a little information about the structure of our APL.

Warning - if you give the window a Name the code above creates a recursive definition wherein the form contains copies of itself.  This doesn't seem to affect execution of the subsequent application code - but Dyalog's "Find Objects" tool can't handle the resulting namepace depth.  For the nonce I'm not giving top-level forms a Name - this may be an area that needs revisiting as I get to understand more.

Define Callbacks


And continuing in the same vein...

form←callbacks SetCallbacks form

where...

  ∇ z←a SetCallbacks w;⎕IO;⎕ML;callback
    ⍝ Set the callbacks
      ⎕IO ⎕ML←0 3
      z←w
      :For callback :In a
          ⍎'z.',(↑callback),'.on',(1⊃callback),'←''',(2⊃callback),''''
      :EndFor
    ∇


Bear in mind that this "minimal code" approach precludes offering our own arguments to callback functions.

Initialise


Setting whatever else it is that we know about in APL, but WPF cannot...

 z←Initialise w;⎕IO;⎕ML;junk;sorted
⍝ Initialise main form controls
 ⎕IO ⎕ML←0 3
 sorted←{(⊂⎕AV⍋⊃⍵)⌷⍵}
 z←w junk←z.cboAuthor.Items.Add∘⊂¨sorted∪#.GLOBALS.Articles.Author
 junk←z.cboTitle.Items.Add∘⊂¨sorted∪#.GLOBALS.Articles.Title...


Something else that's feeling clunky-but functional - I suspect there are smarter ways to pre-populate things like ComboBox controls, but it may assume factors like keeping data in SQL tables which may not sit easily with what my application wants to do.

Show the Window


Finally, we make the user do some work by summoning <ShowDialog> (remember, <Show> doesn't have the decency to actually wait for something to happen - it needs to be followed by <Application.Run>.

Although the sequence...

app←⎕NEW Application
{}app.Run #.frmAcquire


is both "recommended" and achievable, it has one drawback.  That you can only do it once per APL session.  Try restarting your application without restarting APL and you get a Status Window full of nerdtalk about only allowing one Application per AppDomain - and at this time I have no idea how we're supposed to be able to flush the traces away.