XML DataBinding Example

Using (or providing) Microsoft.NET Classes

XML DataBinding Example

Postby PGilbert on Fri Jul 29, 2011 6:04 pm

To answer my question from the last post here is a way to 'Bind' APL data to WPF TextBoxes:

Copy this function to a WS:

Code: Select all
DemoBinding;⎕USING
⍝ XML Binding Example

⍝ Get the Path of the targeted .Net Framework
 ⎕USING←'System,mscorlib.dll'
 progFiles←Environment.GetFolderPath Environment.SpecialFolder.ProgramFiles
⍝ _NetPath←progFiles,'\Reference Assemblies\Microsoft\Framework\v3.0\'
⍝ _NetPath←progFiles,'\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\'
 _NetPath←progFiles,'\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\'

⍝ Build some XML Data
 MyAplData←1 2⍴'First_Name' 'Pierre'
 MyAplData⍪←'Last_Name' 'Gilbert'
 MyAplData⍪←'Location' 'Montreal'
 MyAplData⍪←'Interest' 'APL'
 MyAplData←⎕XML(0 'MyData' '')⍪1,MyAplData

 (⎕UCS 13),'Here is the original XML Data'
 'Modify the Edit Box and Close the Window'
 MyAplData

⍝ Get a .Net XML Document Object
 ⎕USING←'System.Xml,System.Xml.dll'
 MyAplDataXml←⎕NEW XmlDocument
 MyAplDataXml.LoadXml(⊂MyAplData)

⍝ Build the Main Window from XAML
 ⎕USING←'System.IO,mscorlib.dll' 'System.Xml,System.Xml.dll'
 ⎕USING,←⊂('System.Windows.Markup,',_NetPath,'PresentationFramework.dll')
 RootObj←XamlReader.Load XmlReader.Create(⎕NEW StringReader(⊂WinXaml))

 ⍝ Add the XML Document Object to the DataContext Property of the Main Window
 RootObj.DataContext←MyAplDataXml

 ⍝ Show the Window and Let the User Modify the Content
 {}RootObj.ShowDialog

 ⍝ Get Back the Modified DataContext of the Main Window
 MyAplDataXml←RootObj.DataContext

 ⍝ Show the XML file that was modified via the DataBinding
 (⎕UCS 13),'Here is the Modified XML Data'
 ⎕XML ⎕XML MyAplDataXml.InnerXml


add this variable that you need to call 'WinXaml' that contains the XAML of the Main Window:

Code: Select all
<Window
  x:Name="Window"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="300"
  Width="400"
  Title="MainWindow"
  mc:Ignorable="d">
  <Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="0.35*"/>
      <ColumnDefinition Width="0.65*"/>
    </Grid.ColumnDefinitions>
    <Label
      Grid.ColumnSpan="2"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      Content="Please Fill the Form:"
      FontSize="18.667"
      FontWeight="Bold"/>
    <Label
      Grid.Row="1"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="First Name:"/>
    <Label
      Grid.Row="2"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Last Name:"/>
    <Label
      Grid.Row="3"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Location:"/>
    <Label
      Grid.Row="4"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Interest:"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="1"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/MyData/First_Name, Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="2"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/MyData/Last_Name, Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="3"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/MyData/Location, Mode=TwoWay}"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="4"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/MyData/Interest, Mode=TwoWay}"/>
  </Grid>
</Window>


Now if you execute the function 'DemoBinding' you will see a form that was filled by DataBinding from an APL XML variable, if you change the content of the TextBoxes you will see that the XML returned has changed and contains the value of the TextBoxes. This is just a simple example to show the feasability in APL but there is much more to say on the subject.

This is opening the door to the separation of the GUI layer from the Business layer. In other words, the GUI designer could develop the user interface and test it with an XML file saved on disk (databinding to a XML file on disk is possible also) while the APL developper do its development and write the result to an XML file on disk also. When both are finished, than the APL developper use an XML variable to 'pass' and 'receive' the information from the GUI. Naturally both persons will have to sit down and agree on what each one need in the XML file.

Pierre Gilbert
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: XML DataBinding Example

Postby Dick Bowman on Wed Aug 10, 2011 3:11 pm

Thank you for that - it's the sort of little example I like to see.

I had to tweak the ⎕USING path - I suspect that I'm using "something that works" there rather than understanding what I'm doing.

I shall ponder this, and see how I might merge it into a little application.
Visit http://apl.dickbowman.com to read more from Dick Bowman
User avatar
Dick Bowman
 
Posts: 235
Joined: Thu Jun 18, 2009 4:55 pm

