WPF XAML demo: Difference between revisions

From APL Wiki
Jump to navigation Jump to search
No edit summary
m (Change broken links to point to old APL Wiki)
 
(16 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= wpfXamlDemo =
This page gives a [[Dyalog APL]] demonstratration of WPF XAMPL with some utility functions, delivered in the form of a [[namespace]].


'''wpfXamlDemo''' is a Dyalog APL namespace that demonstrates the use of WPF Xaml with some utility functions.
Please read the Dyalog tutorials for a more detailed explanation. Part of the code was taken from ''[http://www.codeproject.com/Articles/22980/WPF-A-Beginner-s-Guide-Part-of-n CodeProject: A Beginner's Guide]''.


Please read the Dyalog tutorials for a more detailed explanation. Part of the code was taken from <ref>[http://www.codeproject.com/Articles/22980/WPF-A-Beginner-s-Guide-Part-of-n CodeProject: A Beginner's Guide]</ref>
=== What is XAML? ===
 
[[wikipedia:XAML|XAML]] stands for Extensible Application Markup Language (and pronounced "Zammel"). It's a simple language based on [[wikipedia:XML]] to create and initialize [[.NET]] objects with hierarchical relations.
=== WHAT IS XAML ? ===
XAML stands for Extensible Application Markup Language (and pronounced "Zammel"). It's a simple language based on XML to create and initialize .NET objects with hierarchical relations.


All you can do in XAML can also be done in code. XAML is just another way to create and initialize objects.
All you can do in XAML can also be done in code. XAML is just another way to create and initialize objects.


You can use WPF without using XAML. It's up to you whether you want to declare it in XAML or write it as code.  There is a [https://msdn.microsoft.com/en-us/library/ms752059(v=vs.100).aspx Xaml Overview] from Microsoft.
You can use WPF without using XAML. It's up to you whether you want to declare it in XAML or write it as code.  There is a [https://msdn.microsoft.com/en-us/library/ms752059(v=vs.100).aspx XAML Overview] from Microsoft.


{{Collapse|Here is an example of Xaml found in the attached namespace called '''sample1''':|
{{Collapse|Here is an example of XAML found in the attached namespace called '''sample1''':|
<source lang=xml>
<syntaxhighlight lang=xml>
<Window
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Line 44: Line 42:
   </Grid>
   </Grid>
</Window>
</Window>
</source>
</syntaxhighlight>
}}
}}
=== FixSimpleXaml ===
=== FixSimpleXaml ===
If you install the attached namespace and execute the following 2 lines in your workspace:
If you install the attached namespace and execute the following 2 lines in your workspace:
<source lang=apl>
<syntaxhighlight lang=apl>
       win ← FixSimpleXaml sample1
       win ← FixSimpleXaml sample1
       win.Show
       win.Show
</source>
</syntaxhighlight>
The following window is displayed:
<!-- The following window is displayed:


{{attachment:Sample1.png || width=250}}
{{attachment:Sample1.png || width=250}} -->


<source lang=apl inline>FixSimpleXaml</source> is a function used to execute the Xaml and return the root element as a .Net object.  All the other elements that are named in the Xaml will be attached to their names to the root object automatically.
<syntaxhighlight lang=apl inline>FixSimpleXaml</syntaxhighlight> is a function used to execute the XAML and return the root element as a .Net object.  All the other elements that are named in the XAML will be attached to their names to the root object automatically.


For example, the element '''!TextBox''' that is named '''textBox1''' (line 15) and the element '''Button''' that is named '''button1''' (line 22) are attached automatically to the root element by the function <source lang=apl inline>FixSimpleXaml</source>:
For example, the element '''!TextBox''' that is named '''textBox1''' (line 15) and the element '''Button''' that is named '''button1''' (line 22) are attached automatically to the root element by the function <syntaxhighlight lang=apl inline>FixSimpleXaml</syntaxhighlight>:
<source lang=apl>
<syntaxhighlight lang=apl>
       win.textBox1.Text
       win.textBox1.Text
Textbox
Textbox
       win.button1
       win.button1
System.Windows.Controls.Button: Click Me !
System.Windows.Controls.Button: Click Me !
</source>
</syntaxhighlight>


That way you don't need to define a separate variable for each named element. If you install the User Command [[sfPropGrid||]] you can see all the properties, methods and events of all the named objects by doing (click the combo of NOE to access all the named objects):
That way you don't need to define a separate variable for each named element. If you install the [[user command]] called <syntaxhighlight lang=apl inline>sfPropGrid</syntaxhighlight> you can see all the properties, methods and events of all the named objects by doing (click the combo of NOE to access all the named objects):
<source lang=apl>
<syntaxhighlight lang=apl>
       ]noe win      ⍝ noe = .Net Object Explorer
       ]noe win      ⍝ noe = .Net Object Explorer
</source>
</syntaxhighlight>
In conclusion <source lang=apl inline>FixSimpleXaml</source> is a simple function to use on simple Xaml that does not have events and is properly formed.  In production code you may want to do something like this:
In conclusion <syntaxhighlight lang=apl inline>FixSimpleXaml</syntaxhighlight> is a simple function to use on simple XAML that does not have events and is properly formed.  In production code you may want to do something like this:
<source lang=apl>
<syntaxhighlight lang=apl>
  :If ⎕NULL≡myObject ← FixSimpleXaml myXaml
  :If ⎕NULL≡myObject ← FixSimpleXaml myXaml
     ⍝ Fixing the Xaml did not work. Show an error and exit.
     ⍝ Fixing the XAML did not work. Show an error and exit.
     ⎕ ← 'Error Fixing Xaml'
     ⎕ ← 'Error Fixing XAML'
     →0
     →0
  :Else
  :Else
     ⍝ There is no error.
     ⍝ There is no error.
  :EndIf
  :EndIf
</source>
</syntaxhighlight>


=== FixXaml ===
=== FixXaml ===
{{Collapse|For cases where there are events that need to be fixed and have better error handling the function <source lang=apl inline>FixXaml</source> is available.  It is useful when using Xaml taken directly from Visual Studio.  For example, with the Xaml code in '''sample2''' that has an event on the button (Click<nowiki>=</nowiki>"<source lang=apl inline>__Button_Click</source>") at line 24, if you do the following:|
{{Collapse|For cases where there are events that need to be fixed and have better error handling the function <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight> is available.  It is useful when using XAML taken directly from Visual Studio.  For example, with the XAML code in '''sample2''' that has an event on the button (Click<nowiki>=</nowiki>"<syntaxhighlight lang=apl inline>__Button_Click</syntaxhighlight>") at line 24, if you do the following:|
<source lang=xml>
<syntaxhighlight lang=xml>
<Window
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Line 116: Line 115:
   </Grid>
   </Grid>
</Window>
</Window>
</source>
</syntaxhighlight>
}}
}}
<source lang=apl>
<syntaxhighlight lang=apl>
       win ← FixXaml sample2
       win ← FixXaml sample2
       win.Show
       win.Show


</source>
</syntaxhighlight>
and then if you click on the button, the value of the '''!TextBox''' will change. The value of the '''!TextBox''' can be retrieved simply by doing:
and then if you click on the button, the value of the '''!TextBox''' will change. The value of the '''!TextBox''' can be retrieved simply by doing:
<source lang=apl>
<syntaxhighlight lang=apl>
       win.textBox1.Text
       win.textBox1.Text
I Was Clicked !
I Was Clicked !
</source>
</syntaxhighlight>


The function <source lang=apl inline>__Button_Click</source> is handling the event.  The author has taken the convention of naming the callback functions with a double underscore prefix.
The function <syntaxhighlight lang=apl inline>__Button_Click</syntaxhighlight> is handling the event.  The author has taken the convention of naming the callback functions with a double underscore prefix.


The goal is to be able to take the Xaml directly from Visual Studio to APL.  The single underscore '_' is a valid first character in Visual Studio and APL but is in conflict with the menu object that will accept an underscore as the first character to define a keyboard shortcut.
The goal is to be able to take the XAML directly from Visual Studio to APL.  The single underscore '_' is a valid first character in Visual Studio and APL but is in conflict with the menu object that will accept an underscore as the first character to define a keyboard shortcut.


The line 5 of '''sample2''' (x:Class="!WpfApplication3.!MainWindow") that is required by Visual Studio is removed by <source lang=apl inline>FixXaml</source>.  See the comments in the function for more information.
The line 5 of '''sample2''' (x:Class="!WpfApplication3.!MainWindow") that is required by Visual Studio is removed by <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight>.  See the comments in the function for more information.


In production code you may want to trap any error by using code like this:
In production code you may want to trap any error by using code like this:
<source lang=apl>
<syntaxhighlight lang=apl>
  :If ⎕NULL≡↑myObject ← FixXaml myXaml
  :If ⎕NULL≡↑myObject ← FixXaml myXaml
     ⍝ Fixing the Xaml did not work. Show an error and exit.
     ⍝ Fixing the XAML did not work. Show an error and exit.
     ⎕ ← 2⊃myObject
     ⎕ ← 2⊃myObject
     →0
     →0
Line 144: Line 143:
     ⍝ There is no error.
     ⍝ There is no error.
  :EndIf
  :EndIf
</source>
</syntaxhighlight>


=== About ⎕USING ===
=== About ⎕USING ===
In general, when using Xaml there is no need to define a <source lang=apl inline>⎕USING</source> before fixing it except when there is a 3rd party dll involved.  For example, the variable <source lang=apl inline>NewWindow</source> is defined as:
In general, when using XAML there is no need to define a <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> before fixing it except when there is a 3rd party dll involved.  For example, the variable <syntaxhighlight lang=apl inline>NewWindow</syntaxhighlight> is defined as:
<source lang=apl>
<syntaxhighlight lang=apl>
<Window
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Window>
</Window>
</source>
</syntaxhighlight>
and if you do:
and if you do:
<source lang=apl>
<syntaxhighlight lang=apl>
       win ← FixSimpleXaml NewWindow
       win ← FixSimpleXaml NewWindow
       win.Show
       win.Show
</source>
</syntaxhighlight>
a Window will appear.  In procedural code, the following is required for the same result:
a Window will appear.  In procedural code, the following is required for the same result:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
       ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
       win ← ⎕NEW Window
       win ← ⎕NEW Window
       win.Show
       win.Show
</source>
</syntaxhighlight>
The first element of Xaml must contain the '''xmlns=''' and the '''xmlns:x=''' declarations. This is instructing the parser (in our case '''System.Windows.Markup.!XamlReader''' in the function <source lang=apl inline>FixSimpleXaml</source> and <source lang=apl inline>FixXaml</source>) to load a series of .NET namespaces required to parse the Xaml.  This is just a convention, there is actually no such web site.
The first element of XAML must contain the '''xmlns=''' and the '''xmlns:x=''' declarations. This is instructing the parser (in our case '''System.Windows.Markup.!XamlReader''' in the function <syntaxhighlight lang=apl inline>FixSimpleXaml</syntaxhighlight> and <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight>) to load a series of .NET namespaces required to parse the XAML.  This is just a convention, there is actually no such web site.


=== 3rd Party Dll ===
=== 3rd Party Dll ===
When using 3rd party dll, they must be added to the declaration in the first element of Xaml.  There are 2 choices on how to do it:
When using 3rd party dll, they must be added to the declaration in the first element of XAML.  There are 2 choices on how to do it:


<source lang=apl>
<syntaxhighlight lang=apl>
   xmlns:myname="clr‑namespace:MyNamespace;assembly=MyDllName"  ⍝ Notice that there is no '.dll' after MyDllName
   xmlns:myname="clr‑namespace:MyNamespace;assembly=MyDllName"  ⍝ Notice that there is no '.dll' after MyDllName
or
or
   xmlns:myname="http://schemas.somewebsite.com/xaml"          ⍝ A website name given by the 3rd party dll (may not exists)
   xmlns:myname="http://schemas.somewebsite.com/xaml"          ⍝ A website name given by the 3rd party dll (may not exists)
</source>
</syntaxhighlight>
The method on the first line is recommended. Here is an example with the Syncfusion !PropertyGrid:
The method on the first line is recommended. Here is an example with the Syncfusion !PropertyGrid:
<source lang=apl>
<syntaxhighlight lang=apl>
   xmlns:sf="clr-namespace:Syncfusion.Windows.PropertyGrid;assembly=Syncfusion.PropertyGrid.Wpf"  ⍝ Notice no .dll at the end
   xmlns:sf="clr-namespace:Syncfusion.Windows.PropertyGrid;assembly=Syncfusion.PropertyGrid.Wpf"  ⍝ Notice no .dll at the end
</source>
</syntaxhighlight>
and the Xaml will look like this:
and the XAML will look like this:
<source lang=apl>
<syntaxhighlight lang=apl>
   <sf:PropertyGrid x:Name="PGrid"/>  ⍝ Notice the prefix 'sf' is the same on both lines (you choose the prefix).
   <sf:PropertyGrid x:Name="PGrid"/>  ⍝ Notice the prefix 'sf' is the same on both lines (you choose the prefix).
</source>
</syntaxhighlight>
but this is not enough, <source lang=apl inline>⎕USING</source> must be set up correctly before fixing the Xaml for 3rd party dlls in order for the parser to find the assembly.  Here is an example for the Syncfusion !PropertyGrid ('''Syncfusion/4.5/''' is the Dyalog sub-directory where the assemblies live):
but this is not enough, <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> must be set up correctly before fixing the XAML for 3rd party dlls in order for the parser to find the assembly.  Here is an example for the Syncfusion !PropertyGrid ('''Syncfusion/4.5/''' is the Dyalog sub-directory where the assemblies live):
<source lang=apl>
<syntaxhighlight lang=apl>
     ⎕USING ← 'Syncfusion.Windows.PropertyGrid,Syncfusion/4.5/Syncfusion.PropertyGrid.Wpf.dll'    ⍝ The .dll is required
     ⎕USING ← 'Syncfusion.Windows.PropertyGrid,Syncfusion/4.5/Syncfusion.PropertyGrid.Wpf.dll'    ⍝ The .dll is required


Line 192: Line 191:
     ⎕USING ← 'MyNamespace,SubDirectoryOfDyalogFolder/MyDllName.dll'  ⍝ If the dll is in a sub-directory of dyalog.exe
     ⎕USING ← 'MyNamespace,SubDirectoryOfDyalogFolder/MyDllName.dll'  ⍝ If the dll is in a sub-directory of dyalog.exe
     ⎕USING ← 'MyNamespace,MyDllName.dll'                            ⍝ If the dll is in the same directory as dyalog.exe
     ⎕USING ← 'MyNamespace,MyDllName.dll'                            ⍝ If the dll is in the same directory as dyalog.exe
</source>
</syntaxhighlight>
Another thing with the value of <source lang=apl inline>⎕USING</source> for 3rd party dll is that it must be set in the __same namespace__ as where <source lang=apl inline>FixXaml</source> or <source lang=apl inline>FixSimpleXaml</source> is located (because <source lang=apl inline>⎕USING</source> is Namespace scope). Alternatively, if you setup your <source lang=apl inline>dyalog.exe.config</source> file that is in the same directory as the <source lang=apl inline>dyalog.exe</source> file with a directive to look in the <source lang=apl inline>Syncfusion/4.5</source> directory you will not need to set up <source lang=apl inline>⎕USING</source> and you don't need to worry about loading it into memory.
Another thing with the value of <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> for 3rd party dll is that it must be set in the __same namespace__ as where <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight> or <syntaxhighlight lang=apl inline>FixSimpleXaml</syntaxhighlight> is located (because <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> is Namespace scope). Alternatively, if you setup your <syntaxhighlight lang=apl inline>dyalog.exe.config</syntaxhighlight> file that is in the same directory as the <syntaxhighlight lang=apl inline>dyalog.exe</syntaxhighlight> file with a directive to look in the <syntaxhighlight lang=apl inline>Syncfusion/4.5</syntaxhighlight> directory you will not need to set up <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> and you don't need to worry about loading it into memory.


Typically the file will look like this:
Typically the file will look like this:
<source lang=apl>
<syntaxhighlight lang=apl>
     <configuration>
     <configuration>
       <startup useLegacyV2RuntimeActivationPolicy="true">
       <startup useLegacyV2RuntimeActivationPolicy="true">
Line 211: Line 210:
       </runtime>
       </runtime>
     </configuration>
     </configuration>
</source>
</syntaxhighlight>


When using procedural code instead of XAML you may want to define a one time <source lang=apl inline>⎕USING</source> like this:
When using procedural code instead of XAML you may want to define a one time <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> like this:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕USING←Using
       ⎕USING←Using
</source>
</syntaxhighlight>
{{Collapse|A typical definition of the function <source lang=apl inline>Using</source> is like this (contributed by Dyalog and Michael J. Hughes):|
{{Collapse|A typical definition of the function <syntaxhighlight lang=apl inline>Using</syntaxhighlight> is like this (contributed by Dyalog and Michael J. Hughes):|
<source lang=apl>
<syntaxhighlight lang=apl>
  use←Using
  use←Using
  ⍝ Full ⎕Using used by system.
  ⍝ Full ⎕Using used by system.
Line 256: Line 255:
  use,←⊂'System.Objects'
  use,←⊂'System.Objects'
  use,←⊂'System.Globalization'
  use,←⊂'System.Globalization'
</source>
</syntaxhighlight>
}}
}}
To add a new definition to an existing <source lang=apl inline>⎕USING</source> and to prevent duplicate entries the 'Union' operator is used like this:
To add a new definition to an existing <syntaxhighlight lang=apl inline>⎕USING</syntaxhighlight> and to prevent duplicate entries the 'Union' operator is used like this:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕USING∪←⊂'System.Windows.Controls,PresentationFramework.dll'
       ⎕USING∪←⊂'System.Windows.Controls,PresentationFramework.dll'
</source>
</syntaxhighlight>
=== Fixing Images ===
=== Fixing Images ===
In Xaml you declare an Image object that is on disk the following way:
In XAML you declare an Image object that is on disk the following way:
<source lang=apl>
<syntaxhighlight lang=apl>
<Image x:Name="MyImageName"
<Image x:Name="MyImageName"
       Source="PathOfMyImage\MyImage.png"  ⍝ PathOfMyImage can be tricky to declare sometimes and is not discussed here.
       Source="PathOfMyImage\MyImage.png"  ⍝ PathOfMyImage can be tricky to declare sometimes and is not discussed here.
       Width="24"
       Width="24"
       Height="24"/>
       Height="24"/>
</source>
</syntaxhighlight>
When you want to keep the Image definition in the APL workspace (because it is easier that way to distribute the workspace or the namespace) one way of doing it is by keeping a '''Base64''' definition of the Image. '''Base64''' encoding is using a set of 64 visible characters to encode binary data.
When you want to keep the Image definition in the APL workspace (because it is easier that way to distribute the workspace or the namespace) one way of doing it is by keeping a [[wikipedia:Base64|Base64]] definition of the Image. Base64 encoding is using a set of 64 visible characters to encode binary data.


It is widely used on the internet, for example in emails for images and binary attachments definition of Base64 on <ref>[http://en.wikipedia.org/wiki/Base64 Wikipedia: Base64]</ref>).
It is widely used on the internet, for example in emails for images and binary attachments.


Here are the steps to use this technique with APL:
Here are the steps to use this technique with APL:


==== Step 1: Save all the images in the workspace with the function FileToBase64String ====
==== Step 1: Save all the images in the workspace with the function FileToBase64String ====
At design time, save the images in the workspace.  The APL variable name of any image must be the original name of the image name in the Xaml with the added suffix '''_b64''' (naming convention only).
At design time, save the images in the workspace.  The APL variable name of any image must be the original name of the image name in the XAML with the added suffix '''_b64''' (naming convention only).
<source lang=apl>
<syntaxhighlight lang=apl>
       Paste_b64 ← FileToBase64String 'D:\Paste.png'
       Paste_b64 ← FileToBase64String 'D:\Paste.png'
       Copy_b64  ← FileToBase64String 'D:\Copy.png'
       Copy_b64  ← FileToBase64String 'D:\Copy.png'
       Cut_b64  ← FileToBase64String 'D:\Cut.png'
       Cut_b64  ← FileToBase64String 'D:\Cut.png'
</source>
</syntaxhighlight>
The variable <source lang=apl inline>Copy_b64</source> in the attached namespace looks like this:
The variable <syntaxhighlight lang=apl inline>Copy_b64</syntaxhighlight> in the attached namespace looks like this:
<source lang=apl>
<syntaxhighlight lang=apl>
       Copy_b64
       Copy_b64
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAaVBMVEX///8AAAC2tra2tra2tra2trb////+/v62trb9/f309PT7+/vExMSurq6Hh4f4+Pj8/Pz5+fnMzMyenp6/zduoqKilpaXNzc2xsbHu7u6Li4vk5OTa2tro6Oj6+vrm5uaMjIympqby8vJPA9lJAAAABnRSTlMAAM8Q7zCjkYU+AAAAiklEQVR4XoXM2Q7CMAxE0bqAk+7s+/7/H4mJMGNXQdzXo5mCiKK2X01nlCpSkbXdspxk4VCLOHBvDvwbYPSWgxu/JQMn5rotMzA8L+d24wAFB+tvzEeFIGDr/0LlrjwEE2D+CxoZ4aoCLASQgau8mQAsb7hqFLp7L28mBSKKPJgS0AdcgO6xtQm8AN3LEZUq6MiXAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAaVBMVEX///8AAAC2tra2tra2tra2trb////+/v62trb9/f309PT7+/vExMSurq6Hh4f4+Pj8/Pz5+fnMzMyenp6/zduoqKilpaXNzc2xsbHu7u6Li4vk5OTa2tro6Oj6+vrm5uaMjIympqby8vJPA9lJAAAABnRSTlMAAM8Q7zCjkYU+AAAAiklEQVR4XoXM2Q7CMAxE0bqAk+7s+/7/H4mJMGNXQdzXo5mCiKK2X01nlCpSkbXdspxk4VCLOHBvDvwbYPSWgxu/JQMn5rotMzA8L+d24wAFB+tvzEeFIGDr/0LlrjwEE2D+CxoZ4aoCLASQgau8mQAsb7hqFLp7L28mBSKKPJgS0AdcgO6xtQm8AN3LEZUq6MiXAAAAAElFTkSuQmCC
</source>
</syntaxhighlight>


This format is perfect for scripted namespaces; compare this to storing the image with the original file values that would have some non-visual characters.
This format is perfect for scripted namespaces; compare this to storing the image with the original file values that would have some non-visual characters.
==== Step 2: Set the .Source of each Image with the function ImageFromBase64String ====
==== Step 2: Set the .Source of each Image with the function ImageFromBase64String ====
At run time, after obtaining the root object set the <source lang=apl inline>.Source</source> of each Image from the previously saved APL variable.
At run time, after obtaining the root object set the <syntaxhighlight lang=apl inline>.Source</syntaxhighlight> of each Image from the previously saved APL variable.
<source lang=apl>
<syntaxhighlight lang=apl>
       win ← FixSimpleXaml sample3
       win ← FixSimpleXaml sample3


Line 298: Line 297:
       win.Copy_b64.Source  ← ImageFromBase64String Copy_b64
       win.Copy_b64.Source  ← ImageFromBase64String Copy_b64
       win.Cut_b64.Source  ← ImageFromBase64String Cut_b64
       win.Cut_b64.Source  ← ImageFromBase64String Cut_b64
</source>
</syntaxhighlight>
If there are many images in the Xaml, the following lines of code can automate the process at run time:
If there are many images in the XAML, the following lines of code can automate the process at run time:
<source lang=apl>
<syntaxhighlight lang=apl>
     ⍝ Get the names of all the 'Image' object that has been fixed in the root object
     ⍝ Get the names of all the 'Image' object that has been fixed in the root object
       imgNames ← {((⊂'Image')≡¨(⍵.⍎¨(⍵.⎕NL-9)).GetType.Name)/(⍵.⎕NL-9)} win
       imgNames ← {((⊂'Image')≡¨(⍵.⍎¨(⍵.⎕NL-9)).GetType.Name)/(⍵.⎕NL-9)} win
Line 307: Line 306:
     ⍝ Base64 variable ending with '_b64' in the APL workspace.
     ⍝ Base64 variable ending with '_b64' in the APL workspace.
       win {(⍺.⍎⍵).Source←ImageFromBase64String⍎⍵}¨ imgNames
       win {(⍺.⍎⍵).Source←ImageFromBase64String⍎⍵}¨ imgNames
</source>
</syntaxhighlight>
The icon and the cursor of the main window can be fixed manually by doing the following:
The icon and the cursor of the main window can be fixed manually by doing the following:
<source lang=apl>
<syntaxhighlight lang=apl>
     ⍝ Fix manually the Icon of the main window.
     ⍝ Fix manually the Icon of the main window.
     win.Icon ← ImageFromBase64String Settings_b64
     win.Icon ← ImageFromBase64String Settings_b64
Line 315: Line 314:
     ⍝ Fix manually the Cursor of the main window.
     ⍝ Fix manually the Cursor of the main window.
     win.Cursor ← CursorFromBase64String HandCursor_b64
     win.Cursor ← CursorFromBase64String HandCursor_b64
</source>
</syntaxhighlight>
 
=== Routed Events ===
=== Routed Events ===
In WPF it is possible to set a single function that will receive all the Click events on the window (in this example it is <source lang=apl inline>__EventHandler</source>) by doing:
In WPF it is possible to set a single function that will receive all the Click events on the window (in this example it is <syntaxhighlight lang=apl inline>__EventHandler</syntaxhighlight>) by doing:
<source lang=apl>
<syntaxhighlight lang=apl>
   ⍝ Set Routed Events on the whole Window for ClickEvent when a MenuItem or a Button are clicked.
   ⍝ Set Routed Events on the whole Window for ClickEvent when a MenuItem or a Button are clicked.
     ⎕USING←'System.Windows,WPF/PresentationCore.dll' 'System.Windows.Controls.Primitives,WPF/PresentationFramework.dll'
     ⎕USING←'System.Windows,WPF/PresentationCore.dll' 'System.Windows.Controls.Primitives,WPF/PresentationFramework.dll'
     win.AddHandler(Controls.MenuItem.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
     win.AddHandler(Controls.MenuItem.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
     win.AddHandler(ButtonBase.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
     win.AddHandler(ButtonBase.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
</source>
</syntaxhighlight>
This is useful because that way you don't need to define individual click events for each control.   
This is useful because that way you don't need to define individual click events for each control.   
{{Collapse|The function <source lang=apl inline>__EventHandler</source> will handle all the click events on the window code (the double underscore prefix is not necessary but is kept for naming consistency)|
{{Collapse|The function <syntaxhighlight lang=apl inline>__EventHandler</syntaxhighlight> will handle all the click events on the window code (the double underscore prefix is not necessary but is kept for naming consistency)|
<source lang=apl>
<syntaxhighlight lang=apl>
  __EventHandler(sender event);name
  __EventHandler(sender event);name
⍝ Single Event Handler for the Window
⍝ Single Event Handler for the Window
Line 360: Line 360:
     'Error: Unknow Name'
     'Error: Unknow Name'
  :EndSelect
  :EndSelect
</source>
</syntaxhighlight>
}}
}}


When all this is done the window can be shown:
When all this is done the window can be shown:
<source lang=apl>
<syntaxhighlight lang=apl>
       win.Show
       win.Show
</source>
</syntaxhighlight>
{{Collapse|The function <source lang=apl inline>DemoSample3</source> contains all the code related to '''sample3'''|
{{Collapse|The function <syntaxhighlight lang=apl inline>DemoSample3</syntaxhighlight> contains all the code related to '''sample3'''|
<source lang=apl>
<syntaxhighlight lang=apl>
     ∇ DemoSample3;imgNames
     ∇ DemoSample3;imgNames


Line 394: Line 394:
       win.Show
       win.Show
     ∇
     ∇
</source>
</syntaxhighlight>
}}
}}


{{Collapse|The function <source lang=apl inline>ScrubAndFix</source> will remove all the events from the Xaml by looping on a <source lang=apl inline>XamlXmlReader</source> object. It is slower than <source lang=apl inline>FixXaml</source> but the events don't need to have a specific prefix.  Perfect for experimenting with the Xaml taken directly from the Web.  All the named objects are attached to the root object|
{{Collapse|The function <syntaxhighlight lang=apl inline>ScrubAndFix</syntaxhighlight> will remove all the events from the XAML by looping on a <syntaxhighlight lang=apl inline>XamlXmlReader</syntaxhighlight> object. It is slower than <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight> but the events don't need to have a specific prefix.  Perfect for experimenting with the XAML taken directly from the Web.  All the named objects are attached to the root object|
<source lang=apl>
<syntaxhighlight lang=apl>
     ∇ rootObj←ScrubAndFix xamlString;err;names;nodeNumber;reader;stringReader;writer;⎕USING
     ∇ rootObj←ScrubAndFix xamlString;err;names;nodeNumber;reader;stringReader;writer;⎕USING
     ⍝ Function to remove the Class and Events elements of Xaml and fix the resulting Xaml.
     ⍝ Function to remove the Class and Events elements of XAML and fix the resulting XAML.
     ⍝ Each named element(s) object will be attached to rootObject.
     ⍝ Each named element(s) object will be attached to rootObject.
     ⍝ Root element name is removed in all cases. Use the rootObj directly to access its properties/methods.
     ⍝ Root element name is removed in all cases. Use the rootObj directly to access its properties/methods.
     ⍝ This function is most usefull when experimenting with Xaml taken directly from the Web but is slower than FixXaml.
     ⍝ This function is most usefull when experimenting with XAML taken directly from the Web but is slower than FixXaml.
     ⍝ Inspired from the code of XAMLPAD2009.
     ⍝ Inspired from the code of XAMLPAD2009.
     ⍝
     ⍝
Line 412: Line 412:
       :Trap 0
       :Trap 0


           ⎕USING,←'System.IO' 'System.Windows.Markup,WPF/PresentationFramework.dll' 'System.Xaml,System.Xaml.dll'
           ⎕USING,←'System.IO' 'System.Windows.Markup,WPF/PresentationFramework.dll' 'System.XAML,System.XAML.dll'
           stringReader←⎕NEW StringReader(⊂xamlString)
           stringReader←⎕NEW StringReader(⊂xamlString)
           reader←⎕NEW XamlXmlReader(stringReader XamlReader.GetWpfSchemaContext)
           reader←⎕NEW XamlXmlReader(stringReader XamlReader.GetWpfSchemaContext)
Line 494: Line 494:
       :EndTrap
       :EndTrap
     ∇
     ∇
</source>
</syntaxhighlight>
}}
}}


The User Command [[wpfXamlEditor||]] is designed to edit the Xaml saved in the workspace and on disk.
The User Command [https://old.aplwiki.com/wpfXamlEditor wpfXamlEditor] is designed to edit the XAML saved in the workspace and on disk.


=== Inserting WPF Controls into Traditional Dyalog Windows ===
=== Inserting WPF Controls into Traditional Dyalog Windows ===
You can insert a WPF control into an already existing application developed with <source lang=apl inline>⎕WC</source> by using an [[https://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost(v=vs.110).aspx|ElementHost]] and a Dyalog's <source lang=apl inline>NetControl</source>. Here is an example of how to insert a WPF Button:
You can insert a WPF control into an already existing application developed with <syntaxhighlight lang=apl inline>⎕WC</syntaxhighlight> by using an [https://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost(v=vs.110).aspx ElementHost] and a Dyalog's <syntaxhighlight lang=apl inline>NetControl</syntaxhighlight>. Here is an example of how to insert a WPF Button:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕USING∪←⊂'System.Windows.Forms.Integration,WPF/WindowsFormsIntegration.dll'  ⍝ Location of 'ElementHost'
       ⎕USING∪←⊂'System.Windows.Forms.Integration,WPF/WindowsFormsIntegration.dll'  ⍝ Location of 'ElementHost'
       ⎕USING∪←⊂'System.Windows.Controls,WPF/PresentationFramework.dll'            ⍝ Location of WPF Button
       ⎕USING∪←⊂'System.Windows.Controls,WPF/PresentationFramework.dll'            ⍝ Location of WPF Button
Line 513: Line 513:
       bn.onClick ← '__Button_Click'
       bn.onClick ← '__Button_Click'
       F.eh.Child ← bn
       F.eh.Child ← bn
</source>
</syntaxhighlight>
Instead of a Button, you could use some complex XAML developed with Visual Studio. You just need to fix the XAML with <source lang=apl inline>FixXaml</source> and make it the child of the <source lang=apl inline>ElementHost</source> element.
Instead of a Button, you could use some complex XAML developed with Visual Studio. You just need to fix the XAML with <syntaxhighlight lang=apl inline>FixXaml</syntaxhighlight> and make it the child of the <syntaxhighlight lang=apl inline>ElementHost</syntaxhighlight> element.


=== How to Access the UI Thread from Another Thread ===
=== How to Access the UI Thread from Another Thread ===
Since Net 2.0 Microsoft does not allow writing on the UI thread from another thread for security and stability reasons. Consequently, if you are executing a long calculation on another thread and you want to show the results by accessing directly the UI thread it is not possible.
Since Net 2.0 Microsoft does not allow writing on the UI thread from another thread for security and stability reasons. Consequently, if you are executing a long calculation on another thread and you want to show the results by accessing directly the UI thread it is not possible.
==== With a Delegate ====
==== With a Delegate ====
{{Collapse|You need to use a little function with the code to be executed that will be put in queue to be executed on the UI thread. This is called a 'Delegate' by Microsoft. The function <source lang=apl inline>DispatchDelegate</source> is an example of how to do this|
{{Collapse|You need to use a little function with the code to be executed that will be put in queue to be executed on the UI thread. This is called a 'Delegate' by Microsoft. The function <syntaxhighlight lang=apl inline>DispatchDelegate</syntaxhighlight> is an example of how to do this|
<source lang=apl>
<syntaxhighlight lang=apl>
  obj DispatchDelegate action;delegate;⎕USING
  obj DispatchDelegate action;delegate;⎕USING
⍝ Function to write asynchronously to the UI thread from another thread.
⍝ Function to write asynchronously to the UI thread from another thread.
Line 550: Line 550:
     :EndIf
     :EndIf
  :End
  :End
</source>
</syntaxhighlight>
}}
}}
Here is an example of how to use that function:
Here is an example of how to use that function:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
       ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
       win ← ⎕NEW Window
       win ← ⎕NEW Window
Line 560: Line 560:


       win DispatchDelegate& 'win.Title←''MyDelegateTitle'''  ⍝ The .Title property is changed from another thread
       win DispatchDelegate& 'win.Title←''MyDelegateTitle'''  ⍝ The .Title property is changed from another thread
</source>
</syntaxhighlight>
{{Collapse|If you have many lines that need to be executed in the UI thread the function <source lang=apl inline>ScriptFollowsDispatchDelegate</source> can be used like this|
{{Collapse|If you have many lines that need to be executed in the UI thread the function <syntaxhighlight lang=apl inline>ScriptFollowsDispatchDelegate</syntaxhighlight> can be used like this|
<source lang=apl>
<syntaxhighlight lang=apl>
  ScriptFollowsDispatchDelegate obj;actions;delegate;dtlb;⎕IO;⎕ML;⎕USING
  ScriptFollowsDispatchDelegate obj;actions;delegate;dtlb;⎕IO;⎕ML;⎕USING
⍝ Function to write asynchronously to the UI thread from another thread.
⍝ Function to write asynchronously to the UI thread from another thread.
Line 599: Line 599:
     :EndIf
     :EndIf
  :End
  :End
</source>
</syntaxhighlight>
}}
}}
<source lang=apl>
<syntaxhighlight lang=apl>
[3]
[3]
[4]  ⍝ ... long running process on another thread ...
[4]  ⍝ ... long running process on another thread ...
Line 610: Line 610:
[9]  ⍝∇ line 3 to by executed in the UI thread
[9]  ⍝∇ line 3 to by executed in the UI thread
[10] ⍝∇ line 4 to by executed in the UI thread
[10] ⍝∇ line 4 to by executed in the UI thread
</source>
</syntaxhighlight>
The author has taken the prefix '⍝∇' instead of '⍝' because in production code you will probably want to erase all the comments in your runtime WS because they are useless and it is helping to obfuscate the code while taking less space.
The author has taken the prefix '⍝∇' instead of '⍝' because in production code you will probably want to erase all the comments in your runtime WS because they are useless and it is helping to obfuscate the code while taking less space.
==== With a DispatcherTimer ====
==== With a DispatcherTimer ====
If you have a repetitive task to be executed on the UI thread then you can use a [[https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer(v=vs.110).aspx|DispatcherTimer]] like this:
If you have a repetitive task to be executed on the UI thread then you can use a [https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer(v=vs.110).aspx DispatcherTimer] like this:
<source lang=apl>
<syntaxhighlight lang=apl>
     ⎕USING∪←'System.Windows,WPF/WindowsBase.dll' 'System,mscorlib.dll'
     ⎕USING∪←'System.Windows,WPF/WindowsBase.dll' 'System,mscorlib.dll'
     tm1_obj ← ⎕NEW Threading.DispatcherTimer(Threading.DispatcherPriority.Normal myObj.Dispatcher)
     tm1_obj ← ⎕NEW Threading.DispatcherTimer(Threading.DispatcherPriority.Normal myObj.Dispatcher)
Line 620: Line 620:
     tm1_obj.onTick  ← '#.MyFunction'      ⍝ Function to be executed every 1 second on the Dispatcher of the object 'myObj'
     tm1_obj.onTick  ← '#.MyFunction'      ⍝ Function to be executed every 1 second on the Dispatcher of the object 'myObj'
     tm1_obj.Start
     tm1_obj.Start
</source>
</syntaxhighlight>
Note that since the <source lang=apl inline>DispatcherTimer</source> is executed on the UI thread you cannot have a long callback because it will freeze the UI during its execution.
Note that since the <syntaxhighlight lang=apl inline>DispatcherTimer</syntaxhighlight> is executed on the UI thread you cannot have a long callback because it will freeze the UI during its execution.
==== With a BackgroundWorker ====
==== With a BackgroundWorker ====
The .NET framework provides a simple way to get started in threading with the [[https://msdn.microsoft.com/fr-fr/library/cc221403(v=vs.95).aspx|BackgroundWorker]] component. This wraps much of the complexity and makes spawning a background thread relatively safe. It offers several features which include spawning a background thread, the ability to cancel the background process before it has completed, and the chance to report the progress back to your UI. The <source lang=apl inline>BackgroundWorker</source> is an excellent tool when you want multithreading in your application with access to the UI thread, mainly because it's so easy to use.
The .NET framework provides a simple way to get started in threading with the [https://msdn.microsoft.com/fr-fr/library/cc221403(v=vs.95).aspx BackgroundWorker] component. This wraps much of the complexity and makes spawning a background thread relatively safe. It offers several features which include spawning a background thread, the ability to cancel the background process before it has completed, and the chance to report the progress back to your UI. The <syntaxhighlight lang=apl inline>BackgroundWorker</syntaxhighlight> is an excellent tool when you want multithreading in your application with access to the UI thread, mainly because it's so easy to use.


An example is included in this namespace. It is inspired by this [[http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/|article]].  You start the example by doing:
An example is included in this namespace. It is inspired by [http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/ this article].  You start the example by doing:
<source lang=apl>
<syntaxhighlight lang=apl>
       BackgroundWorkerSample& 1000
       BackgroundWorkerSample& 1000
</source>
</syntaxhighlight>
A window will appear and if you click on the 'Start' button, the UI will be refreshed with the multiple of 42 between 1 and 1000 while simulating a long calculation. Check the comments of this function with the links for more details.
A window will appear and if you click on the 'Start' button, the UI will be refreshed with the multiple of 42 between 1 and 1000 while simulating a long calculation. Check the comments of this function with the links for more details.


=== How to install wpfXamlDemo in your workspace ===
=== How to install wpfXamlDemo in your workspace ===


1. Download [[attachment:wpfXamlDemo.v1.6.txt]]
# Download [https://old.aplwiki.com/wpfXamlDemo?action=AttachFile&do=view&target=wpfXamlDemo.v1.6.txt wpfXamlDemo.v1.6.txt]
1. Do a Select all (Ctrl+A) and a copy (Ctrl+C).
# Do a Select all (Ctrl+A) and a copy (Ctrl+C).
1. In your workspace execute <source lang=apl inline>)ed ⍟ wpfXamlDemo </source>
# In your workspace execute <syntaxhighlight lang=apl inline>)ed ⍟ wpfXamlDemo </syntaxhighlight>
1. Paste (Ctrl+V) the text into the Dyalog editor
# Paste (Ctrl+V) the text into the Dyalog editor
1. Press Escape and ')save' your workspace
# Press Escape and ')save' your workspace


Optionally to de-script the namespace you can do:
Optionally to de-script the namespace you can do:
<source lang=apl>
<syntaxhighlight lang=apl>
     #.wpfXamlDemo←{('n' ⎕NS ⍵)⊢n←⎕NS ''}#.wpfXamlDemo
     #.wpfXamlDemo←{('n' ⎕NS ⍵)⊢n←⎕NS ''}#.wpfXamlDemo
</source>
</syntaxhighlight>
 
[[Category:Tutorials]][[Category:Dyalog APL examples]][[Category:.NET]]
== Version Information ==
Original author: [[User:PierreGilbert|Pierre Gilbert]]
----
[[Category:Dyalog]][[Category:DotNet]][[Category:WPF]]
 
<references />

Latest revision as of 13:13, 4 March 2024

This page gives a Dyalog APL demonstratration of WPF XAMPL with some utility functions, delivered in the form of a namespace.

Please read the Dyalog tutorials for a more detailed explanation. Part of the code was taken from CodeProject: A Beginner's Guide.

What is XAML?

XAML stands for Extensible Application Markup Language (and pronounced "Zammel"). It's a simple language based on wikipedia:XML to create and initialize .NET objects with hierarchical relations.

All you can do in XAML can also be done in code. XAML is just another way to create and initialize objects.

You can use WPF without using XAML. It's up to you whether you want to declare it in XAML or write it as code. There is a XAML Overview from Microsoft.

Here is an example of XAML found in the attached namespace called sample1:
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Height="200"
   Width="250"
   Title="MainWindow"
   WindowStartupLocation="CenterScreen">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="0.5*"/>
         <RowDefinition Height="0.5*"/>
      </Grid.RowDefinitions>
      <TextBox
         x:Name="textBox1"
         Width="130"
         HorizontalAlignment="Center"
         VerticalAlignment="Center"
         Text="TextBox"
         TextAlignment="Center"/>
      <Button
         x:Name="button1"
         Height="40"
         Width="120"
         Grid.Row="1"
         HorizontalAlignment="Center"
         VerticalAlignment="Center"
         Content="Click Me !"/>
   </Grid>
</Window>

FixSimpleXaml

If you install the attached namespace and execute the following 2 lines in your workspace:

      win ← FixSimpleXaml sample1
      win.Show

FixSimpleXaml is a function used to execute the XAML and return the root element as a .Net object. All the other elements that are named in the XAML will be attached to their names to the root object automatically.

For example, the element !TextBox that is named textBox1 (line 15) and the element Button that is named button1 (line 22) are attached automatically to the root element by the function FixSimpleXaml:

      win.textBox1.Text
Textbox
      win.button1
System.Windows.Controls.Button: Click Me !

That way you don't need to define a separate variable for each named element. If you install the user command called sfPropGrid you can see all the properties, methods and events of all the named objects by doing (click the combo of NOE to access all the named objects):

      ]noe win       ⍝ noe = .Net Object Explorer

In conclusion FixSimpleXaml is a simple function to use on simple XAML that does not have events and is properly formed. In production code you may want to do something like this:

 :If ⎕NULL≡myObject ← FixSimpleXaml myXaml
    ⍝ Fixing the XAML did not work. Show an error and exit.
     ⎕ ← 'Error Fixing XAML'
     →0
 :Else
    ⍝ There is no error.
 :EndIf

FixXaml

For cases where there are events that need to be fixed and have better error handling the function FixXaml is available. It is useful when using XAML taken directly from Visual Studio. For example, with the XAML code in sample2 that has an event on the button (Click="__Button_Click") at line 24, if you do the following:
<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   x:Class="WpfApplication3.MainWindow"
   Height="200"
   Width="250"
   Title="MainWindow"
   WindowStartupLocation="CenterScreen">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="0.5*"/>
         <RowDefinition Height="0.5*"/>
      </Grid.RowDefinitions>
      <TextBox
         x:Name="textBox1"
         Width="130"
         HorizontalAlignment="Center"
         VerticalAlignment="Center"
         Text="TextBox"
         TextAlignment="Center"/>
      <Button
         x:Name="button1"
         Click="__Button_Click"
         Height="40"
         Width="120"
         Grid.Row="1"
         HorizontalAlignment="Center"
         VerticalAlignment="Center"
         Content="Click Me !"/>
   </Grid>
</Window>
      win ← FixXaml sample2
      win.Show

and then if you click on the button, the value of the !TextBox will change. The value of the !TextBox can be retrieved simply by doing:

      win.textBox1.Text
I Was Clicked !

The function __Button_Click is handling the event. The author has taken the convention of naming the callback functions with a double underscore prefix.

The goal is to be able to take the XAML directly from Visual Studio to APL. The single underscore '_' is a valid first character in Visual Studio and APL but is in conflict with the menu object that will accept an underscore as the first character to define a keyboard shortcut.

The line 5 of sample2 (x:Class="!WpfApplication3.!MainWindow") that is required by Visual Studio is removed by FixXaml. See the comments in the function for more information.

In production code you may want to trap any error by using code like this:

 :If ⎕NULL≡↑myObject ← FixXaml myXaml
    ⍝ Fixing the XAML did not work. Show an error and exit.
     ⎕ ← 2⊃myObject
     →0
 :Else
    ⍝ There is no error.
 :EndIf

About ⎕USING

In general, when using XAML there is no need to define a ⎕USING before fixing it except when there is a 3rd party dll involved. For example, the variable NewWindow is defined as:

<Window
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Window>

and if you do:

      win ← FixSimpleXaml NewWindow
      win.Show

a Window will appear. In procedural code, the following is required for the same result:

      ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
      win ← ⎕NEW Window
      win.Show

The first element of XAML must contain the xmlns= and the xmlns:x= declarations. This is instructing the parser (in our case System.Windows.Markup.!XamlReader in the function FixSimpleXaml and FixXaml) to load a series of .NET namespaces required to parse the XAML. This is just a convention, there is actually no such web site.

3rd Party Dll

When using 3rd party dll, they must be added to the declaration in the first element of XAML. There are 2 choices on how to do it:

   xmlns:myname="clr‑namespace:MyNamespace;assembly=MyDllName"  ⍝ Notice that there is no '.dll' after MyDllName
or
   xmlns:myname="http://schemas.somewebsite.com/xaml"           ⍝ A website name given by the 3rd party dll (may not exists)

The method on the first line is recommended. Here is an example with the Syncfusion !PropertyGrid:

   xmlns:sf="clr-namespace:Syncfusion.Windows.PropertyGrid;assembly=Syncfusion.PropertyGrid.Wpf"  ⍝ Notice no .dll at the end

and the XAML will look like this:

   <sf:PropertyGrid x:Name="PGrid"/>  ⍝ Notice the prefix 'sf' is the same on both lines (you choose the prefix).

but this is not enough, ⎕USING must be set up correctly before fixing the XAML for 3rd party dlls in order for the parser to find the assembly. Here is an example for the Syncfusion !PropertyGrid (Syncfusion/4.5/ is the Dyalog sub-directory where the assemblies live):

     ⎕USING ← 'Syncfusion.Windows.PropertyGrid,Syncfusion/4.5/Syncfusion.PropertyGrid.Wpf.dll'    ⍝ The .dll is required

or more general

     ⎕USING ← 'MyNamespace,FullPathOfAssembly/MyDllName.dll'          ⍝ If the dll is outside of the dyalog.exe folder
     ⎕USING ← 'MyNamespace,SubDirectoryOfDyalogFolder/MyDllName.dll'  ⍝ If the dll is in a sub-directory of dyalog.exe
     ⎕USING ← 'MyNamespace,MyDllName.dll'                             ⍝ If the dll is in the same directory as dyalog.exe

Another thing with the value of ⎕USING for 3rd party dll is that it must be set in the __same namespace__ as where FixXaml or FixSimpleXaml is located (because ⎕USING is Namespace scope). Alternatively, if you setup your dyalog.exe.config file that is in the same directory as the dyalog.exe file with a directive to look in the Syncfusion/4.5 directory you will not need to set up ⎕USING and you don't need to worry about loading it into memory.

Typically the file will look like this:

    <configuration>
       <startup useLegacyV2RuntimeActivationPolicy="true">
          <supportedRuntime version="v4.0">
          </supportedRuntime>
       </startup>
       <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <probing privatePath="MJHSoftware;Syncfusion/4.5">
             </probing>
          </assemblyBinding>
          <!-- <NetFx40_LegacySecurityPolicy enabled="true"/> -->
          <loadFromRemoteSources enabled="true"/>
       </runtime>
    </configuration>

When using procedural code instead of XAML you may want to define a one time ⎕USING like this:

      ⎕USING←Using
A typical definition of the function Using is like this (contributed by Dyalog and Michael J. Hughes):
 use←Using
 ⍝ Full ⎕Using used by system.

 use←'' 'System,System.dll'
 use,←⊂'System.Windows,WPF/PresentationFramework.dll'
 use,←⊂'System.Windows,WPF/PresentationCore.dll'
 use,←⊂'System.Windows.Input,WPF/PresentationCore.dll'
 use,←⊂'System.Windows.Shapes'
 use,←⊂'System.Windows.MessageBox'
 use,←⊂'Microsoft.Win32,WPF/PresentationFramework.dll'
 use,←⊂'System.Windows.Controls'
 use,←⊂'System.Windows.Markup'
 use,←⊂'System.Windows.Navigation'
 use,←⊂'System.Windows.Media'
 use,←⊂'System.Windows.Media.Imaging'
 use,←⊂'System.Windows.Documents'
 use,←⊂'System.Drawing.Bitmap'
 use,←⊂'System.Windows.Controls.Primitives'
 use,←⊂'System.Windows.Data'
 use,←⊂'System.Data,system.data.dll'
 use,←⊂'System.Xml,system.xml.dll'
 use,←⊂'System.Windows.Automation.Peers'
 use,←⊂'System.Windows.Automation.Provider,WPF/UIAutomationProvider.dll'
 use,←⊂'System.Windows,WPF/WindowsBase.dll'
 use,←⊂'System.ComponentModel,System.dll'
 use,←⊂'System.Windows.Media.Animation,WPF/PresentationFramework.dll'
 use,←⊂'System.Windows.Media.Animation,WPF/PresentationCore.dll'
 use,←⊂'System.Printing,WPF/ReachFramework.dll'
 use,←⊂'System.Windows.Threading'
 use,←⊂'System.Threading'
 use,←⊂'System.IO'
 use,←⊂'System.Windows.SystemParameters'
 use,←⊂'System.Collections'
 use,←⊂'System.Collections.Generic'
 use,←⊂'System.Collections.ObjectModel'
 use,←⊂'System.Objects'
 use,←⊂'System.Globalization'

To add a new definition to an existing ⎕USING and to prevent duplicate entries the 'Union' operator is used like this:

      ⎕USING∪←⊂'System.Windows.Controls,PresentationFramework.dll'

Fixing Images

In XAML you declare an Image object that is on disk the following way:

<Image x:Name="MyImageName"
       Source="PathOfMyImage\MyImage.png"   ⍝ PathOfMyImage can be tricky to declare sometimes and is not discussed here.
       Width="24"
       Height="24"/>

When you want to keep the Image definition in the APL workspace (because it is easier that way to distribute the workspace or the namespace) one way of doing it is by keeping a Base64 definition of the Image. Base64 encoding is using a set of 64 visible characters to encode binary data.

It is widely used on the internet, for example in emails for images and binary attachments.

Here are the steps to use this technique with APL:

Step 1: Save all the images in the workspace with the function FileToBase64String

At design time, save the images in the workspace. The APL variable name of any image must be the original name of the image name in the XAML with the added suffix _b64 (naming convention only).

      Paste_b64 ← FileToBase64String 'D:\Paste.png'
      Copy_b64  ← FileToBase64String 'D:\Copy.png'
      Cut_b64   ← FileToBase64String 'D:\Cut.png'

The variable Copy_b64 in the attached namespace looks like this:

      Copy_b64
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAaVBMVEX///8AAAC2tra2tra2tra2trb////+/v62trb9/f309PT7+/vExMSurq6Hh4f4+Pj8/Pz5+fnMzMyenp6/zduoqKilpaXNzc2xsbHu7u6Li4vk5OTa2tro6Oj6+vrm5uaMjIympqby8vJPA9lJAAAABnRSTlMAAM8Q7zCjkYU+AAAAiklEQVR4XoXM2Q7CMAxE0bqAk+7s+/7/H4mJMGNXQdzXo5mCiKK2X01nlCpSkbXdspxk4VCLOHBvDvwbYPSWgxu/JQMn5rotMzA8L+d24wAFB+tvzEeFIGDr/0LlrjwEE2D+CxoZ4aoCLASQgau8mQAsb7hqFLp7L28mBSKKPJgS0AdcgO6xtQm8AN3LEZUq6MiXAAAAAElFTkSuQmCC

This format is perfect for scripted namespaces; compare this to storing the image with the original file values that would have some non-visual characters.

Step 2: Set the .Source of each Image with the function ImageFromBase64String

At run time, after obtaining the root object set the .Source of each Image from the previously saved APL variable.

      win ← FixSimpleXaml sample3

      win.Paste_b64.Source ← ImageFromBase64String Paste_b64
      win.Copy_b64.Source  ← ImageFromBase64String Copy_b64
      win.Cut_b64.Source   ← ImageFromBase64String Cut_b64

If there are many images in the XAML, the following lines of code can automate the process at run time:

    ⍝ Get the names of all the 'Image' object that has been fixed in the root object
      imgNames ← {((⊂'Image')≡¨(⍵.⍎¨(⍵.⎕NL-9)).GetType.Name)/(⍵.⎕NL-9)} win

    ⍝ Set the Source of all the 'Image'. Each 'Image' names must have an equivalent
    ⍝ Base64 variable ending with '_b64' in the APL workspace.
      win {(⍺.⍎⍵).Source←ImageFromBase64String⍎⍵}¨ imgNames

The icon and the cursor of the main window can be fixed manually by doing the following:

    ⍝ Fix manually the Icon of the main window.
     win.Icon ← ImageFromBase64String Settings_b64

    ⍝ Fix manually the Cursor of the main window.
     win.Cursor ← CursorFromBase64String HandCursor_b64

Routed Events

In WPF it is possible to set a single function that will receive all the Click events on the window (in this example it is __EventHandler) by doing:

  ⍝ Set Routed Events on the whole Window for ClickEvent when a MenuItem or a Button are clicked.
    ⎕USING←'System.Windows,WPF/PresentationCore.dll' 'System.Windows.Controls.Primitives,WPF/PresentationFramework.dll'
    win.AddHandler(Controls.MenuItem.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
    win.AddHandler(ButtonBase.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))

This is useful because that way you don't need to define individual click events for each control.

The function __EventHandler will handle all the click events on the window code (the double underscore prefix is not necessary but is kept for naming consistency)
 __EventHandler(sender event);name
⍝ Single Event Handler for the Window

⍝ Get name of the control that was clicked
 name←event.Source.Name

⍝ sender is the Root Object in this case because
⍝ the routed event was attached to it.
 sender.lStatusBar.Content←'I Was Clicked: ',name

⍝ Select the code to be executed:
 :Select name
 :Case 'mnuCut'

 :Case 'mnuCopy'

 :Case 'mnuPaste'

 :Case 'mnuPrint'

 :Case 'mnuQuit'

 :Case 'mnuAbout'

 :Case 'btnButton1'

 :Case 'btnButton2'

 :Case 'btnButton3'

 :Else
     'Error: Unknow Name'
 :EndSelect

When all this is done the window can be shown:

      win.Show
The function DemoSample3 contains all the code related to sample3
    ∇ DemoSample3;imgNames

     ⍝ Parse the xaml of the variable sample3
      win←FixSimpleXaml sample3

      ⍝ Get the names of all the 'Image' object that has been fixed in rootObj
      imgNames←{((⊂'Image')≡¨(⍵.⍎¨(⍵.⎕NL-9)).GetType.Name)/(⍵.⎕NL-9)}win

     ⍝ Set the Source of all the 'Image'. Each 'Image' must have a Base64 variable of the same name.
      win{(⍺.⍎⍵).Source←ImageFromBase64String⍎⍵}¨imgNames

     ⍝ Fix manually the Icon of the main window.
      win.Icon←ImageFromBase64String Settings_b64

     ⍝ Fix manually the Cursor of the main window.
      win.Cursor←CursorFromBase64String HandCursor_b64

     ⍝ Set Routed Events on the whole Window for ClickEvent when a MenuItem or a Button are clicked.
      ⎕USING←'System.Windows,WPF/PresentationCore.dll' 'System.Windows.Controls.Primitives,WPF/PresentationFramework.dll'
      win.AddHandler(Controls.MenuItem.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))
      win.AddHandler(ButtonBase.ClickEvent)(⎕NEW RoutedEventHandler(⎕OR'__EventHandler'))

     ⍝ Show the window.
      win.Show
    ∇
The function ScrubAndFix will remove all the events from the XAML by looping on a XamlXmlReader object. It is slower than FixXaml but the events don't need to have a specific prefix. Perfect for experimenting with the XAML taken directly from the Web. All the named objects are attached to the root object
    ∇ rootObj←ScrubAndFix xamlString;err;names;nodeNumber;reader;stringReader;writer;⎕USING
    ⍝ Function to remove the Class and Events elements of XAML and fix the resulting XAML.
    ⍝ Each named element(s) object will be attached to rootObject.
    ⍝ Root element name is removed in all cases. Use the rootObj directly to access its properties/methods.
    ⍝ This function is most usefull when experimenting with XAML taken directly from the Web but is slower than FixXaml.
    ⍝ Inspired from the code of XAMLPAD2009.
    ⍝
    ⍝ XamlString = Vector of Characters representing a XAML string
    ⍝ rootObj    = WPF Root Element Object if no error
    ⍝            = ⎕NULL (explanation of error) if error

      :Trap 0

          ⎕USING,←'System.IO' 'System.Windows.Markup,WPF/PresentationFramework.dll' 'System.XAML,System.XAML.dll'
          stringReader←⎕NEW StringReader(⊂xamlString)
          reader←⎕NEW XamlXmlReader(stringReader XamlReader.GetWpfSchemaContext)
          writer←⎕NEW XamlObjectWriter(reader.SchemaContext)

          ⎕USING←0⍴⊂''
          names←''         ⍝ Initial value of the names to fix.
          nodeNumber←0     ⍝ Initial value of the node number.

          :While reader.Read

              :If 'StartObject'≡⍕reader.NodeType
                ⍝ Will increment of 1 each time a new node (with a new object) begins.
                  nodeNumber+←1
              :End

              :If 'StartMember'≡⍕reader.NodeType
              :AndIf reader.Member.Name≡'Class'
                  {}reader.Read
                  {}reader.Read

              :ElseIf 'StartMember'≡⍕reader.NodeType
              :AndIf reader.Member.IsEvent
                  {}reader.Read
                  {}reader.Read

              :ElseIf 'StartMember'≡⍕reader.NodeType
              :AndIf reader.Member.Name≡'Name'
                  writer.WriteNode(reader)
                  {}reader.Read
                  writer.WriteNode(reader)

                ⍝ Check if reader is on first node
                  :If 1≠nodeNumber
                    ⍝ reader is not on first node. Keep the name.
                      names,←⊂reader.Value
                  :Else
                    ⍝ reader is on first node. Don't keep the name.
                  :End

                  {}reader.Read
                  writer.WriteNode(reader)

              :Else
                  writer.WriteNode(reader)

              :End

          :EndWhile

          rootObj←writer.Result

         ⍝ Assign the value of each names found to the root object:
          :If 0≠⍴names
              rootObj{⍎'⍺.',⍵,'←⍺.FindName(⊂,⍵)'}¨names
          :Else
             ⍝ Do nothing, there is no names.
          :End

      :Else
        ⍝ Build the Error Message
          :If 90≠⎕EN
            ⍝ APL Error
              err←(1⊃⎕DM),': ',{(' '=1↑⍵)↓((1↓a,0)∨a←' '≠⍵)/⍵}(2⊃⎕DM),(⎕UCS 13)

          :Else
             ⍝ .Net Error
              :Trap 0
                ⍝ Show the value of ⎕EXCEPTION.Message if not ⎕NULL.
                  err←('EXCEPTION: ',⎕EXCEPTION.Message),(⎕UCS 13)
              :Else
                ⍝ Sometimes ⎕EXCEPTION is (NULL) and will bug here.
                  err←'Exception (NULL): Unknown error',(⎕UCS 13)

              :EndTrap

          :End

          rootObj←⎕NULL err

      :EndTrap
    ∇

The User Command wpfXamlEditor is designed to edit the XAML saved in the workspace and on disk.

Inserting WPF Controls into Traditional Dyalog Windows

You can insert a WPF control into an already existing application developed with ⎕WC by using an ElementHost and a Dyalog's NetControl. Here is an example of how to insert a WPF Button:

      ⎕USING∪←⊂'System.Windows.Forms.Integration,WPF/WindowsFormsIntegration.dll'  ⍝ Location of 'ElementHost'
      ⎕USING∪←⊂'System.Windows.Controls,WPF/PresentationFramework.dll'             ⍝ Location of WPF Button

      'F' ⎕WC 'Form' ('Caption' 'My WPF Button')('Posn' (10 10))('Size'(10 20))    ⍝ Create a Form
      'F.eh' ⎕WC 'NetControl' 'ElementHost' ('Posn'(10 10))('Size'(15 20))         ⍝ Add 'ElementHost'

     ⍝ Prepare the WPF Button, and make it the 'child' property of the 'ElementHost' control
      bn ← ⎕NEW Button
      bn.Content ← 'Click Me'
      bn.onClick ← '__Button_Click'
      F.eh.Child ← bn

Instead of a Button, you could use some complex XAML developed with Visual Studio. You just need to fix the XAML with FixXaml and make it the child of the ElementHost element.

How to Access the UI Thread from Another Thread

Since Net 2.0 Microsoft does not allow writing on the UI thread from another thread for security and stability reasons. Consequently, if you are executing a long calculation on another thread and you want to show the results by accessing directly the UI thread it is not possible.

With a Delegate

You need to use a little function with the code to be executed that will be put in queue to be executed on the UI thread. This is called a 'Delegate' by Microsoft. The function DispatchDelegate is an example of how to do this
 obj DispatchDelegate action;delegate;⎕USING
⍝ Function to write asynchronously to the UI thread from another thread.
⍝ The calling thread does not wait for the operation to finish.

⍝ obj    = UI Object that you want to write to from another thread
⍝ action = Line to execute in the UI thread. Use '⋄' to write multiple lines.

 :Trap 0
     ⎕USING←'System,System.dll' 'System.Windows.Threading,WPF/WindowsBase.dll'

    ⍝ Create a function (⍙Delegate) with the 'action' to execute in the UI thread.
    ⍝ The function will be executed where 'DispatchDelegate' is called and not where
    ⍝ 'DispatchDelegate' is located.
     delegate←(⎕IO⊃⎕RSI).⎕OR(⎕IO⊃⎕RSI).⎕FX'⍙Delegate'action'⎕EX ''⍙Delegate'''

    ⍝ Get a 'Dispatcher' from the UI object. That way we are sure to have the right UI thread.
    ⍝ Use the method '.Invoke' instead of '.BeginInvoke' for synchronous call.
     {}obj.Dispatcher.BeginInvoke(DispatcherPriority.Normal(⎕NEW Action delegate))

 :Else
     ⎕←'Error DispatchDelegate: ',action

   ⍝ Returns the Last Error
     :If 90=⎕EN
         ⎕←'EXCEPTION: ',⎕EXCEPTION.Message
     :Else
         ⎕←(1⊃⎕DM),': ',(2⊃⎕DM)
     :EndIf
 :End

Here is an example of how to use that function:

      ⎕USING←'System.Windows,WPF/PresentationFramework.dll'
      win ← ⎕NEW Window
      win.Show
      win.Title ← 'MyTitle'

      win DispatchDelegate& 'win.Title←''MyDelegateTitle'''  ⍝ The .Title property is changed from another thread
If you have many lines that need to be executed in the UI thread the function ScriptFollowsDispatchDelegate can be used like this
 ScriptFollowsDispatchDelegate obj;actions;delegate;dtlb;⎕IO;⎕ML;⎕USING
⍝ Function to write asynchronously to the UI thread from another thread.
⍝ The calling thread does not wait for the operation to finish.
⍝ Treat following commented lines in caller as a script.
⍝ Lines beginning with ⍝∇ are kept
⍝ Lines beginning with ⍝∇⍝ are stripped out (comments)

⍝ obj     = UI Object that you want to write to from another thread
⍝ actions = Lines to execute in the UI thread.

 (⎕IO ⎕ML)←1 3

 :Trap 0
     ⎕USING←'System,System.dll' 'System.Windows.Threading,WPF/WindowsBase.dll'

    ⍝ Create a function (⍙Delegate) with the 'action' to execute in the UI thread.
    ⍝ The function will be executed where 'DispatchDelegate' is called and not where
    ⍝ 'DispatchDelegate' is located.
     dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}
     actions←{{'⍝'=↑⍵:'' ⋄ ' ',dtlb ⍵}¨2↓¨⍵/⍨∧\(⊂'⍝∇')≡¨2↑¨⍵}dtlb¨(1+2⊃⎕LC)↓⎕NR 2⊃⎕XSI
     delegate←(⎕IO⊃⎕RSI).⎕OR(⎕IO⊃⎕RSI).⎕FX(⊂'⍙Delegate'),actions,(⊂'⎕EX ''⍙Delegate''')

    ⍝ Get a 'Dispatcher' from the UI object. That way we are sure to have the right UI thread.
    ⍝ Use the method '.Invoke' instead of '.BeginInvoke' for synchronous call.
     {}obj.Dispatcher.BeginInvoke(DispatcherPriority.Normal(⎕NEW Action delegate))

 :Else
     ⎕←'Error ScriptFollowsDispatchDelegate: ',actions

    ⍝ Returns the Last Error
     :If 90=⎕EN
         ⎕←'EXCEPTION: ',⎕EXCEPTION.Message
     :Else
         ⎕←(1⊃⎕DM),': ',(2⊃⎕DM)
     :EndIf
 :End
[3]
[4]  ⍝ ... long running process on another thread ...
[5]
[6]    ScriptFollowsDispatchDelegate myObj
[7]  ⍝∇ line 1 to by executed in the UI thread
[8]  ⍝∇ line 2 to by executed in the UI thread
[9]  ⍝∇ line 3 to by executed in the UI thread
[10] ⍝∇ line 4 to by executed in the UI thread

The author has taken the prefix '⍝∇' instead of '⍝' because in production code you will probably want to erase all the comments in your runtime WS because they are useless and it is helping to obfuscate the code while taking less space.

With a DispatcherTimer

If you have a repetitive task to be executed on the UI thread then you can use a DispatcherTimer like this:

     ⎕USING∪←'System.Windows,WPF/WindowsBase.dll' 'System,mscorlib.dll'
     tm1_obj ← ⎕NEW Threading.DispatcherTimer(Threading.DispatcherPriority.Normal myObj.Dispatcher)
     tm1_obj.Interval ← TimeSpan.FromSeconds 1
     tm1_obj.onTick   ← '#.MyFunction'       ⍝ Function to be executed every 1 second on the Dispatcher of the object 'myObj'
     tm1_obj.Start

Note that since the DispatcherTimer is executed on the UI thread you cannot have a long callback because it will freeze the UI during its execution.

With a BackgroundWorker

The .NET framework provides a simple way to get started in threading with the BackgroundWorker component. This wraps much of the complexity and makes spawning a background thread relatively safe. It offers several features which include spawning a background thread, the ability to cancel the background process before it has completed, and the chance to report the progress back to your UI. The BackgroundWorker is an excellent tool when you want multithreading in your application with access to the UI thread, mainly because it's so easy to use.

An example is included in this namespace. It is inspired by this article. You start the example by doing:

      BackgroundWorkerSample& 1000

A window will appear and if you click on the 'Start' button, the UI will be refreshed with the multiple of 42 between 1 and 1000 while simulating a long calculation. Check the comments of this function with the links for more details.

How to install wpfXamlDemo in your workspace

  1. Download wpfXamlDemo.v1.6.txt
  2. Do a Select all (Ctrl+A) and a copy (Ctrl+C).
  3. In your workspace execute )ed ⍟ wpfXamlDemo
  4. Paste (Ctrl+V) the text into the Dyalog editor
  5. Press Escape and ')save' your workspace

Optionally to de-script the namespace you can do:

    #.wpfXamlDemo←{('n' ⎕NS ⍵)⊢n←⎕NS ''}#.wpfXamlDemo