ClipBoard

Foibles

HTML Help

NotifyIcon

Queuing Events

Controlling Cursor Location

Positioning Child Windows

Window Initialisation

Mouse Position




WPF Tutorial Contents

APL  Home Page


ClipBoard


A simple snippet, where <items> is a matrix of character vectors...

 ⎕USING←#.WPF.Using      ⍝ The usual overkill...
...
cd←(∊(#.ex.(⊂axis 1)⍕¨items),¨⊂⎕UCS 13 10) ⍝ Uses an "enclose with axis" defined operator
Clipboard.SetData(DataFormats.Text cd)

Foibles


Not everything works al the logically-minded might expect, here are some oddities...

HTML Help


Not really a WPF topic, but possibly worth recording somewhere.

When using Microsoft's HTML Help, there are three (at least) options when opening the .chm file from an application

Note the correspondence of topic to source HTML files in all of this (your application author is going to have knowledge of your help authoring arrangements).

NotifyIcon


When you want a text balloon to appear in the Windows TaskBar, notifying the user that something significant has happened in your application (running minimised as a background task) NotifyIcon is what you need.

Of course, when you look it up in the "documentation" you'll be told that it isn't part of WPF but that you can use the .NET equivalent (how kind of them).  So...

 ∇ Notify(text icon duration);⎕IO;⎕ML;⎕USING;n
    ⍝ Display a NotifyIcon in the TaskBar
      ⎕IO ⎕ML←0 3
      ⎕USING←,⊂'System.Windows.Forms,System.Windows.Forms.dll'
      ⎕USING,←⊂'System.Drawing,system.drawing.dll'
      n←⎕NEW NotifyIcon ⍬
      n.BalloonTipText←text
      n.Icon←⎕NEW Icon(⊂icon)
      n.Visible←1
      n.ShowBalloonTip duration
    ∇


And in operation...

Notify  'This is a message'  'd:\dick\temp\flashlight.ico' 3000

Be warned that this can be a little fragile or environment-sensitive; you may need to set the Text property (BalloonTipText and Text are different) , and the duration might be overruled by some arcane Windows settings.  Notice also that some form of Icon is always required, although - at a guess - this could be quite minimal if you'd prefer a text-only appearance.

The NotifyIcon will pop up even if you've set it to AutoHide.

Queuing Events

Here's an area where I'm experiencing more frustration than satisfaction - with the APL/W Native GUI it was quite simple, you used ⎕NQ...

⎕NQ form.control "GotFocus"

which was very handy when you wanted to decide which control should get the focus immediately after the form was opened.

But ⎕NQ doesn't work (at leat at the time of writing) for WPF controls (try it, DOMAIN ERROR is your reward).

In theory RaiseEvent should serve the same purpose, as in...

⎕using←#.UTIL.WPF.Using ''                                  ⍝ A catchall to make a "sufficient" ⎕USING
x← ⎕NEW RoutedEventArgs(TabItem.GotFocusEvent z.tbiRefresh) ⍝ To queue up a "GotFocus" on TabItem tbiRefresh
z.RaiseEvent x                                              ⍝ And raise the event


But, if you're using ShowDialog to show your WPF window and wait for things to happen - it's ineffective.  Maybe using Show and Application.Run would be the answer, but there are problems there (search for ShowDialog in the Dyalog APL Forum).  Some people allege that the above works, possibly without troubling themselves to actually try it in practice.

Actually, revisiting this I have been able to seemingly make it work, encapsulated the code into a little function so that my applications are less troubled by WPFnerdery...

 ∇ RaiseEvent(ref event);⎕IO;⎕ML;routedevent;⎕USING
⍝ Raise an event on a control
      ⎕IO ⎕ML←0 3
      ⎕USING←#.UTIL.WPF.Using''
      ⍎'routedevent←⎕NEW RoutedEventArgs(',event,' ref) '
      ref.RaiseEvent routedevent
    ∇


Something I'd really like to be able to do is to move the mouse around, but am currently running into a heap of "can't convert mouse arguments into event arguments" - if there's any progress I'll try to remember to add further clarification here or hereabouts.  The section below on Controlling Cursor Location may be helpful.  

There are (variously clumsy) workarounds that solve the issue for various scenarios , for example you can put a callback on the onActivated event for the window (but be careful, you need to turn it off once you've fired it - otherwise the callback runs every time you switch back to your application from another).

Next time I run into a need, I'll explore some more.  But I'd hope that the domain of ⎕NQ could extend to WPF objects.

Controlling Cursor Location

By now it scarcely comes as a surprise to find that WPF does not appear to offer any functionality to directly locate the (mouse) cursor where we need it to be - and this is compounded by an opaque approach to telling us where a control is located....

So, and I'm relying on information gleaned from sites using obsolete programming lanuages, we finally settle on...

Locate the top left corner of a Button...


  ⎕USING←'System.Windows,WindowsBase.dll'
  bc←-⌊0.5+((window.TransformToVisual(window.btnOK)).Transform(⎕NEW Point(0,0))).(X Y)


Put the Cursor in the middle of the Button...


  ⎕NA'user32|SetCursorPos I4 I4'
  {}SetCursorPos window.(Left Top)+0 25+bc+⌊0.5×window.btnOK.(ActualWidth ActualHeight)



Feel free to substitute your own jiggery-pokery for working out precisely where you want to stick it.

I am - as often - indebted to Pierre Gilbert and Mike Hughes for their guidance on this.

Positioning Child Windows

As you might expect, WPF has a will (if not a mind) of its own when it comes to deciding where a window should be positioned, for main application windows it seems best to impose one's own decisions and explicitly set the Left and Top properties (and to maintain a pretence to user-friendliness by remembering where the window was when closed the last time an application was run, reusing these to restore the application to where the user last saw it).

What's more of a headache is that there seems to be no sense at all when it comes to child windows, so we have two main options (discounting the obvious one of just leaving WPF to do its unaided worst).


And if you want to move the cursor into a control in a child window (full and unexpurgated code to cover all options)...

  ∇ MoveCursor w;⎕IO;⎕ML;⎕USING;bc;window;control;offset;⎕TRAP;pos
    ⍝ Move cursor to centre of control <w>
      ⎕IO ⎕ML←0 3
      ⎕USING←'System.Windows,WindowsBase.dll'
      ⎕NA'user32|SetCursorPos I4 I4'
      window control offset←3↑w,⊂0 0
      bc←-⌊0.5+((window.TransformToVisual control).Transform(⎕NEW Point(0,0))).(X Y)
      :Trap 6
          pos←window.Owner.(Left Top)
          pos+←⌊0.5+(0.5×window.Owner.(ActualWidth ActualHeight))-0.5×window.(Width Height)
      :Else
          pos←window.(Left Top)
      :EndTrap
      {}SetCursorPos pos+(0 25+offset)+bc+⌊0.5×control.(ActualWidth ActualHeight)
    ∇

About which we note...

Window Initialisation

A trap that someone coming from the Dyalog APL/W Gui can fall into is attempting to set initial properties of controls inside a window before using ShowDialog (for example presettting selected rows of a DataGrid) , maybe writing code like...

 frmSummary names                                                                                 callbacks←#.UTIL.XAML.LoadXAML(#.GLOBALS.homefolder,'XAML/summary.xaml')(⊂'')
 frmSummary←names #.UTIL.WPFTOOLS.SetObjects frmSummary
 frmSummary←callbacks #.UTIL.WPFTOOLS.SetCallbacks frmSummary
 frmSummary.Owner←#.frmTrawl
 {}frmSummary.dgrSummary #.UTIL.WPFTOOLS.PopulateGrid('From' 'TimeStamp' 'Subject')('String'             'DateTime' 'String')summary
 frmSummary.dgrSummary.SelectedIndex←0
 {}frmSummary.ShowDialog


Which won't have the desired effect.
What you need to do is to put this sort of initialisation into a callback for the Loaded event on the window.  Thinking about this some more, it looks as though some "initialisation" is allowable before the show (you can see the PopulateGrid above) and some is ineffectual (the setting of SelectedIndex) - which makes me think that for consistency I ought to put everything into the Loaded callback, but - for the nonce - I'll persevere until things break terminally.

Mouse Position


Another of the frustrations thrown at us, where's the mouse when you click it?

 ⎕USING←'System.Windows.Input,presentationcore.dll'
 x y←(Mouse.GetPosition #.wdwDogMap.cvsMap).(X Y)

Seems to be doing the trick, although web searches suggest there might need to be more to it (we shall see)...