Re: XML DataBinding Example

Postby PGilbert on Wed Aug 10, 2011 4:59 pm

Hello Dick, for the benefits of everybody could you show us what you needed to do to tweak the ⎕USING path. I was pretty sure that the way it was done was correct to reach the .Net 4 path of anyone.
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: XML DataBinding Example

Postby Dick Bowman on Thu Aug 11, 2011 3:03 pm

This is what I used (it's a variant of a variant of Jason's Dyalog2008 example).

Code: Select all
 use←Using opt;path;progFiles;⎕USING
 ⎕USING←,⊂'System'
 progFiles←Environment.GetFolderPath Environment.SpecialFolder.ProgramFiles
 path←progFiles,'\Reference Assemblies\Microsoft\Framework\v3.0\'
 use←'System,System.dll' 'System.IO,mscorlib.dll' 'System.Xml,system.xml.dll'
 use,←⊂'System.Windows,',path,'PresentationFramework.dll'
 use,←⊂'System.Windows.Markup'


It's not smart and it's not clever - I'm bemused that it seems to get me to .NET 4 even though it looks like it's fetching from .NET 3.

I really ought to pay more attention, but - for the nonce - this is working for me. I think it's one of the weaknesses of our living outside of the Visual Studio world, where all this stuff seems to be taken for granted. Things like GAC go right over my head.
Visit http://apl.dickbowman.com to read more from Dick Bowman
User avatar
Dick Bowman
 
Posts: 235
Joined: Thu Jun 18, 2009 4:55 pm

Re: XML DataBinding Example

Postby PGilbert on Sat Nov 19, 2011 3:19 pm

Following some discussion with Michael Hughes and Daniel Baronet here is the same example but using a DataTable instead of Xml for the DataBinding:

Code: Select all
 DemoBinding2;⎕USING
⍝ Binding Example Using a DataTable

⍝ Create an empty DataTable
⍝ The Column Names are the 'Path' for the DataBinding
 ⎕USING←'System' 'System.Data,system.data.dll'
 dt←⎕NEW DataTable
 {}'First_Name' 'Last_Name' 'Location' 'Interest'{dt.Columns.Add ⍺ ⍵}¨4⍴String

 ⍝ Fill the First Row of the DataTable
 2010⌶dt(1 4⍴'Pierre' 'Gilbert' 'Montreal' 'APL')

⍝ Build the Main Window from XAML
 ⎕USING←'System.Windows.Markup,WPF/PresentationFramework.dll'
 RootObj←XamlReader.Parse(⊂WinXaml2)

 ⍝ Add the DataTable Object to the DataContext Property of the Main Window
 RootObj.DataContext←dt

 ⍝ Show the Window and Let the User Modify the Content
 {}RootObj.ShowDialog

 ⍝ Get Back the Modified DataContext of the Main Window
 dt←RootObj.DataContext

 ⍝ Show the DataTable that was modified via the DataBinding
 (⎕UCS 13),'Here is the Modified Data'
 2011⌶dt


the WinXaml2 variable is now the following (using Path instead of XPath):

Code: Select all
<Window
  x:Name="Window"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="300"
  Width="400"
  Title="MainWindow"
  mc:Ignorable="d">
  <Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="0.35*"/>
      <ColumnDefinition Width="0.65*"/>
    </Grid.ColumnDefinitions>
    <Label
      Grid.ColumnSpan="2"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      Content="Please Fill the Form:"
      FontSize="18.667"
      FontWeight="Bold"/>
    <Label
      Grid.Row="1"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="First Name:"/>
    <Label
      Grid.Row="2"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Last Name:"/>
    <Label
      Grid.Row="3"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Location:"/>
    <Label
      Grid.Row="4"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Interest:"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="1"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding Path=First_Name, Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="2"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding Path=Last_Name, Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="3"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding Path=Location, Mode=TwoWay}"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="4"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding Path=Interest, Mode=TwoWay}"/>
  </Grid>
</Window>
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: XML DataBinding Example

Postby Dick Bowman on Fri Dec 16, 2011 2:51 pm

Thanks for that, belatedly got around to checking out and using it.

I agree, using a DataTable seems preferable to XML.

Interested to see your use of <XamlReader.Parse> while I stay with the evergreen <LoadXAML> from Jason's 2008 presentation; an example of the sort of thing we need to write brief "how-to"s and "why-to"s about. At a guess my ancient blunderbuss is doing more work than needs to be done.

Not sure at the moment how great the advantage of using Data Binding is - I'll guess that this will become clearer with experience.

Background on why I explored this - wanted to add a bit of functionality to an old application and felt that in the same way as I'd consigned ⎕WC to my dustbin-of-history it was now time to apply the same fate to APL/W Forms and graft some WPF windows into the ancient code. Worried that there might be some sort of interference, but it seems to have worked out.
Visit http://apl.dickbowman.com to read more from Dick Bowman
User avatar
Dick Bowman
 
Posts: 235
Joined: Thu Jun 18, 2009 4:55 pm

Re: XML DataBinding Example

Postby PGilbert on Sat Dec 17, 2011 11:29 pm

Dick Bowman wrote:Not sure at the moment how great the advantage of using Data Binding is - I'll guess that this will become clearer with experience.

Hello Dick, here is my goal with this DataBinding business:
In my current APL application that I am converting from []W to WPF I have a 'Make' and a 'Refresh' function for each UI page. The 'Make' functions are using []W that I will convert to WPF with XAML or code (using XAML everything is replace with one line of code - neat !). Once the page is created, its the 'Refresh' functions that are called. Currently I am gathering from different variables in the WS what is needed to refresh the UI page and make a single vector that I pass as an argument to the 'Refresh' function. Than inside the 'Refresh' function the []W will alter each control one by one, with WPF and DataBinding you could use the same variable of the 'Refresh' function for DataBinding. Meaning that you set that variable to a Data Context property (as a XML or Data Table) and voila all the controls are refreshed automatically when the binding is properly written. This means that the 'Refresh' function could be one line of code too !

And there is more... Michael Hughes and Dyalog have proven the feasability that you could pass an apl variable directly in the DataContext for DataBinding when doing a custom DataBinding using an APL class exported as a dll ! So if you want, there is no need to convert the apl variable to XML or DataTable, you could use it directly - pretty neat ! The binding could look like this for example: <Label Content="{apl:APLBinding XPath=2⊃V}"/> where V is the value of the DataContext.

In the case that you need to do some Data Validation (ex. keep a number between a min and max value) you need to be DataBinding currently in WPF, so this DataBinding business could be quite usefull (for refreshing a control and to validate a control).

Pierre Gilbert
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: XML DataBinding Example

Postby Dick Bowman on Thu Oct 18, 2012 2:41 pm

Belatedly following up on this I've documented an example of Binding for a DataGrid at http://www.dogon.myzen.co.uk/APL/Tutorials/WPF/Binding.html

Also on this page I've reduced Pierre's example above to something more bare-bones.

At some point in the future I want to pin down Binding for ComboBox and DataGridComboBoxColumn - unless somebody would care to beat me to it (which will not be hard).

I begin to see some benefit to Binding when using WPF in terms of reducing the amount of APL we write to handle mundane stuff, but I'm not developing any skill-transference ability applying what I know for one control to others - maybe that will happen in time.

As always, I'm grateful to Pierre for sharing his expertese here.
Visit http://apl.dickbowman.com to read more from Dick Bowman
User avatar
Dick Bowman
 
Posts: 235
Joined: Thu Jun 18, 2009 4:55 pm

Re: XML DataBinding Example

Postby PGilbert on Fri Apr 19, 2013 11:23 am

Here is my latest attempt to simplify the Data Binding of an Apl variable by using Xml. I am not particularly crazy about Xml but it is simple, easy to understand and does the job.

Saving an Apl variable as Xml has been done before but not in a way possible to search the result with a XPath statement and consequently not usefull for the use of Data Binding.

The functions 'AplToXml' and 'XmlToApl' from the namespace 'DC' will Serialize and Deserialize an APL variable or namespace so that it can be search by a XPath statement in a Data Binding context.

Using Xml for Data Binding on small forms is fast. For large array a DataTable is more appropriate. There is many ways to represent an Apl variable as Xml for DataBinding but this one works for my needs. Empty element like ⍳0 and '' are permitted and their conversion is lossless.

See the comments of the functions for more informations.

Here is some example:

      ⍝ Build the Main Window from XAML:
⎕USING←'System.Windows.Markup,WPF/PresentationFramework.dll'
RootObj←XamlReader.Parse(⊂xml1)

⍝ Build and set the DataContext:
DC←AplToXml 'Pierre' 'Gilbert' 'Montréal' 'Apl'
RootObj.DataContext←DC

⍝ Show the form and modify the town from Montréal to Toronto:
{}RootObj.ShowDialog

⍝ Get back the DataContext as an Apl variable:
XmlToApl RootObj.DataContext
Pierre Gilbert Toronto Apl


⍝ If you need to fill a grid you need a 2 dimensional matrix:
RootObj←XamlReader.Parse(⊂xml2)
DC←AplToXml 10 2⍴⍳20
RootObj.DataContext←DC

⍝ Show the form and modify some values in the grid:
{}RootObj.ShowDialog

⍝ Get back the DataContext as an Apl variable with the modifications:
XmlToApl RootObj.DataContext


Here is the definition of xml1:
Code: Select all
<Window
  x:Name="Window"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="300"
  Width="400"
  Title="MainWindow"
  mc:Ignorable="d">
  <Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
      <RowDefinition Height="0.15*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="0.35*"/>
      <ColumnDefinition Width="0.65*"/>
    </Grid.ColumnDefinitions>
    <Label
      Grid.ColumnSpan="2"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      Content="Please Fill the Form:"
      FontSize="18.667"
      FontWeight="Bold"/>
    <Label
      Grid.Row="1"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="First Name:"/>
    <Label
      Grid.Row="2"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Last Name:"/>
    <Label
      Grid.Row="3"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Location:"/>
    <Label
      Grid.Row="4"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
      Content="Interest:"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="1"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/Data/*[1], Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="2"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/Data/*[2], Mode=TwoWay}"/>   
    <TextBox
      Grid.Column="1"
      Grid.Row="3"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/Data/*[3], Mode=TwoWay}"/>
    <TextBox
      Grid.Column="1"
      Grid.Row="4"
      Margin="10,0,100,0"
      VerticalAlignment="Center"
      Text="{Binding XPath=/Data/*[4], Mode=TwoWay}"/>
  </Grid>
</Window>


Here is the definition of xml2:
Code: Select all
<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="300"
  Width="300"
  Title="MainWindow">
  <Grid>
    <DataGrid
      AutoGenerateColumns="False"
      ItemsSource="{Binding XPath=/Data/*}">
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding XPath=@C1}" Header="C1"/>
        <DataGridTextColumn Binding="{Binding XPath=@C2}" Header="C2"/>
      </DataGrid.Columns>
    </DataGrid>
  </Grid>
</Window>


Here is the definition of the scripted namespace DC:
Code: Select all
:Namespace DC

    ⎕ML←3 ⋄ ⎕IO←1 ⋄ ⎕PP←10
   
    ⍝ Serialize and Deserialize an Array or NameSpace of Arrays to XML in order to use it as DataContext.
    ⍝ An Array could be a Scalar, Vector or a 2 dimensional non-nested array of depth 2 or less.
    ⍝ Empty values ⍳0 and '' are permitted and their conversion is lossless.
    ⍝
    ⍝ The functions are not intended to provide a Xml representation of any Apl array or namespace
    ⍝ but rather a concise Xml representation of simple Apl arrays that can be used for DataBinding.
    ⍝
    ⍝ The main functions are 'AplToXml' and 'XmltoApl', see their comments for more informations.
    ⍝ 'AplToXml' returns a .Net XmlDocument Object. Adjust ⎕PP to the required precision.
    ⍝ To get the content of a .net XmlDocument object use the OuterXml property: xmlDoc.OuterXml   
    ⍝
    ⍝ For a scalar or vector, one xml element is created for each Apl number or character element:
    ⍝ For numbers the element name is always 'n':    <n></n>
    ⍝ For characters the element name is always 's': <s></s>
    ⍝ The rank(⍴⍴) and the shape(⍴) are encoded as attributes in the root element.
    ⍝
    ⍝ For a vector the xml representation is:
    ⍝   ⎕XML ⎕XML ( AplToXml 1 2 3 'Vive Apl' '' (⍳0) ).OuterXml
    ⍝
    ⍝ <Data Rank="1" Shape="6" Type="array" xmlns="">
    ⍝   <n>1</n>
    ⍝   <n>2</n>
    ⍝   <n>3</n>
    ⍝   <s>Vive Apl</s>
    ⍝   <s></s>
    ⍝   <n></n>
    ⍝ </Data>
    ⍝
    ⍝ For a namespace of vectors the xml representation is:
    ⍝   ns ← ⎕NS ''
    ⍝   ns.Array1 ← 1 2 3
    ⍝   ns.Array2 ← 'text' 45.67 ''
    ⍝
    ⍝   ⎕XML ⎕XML ( AplToXml ns ).OuterXml
    ⍝
    ⍝ <Data Type="ns" xmlns="">
    ⍝   <Array1 Rank="1" Shape="3" Type="array" xmlns="">
    ⍝     <n>1</n>
    ⍝     <n>2</n>
    ⍝     <n>3</n>
    ⍝   </Array1>
    ⍝   <Array2 Rank="1" Shape="3" Type="array" xmlns="">
    ⍝     <s>text</s>
    ⍝     <n>45.67</n>
    ⍝     <s></s>
    ⍝   </Array2>
    ⍝</Data>
    ⍝
    ⍝ For 2 dimentional matrix each xml element represent a row of the matrix.
    ⍝ The values are encoded as attributes of each xml element.
    ⍝ The xml element name represent the type of data of each columns.
    ⍝ Within a column, the type of data does not need to be homogenous.   
    ⍝
    ⍝ For a matrix the xml representation is:
    ⍝   ⎕XML ⎕XML ( AplToXml 2 3⍴ 1 2 '' 'Vive' 'Apl' (⍳0) ).OuterXml
    ⍝
    ⍝ <Data Rank="2" Shape="2 3" Type="array" xmlns="">                                                                                 
    ⍝   <nns C1="1"    C2="2"   C3=""></nns>                                                                                                 
    ⍝   <ssn C1="Vive" C2="Apl" C3=""></ssn>                                                                                           
    ⍝ </Data>
    ⍝
    ⍝ Note: The column names are automatically set to: 'C1', 'C2', 'C3', etc.
    ⍝       For a Date there is no conversion possible in Xaml, the date has to be a character vector.

    ∇ xmlDoc←{rootName}AplToXml apl;array;arrayName;attribute;ElementName;i;ns;row;xml;xmlArray;⎕USING
    ⍝ Serialize an Array or NameSpace of Arrays to XML in order to use it as DataContext.
    ⍝ An Array could be a Scalar, Vector or a 2 dimensional non-nested array of depth 2 or less.
    ⍝ XmlToApl is used to Deserialize the XmlDocument after use as DataContext.
    ⍝
    ⍝ apl      = Array or NameSpace of Arrays.
    ⍝ xmlDoc   = .Net XmlDocument Object representing the value of apl.
    ⍝ rootName = Optional: The Name of the Xml Root Element (Default = 'Data').
    ⍝
    ⍝ * * * * Typical use of DataBinding for Arrays * * * * *
    ⍝
    ⍝ xmlDoc ← AplToXml 1 'text' 3
    ⍝
    ⍝ XPath='/Data/*'    -> All elements
    ⍝ XPath='/Data/*[2]' -> Second element
    ⍝
    ⍝  xmlDoc ← AplToXml 10 3 ⍴ ⍳30
    ⍝
    ⍝ XPath='/Data/*/@C1'    -> All elements of first column
    ⍝ XPath='/Data/*/@C2'    -> All elements of second column
    ⍝ XPath='/Data/*[2]/@C2' -> Element of second column in second row
    ⍝
    ⍝ * * * * Typical use of DataBinding for NameSpace * * * *
    ⍝
    ⍝ ns ← ⎕NS ''
    ⍝ ns.array1 ← 1 2 3
    ⍝ ns.array2 ← 'text' 45.67 ''
    ⍝
    ⍝ xmlDoc ← AplToXml ns
    ⍝
    ⍝ XPath='/Data/array1/*'    -> All the elements of array1
    ⍝ XPath='/Data/array2/*[2]' -> Second element of array2
    ⍝
    ⍝ * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    ⍝
    ⍝ You need to specify Mode=TwoWay if you want the input result of a TextBox
    ⍝ to modify the DataContext, the default value is OneWay:
    ⍝    <TextBox Text="{Binding XPath=/Data/array2/*[2], Mode=TwoWay}"/>
    ⍝
    ⍝ To fill a ComboBox with all the elements of array1:
    ⍝    <ComboBox ItemsSource="{Binding XPath=/Data/array1/*}, SelectedIndex="-1"/>
    ⍝
    ⍝ For RadioButton and CheckBox the IsChecked property must be bind to: 'True' and 'False' in characters.
    ⍝ The IsVisible property must be bind to: 'Visible', 'Hidden' or 'Collapsed' in characters.
    ⍝ Used the BooleanConverter and the VisibilityConverter included in this namespace for the conversion.
    ⍝
    ⍝ To Set as the DataContext:
    ⍝    MyNetObject.DataContext ← xmlDoc
    ⍝
    ⍝ After Waiting on the Window, from the modified DataContext
    ⍝ to obtain an APL Array or NameSpace from the DataContext:
    ⍝    apl ← XmlToApl MyNetObject.DataContext
    ⍝
    ⍝ Note: The characters: '&' '<' '>' are not allowed in Xml because they
    ⍝       interfere with the syntax. They are replaced by '&amp;' '&lt;' '&gt;'
    ⍝       by ⎕XML and revert back to '&' '<' '>' when converted back to APL characters.
    ⍝
    ⍝ Knowned limitation: extra whitespaces of character element are not kept by choice.
     
    ⍝ Default value in case of error:
      ⎕USING←'System.Xml,System.Xml.dll'
      xmlDoc←⎕NEW XmlDocument
     
      :If 0=⎕NC'rootName'   ⍝ There is no rootName
      :OrIf 0=1↑0⍴rootName  ⍝ rootName exists and it's not characters
          rootName←'Data'   ⍝ Default rootName
      :End
     
    ⍝ Make a selection based on the name classification of 'apl':
      :Select ⍬⍴⎕NC'apl'
      :Case 2 ⍝ APL Array
     
        ⍝ Verify the depth:
          :If 3≤≡array←apl
              ⎕←'AplToXml Error: The Depth of Argument Must Be Smaller than 3 !'
              →0
          :End
     
        ⍝ DFN To Select the Element Name according to it's type:
          ElementName←{'ns'[⎕IO+' '=↑1↑0⍴⍵]} ⍝ n=number, s=string
     
          :Trap 0
     
              :If 0=⍬⍴⍴array ⍝ shape 0  (scalar or empty)
     
                  xml←1 4⍴0 rootName''(4 2⍴'Rank'(⍬⍴⍴⍴array)'Shape' 0 'Type' 'array' 'xmlns' '')  ⍝ Root Element
                  xml⍪←1(ElementName array)(⍕array)''                                             ⍝ Xml Element APL Style
     
              :ElseIf 1=⍬⍴⍴⍴array ⍝ rank 1 (vector)
     
                  xml←1 4⍴0 rootName''(4 2⍴'Rank' 1 'Shape'(⍬⍴⍴array)'Type' 'array' 'xmlns' '')   ⍝ Root Element
                  xml⍪←1,((ElementName¨array),[1.5](⍕¨array)),⊂''                                 ⍝ Xml Element APL Style
     
              :ElseIf 2=⍬⍴⍴⍴array ⍝ rank 2 (2 dimensional matrix)
     
                  xml←1 4⍴0 rootName''(4 2⍴'Rank' 2 'Shape'(⍕⍴array)'Type' 'array' 'xmlns' '')    ⍝ Root Element
                  attribute←('C',¨⍕¨⍳1↓⍴array)                                                    ⍝ Column's Names
     
                  :For i :In ⍳1↑⍴array                                                            ⍝ Iterate for each row
                      xml⍪←1(,ElementName¨row)''(attribute,⍉⍕¨row←array[,i;])                     ⍝ Xml Element Apl Style
                  :EndFor
     
              :Else
     
                  ⎕←'AplToXml Error: The Rank of Argument Must Be Smaller Than 3 !'
     
              :EndIf
     
            ⍝ Get a .Net XmlDocument object from the xml:
              xml←⎕XML xml             ⍝ Convert to Characters
              ((xml='¯')/xml)←'-'      ⍝ Change the negative sign
              xmlDoc.LoadXml(⊂xml)     ⍝ Write the Characters to the XmlDocument
     
          :EndTrap
     
      :Case 9 ⍝ NameSpace
     
        ⍝ apl is a NameSpace
          ns←apl
     
          :Trap 0
            ⍝ Add the Root Node:
              xmlDoc.LoadXml(⊂'<',rootName,' Type="ns" xmlns=""/>') ⍝ Type is NameSpace
     
            ⍝ Convert each simple array of the NameSpace into Xml and Append it to XmlDoc:
              :For arrayName :In ns.⎕NL-2
                ⍝ Convert each array of the NS into a XmlDocument:
                  xmlArray←arrayName AplToXml(ns.⍎arrayName) ⍝ xmlArray is a XmlDocument
     
                ⍝ Append xmlArray to the XmlDocument:
                  {}xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlArray.DocumentElement)1)
     
              :EndFor
          :EndTrap
      :EndSelect
    ∇
   
    ∇ apl←XmlToApl xmlDoc;array;i;rank;shape;TypeConvert;xml;xmlElement;xmlElements
    ⍝ Deserialize a .net XmlDocument object created by AplToXml back to APL.
    ⍝ See the comments of function AplToXml for more information.
    ⍝
    ⍝ Empty Xml elements are converted according to the following:
    ⍝   <n></n> converted to ⍳0
    ⍝   <s></s> converted to ''
    ⍝
    ⍝ Numeric element with error are converted to 0:
    ⍝   <n>2..34</n> converted to 0
     
      :Trap 0
          :Select xmlDoc.GetType.ToString
          :Case 'System.Xml.XmlElement'
            ⍝ OK. Used when called recursively.
              xmlElement←xmlDoc
     
          :Case 'System.Xml.XmlDocument'
            ⍝ Get the root element of the XmlDocument.
              xmlElement←xmlDoc.DocumentElement
     
          :Else
              ⎕←'XmltoApl Error: Argument must be a .Net XmlDocument !'
              →0
     
          :EndSelect
     
        ⍝ Make a selection based on the value of attribute 'Type':
          :Select xmlElement.Attributes[⊂'Type'].Value
          :Case 'array'
            ⍝ xmlDoc is a simple array.
     
            ⍝ Default value in case of error:
              apl←⍳0
     
            ⍝ Obtain the rank and shape of original Apl array from attributes:
              (rank shape)←⍎¨(xmlElement.Attributes['Rank' 'Shape']).Value
     
            ⍝ Obtain the XmlElement as APL characters:
              xml←xmlElement.OuterXml
              ((xml='-')/xml)←'¯'       ⍝ Change the negative sign
     
            ⍝ Convert the character's xml to a numeric matrix with ⎕XML:
              xml←1↓[1]⎕XML xml         ⍝ Remove the root element
     
            ⍝ DFN function to convert each element back to APL:
              TypeConvert←{
                  (⍺='n')∧(⍵≡''):⍳0     ⍝ <n></n>
                  (⍺='s')∧(⍵≡''):''     ⍝ <s></s>
                  (⍺='n'):⍬⍴⊃(//⎕VFI ⍵) ⍝ <n>3.45</n>
                  (⍺='s'):⍵}            ⍝ <s>text</s>
     
            ⍝ Apply the shape and rank to the conversion:
              :If 0≡⍬⍴shape
              :AndIf 0≡⍬⍴rank
     
                ⍝ Scalar
                  apl←(⊃xml[1;2])TypeConvert(⊃xml[1;3])
     
              :ElseIf 0≡⍬⍴shape
              :AndIf 1≡⍬⍴rank
     
                ⍝ Originally Empty with either ⍳0 or ''
                ⍝ Could have changed during the DataBinding
                ⍝ Do nothing.
                  apl←(⊃xml[1;2])TypeConvert(⊃xml[1;3])
     
              :ElseIf 1≡⍬⍴rank
     
                ⍝ Vector
                  apl←shape⍴xml[;2]TypeConvert¨xml[;3]
     
              :ElseIf 2≡⍬⍴rank
     
                 ⍝ Matrix
                  :For i :In ⍳1↑⍴xml
                      apl,←(⊃xml[i;2])TypeConvert¨(2⌷[2]⊃xml[i;4])
                  :EndFor
     
                  apl←shape⍴apl
     
              :EndIf
     
     
          :Case 'ns'
            ⍝ xmlDoc is a namespace of arrays.
     
            ⍝ Default value in case of error:
              apl←⎕NS''
     
            ⍝ Obtain all the child nodes of the XmlElement:
              xmlElements←⌷xmlElement.ChildNodes
     
            ⍝ Iterate to fill the NameSpace.
              :For xmlElement :In xmlElements
                ⍝ Otain an Array from each XmlElement:
                  array←XmlToApl xmlElement
     
                ⍝ Fix the name with it's value in the NameSpace:
                  ⍎'apl.',(xmlElement.Name),'←array'
     
              :EndFor
          :EndSelect
      :Else
     
          ⎕←'XmltoApl Error: Argument must be a .Net XmlDocument !'
     
      :EndTrap
    ∇
           
    ∇ r←file_name AplToFile apl;xml;xmlDoc;⎕USING
    ⍝ Writes to a file the Xml representation of an Apl array obtained by Xml.fromApl
    ⍝ apl       = Apl array or namespace of arrays.
    ⍝ file_name = fully qualify file name with file extension (.xml)
    ⍝ r         = 1 for success, 0 for failure
     
    ⍝ Get a .Net XmlDocument Object from the 'apl':
      xmlDoc←AplToXml apl
     
    ⍝ Get the character representation:
      xml←xmlDoc.OuterXml
     
    ⍝ Write to file:
      :Trap 0
        ⍝ UTF-8 Encoding
          ⎕USING←',mscorlib.dll'
          System.IO.File.WriteAllText((⊂,file_name),(⊂,xml),(System.Text.Encoding.UTF8))
          r←1
      :Else
        ⍝ Failure: Unexpected Error While Writing the File.
          r←0
      :EndTrap
    ∇
     
    ⍝ Boolean Converter
      BoolCvt←{
          {
              ⍵≡1:'True'    ⍝ 1 = 'True'
              ⍵≡0:'False'   ⍝ 0 = 'False'
              ⍵≡'True':1    ⍝ 'True' = 1
              ⍵≡'False':0   ⍝ 'False' = 0
              ' '=↑1↑0⍴⍵:0  ⍝ Any character = 0
              'False'}¨⍵    ⍝ Any number = 'False'
      }
     
    ⍝ Visibility Converter
      VisiCvt←{
          {
              ⍵≡1:'Visible'    ⍝ 1  = 'Visible'
              ⍵≡0:'Hidden'     ⍝ 0  = 'Hidden'
              ⍵≡¯1:'Collapsed' ⍝ ¯1 = 'Collapsed'
              ⍵≡'Visible':1    ⍝ 'Visible' = 1
              ⍵≡'Hidden':0     ⍝ 'Hidden' = 0
              ⍵≡'Collapsed':¯1 ⍝ 'Collapsed' = ¯1
              ' '=↑1↑0⍴⍵:1     ⍝ Any character = 1
              'Visible'}¨⍵     ⍝ Any number = 'Visible'
      }
       
:EndNamespace
Last edited by PGilbert on Mon Apr 22, 2013 2:15 pm, edited 4 times in total.
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Re: XML DataBinding Example

Postby PGilbert on Sat Apr 20, 2013 10:36 pm

Here is another example taken here using a DataTemplate with the functions of the previous post:

      ⍝ Build the Main Window from XAML:
⎕USING←'System.Windows.Markup,WPF/PresentationFramework.dll'
RootObj←XamlReader.Parse(⊂xml3)

⍝ Build and set the DataContext:
mat←1 2⍴'Google' 'http://www.google.com'
mat⍪←'Amazon' 'http://www.amazon.com'
mat⍪←'Slashdot' 'http://slashdot.com'
mat⍪←'Ars Technica' 'http://arstechnica.com'
mat⍪←'New Egg' 'http://www.newegg.com'
mat⍪←'Dyalog' 'http://www.dyalog.com'

DC←#.DC.AplToXml mat

⎕xml ⎕xml DC.OuterXml
<Data Rank="2" Shape="6 2" Type="array" xmlns="">
<ss C1="Google" C2="http://www.google.com"></ss>
<ss C1="Amazon" C2="http://www.amazon.com"></ss>
<ss C1="Slashdot" C2="http://slashdot.com"></ss>
<ss C1="Ars Technica" C2="http://arstechnica.com"></ss>
<ss C1="New Egg" C2="http://www.newegg.com"></ss>
<ss C1="Dyalog" C2="http://www.dyalog.com"></ss>
</Data>

RootObj.DataContext←DC

⍝ Show the form
{}RootObj.ShowDialog


Here is the definition of xml3:
Code: Select all
<Window
  Name="Form1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="400"
  Width="550"
  Title="WindowsApplication1">
  <Window.Resources>
    <!-- create a data template to display the desired XML node values -->
    <DataTemplate x:Key="BookmarkDataTemplate">
      <StackPanel Margin="5">
        <TextBlock
          FontSize="12"
          FontWeight="Bold"
          Foreground="White">
          <TextBlock.Text>
            <Binding XPath="@C1"/>
          </TextBlock.Text>
        </TextBlock>
        <TextBlock FontSize="10" Foreground="LightGray">
          <TextBlock.Text>
            <Binding XPath="@C2"/>
          </TextBlock.Text>
        </TextBlock>
      </StackPanel>
    </DataTemplate>
  </Window.Resources>
  <!-- write the data to the screen by binding the data template to a list box -->
  <StackPanel>
    <TextBlock
      Margin="10"
      FontSize="14"
      FontWeight="Bold">My Favorites
    </TextBlock>
    <ListBox
      Margin="10"
      Background="#999"
      BorderBrush="White"
      BorderThickness="2"
      ItemTemplate="{StaticResource BookmarkDataTemplate}"
      ItemsSource="{Binding XPath=Data/*}"/>
  </StackPanel>
</Window>
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Next

Return to Microsoft.NET

Who is online

Users browsing this forum: No registered users and 1 guest