Author Archives: Peter Grew

ClickOnce Uninstall Script

There’s a previous post Sound of a Silent Install how to perform a silent install in virtual desktop environments using the Windows installer (MSI) version.

The conventional way to install Infor Smart Office on client machines is to use Microsoft ClickOnce network deployment to get an isolated, secure, self-updating application that can be installed and run with minimal user interaction.

To Uninstall a ClickOnce application require the launch of the uninstall dialog and some user interaction. To perform uninstall on many client machines can become tedious so here is a Powershell script example that performs the task.

The script use the application display name from Windows Start menu to find the ClickOnce uninstall string in the Windows Registry. It launches the ClickOnce uninstall dialog, selects the “Remove the application from this computer” option and press the OK button. This example has been tested on Windows v10.0.17763 and there’s no guarantee it will work on every Windows version.

<# Set $ISODisplayName to the visible name of the application in the Windows Start menu #>
$ISODisplayName = "Infor Smart Office - DEV"

<# Get uninstallable ClickOnce applications #>
$InstalledApplicationNotMSI = Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall | foreach-object {Get-ItemProperty $_.PsPath}

<# Find uninstall string for named ClickOnce application #>
<# The name is the display name visible in the Windows Start Menu #>
$UninstallString = $InstalledApplicationNotMSI | ? { $_.displayname -match $ISODisplayName } | select UninstallString 
$selectedUninstallString = $UninstallString.UninstallString

<# Launch ClickOnce uninstall command #>
$wshell.run("cmd /c $selectedUninstallString")

<# Wait 7 seconds for uninstall dialog to appear #>
Start-Sleep 7

<# Move focus and Select option "Remove the Application from this computer" #>
<# Key specifications: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.sendkeys?view=netframework-4.7.2 #>
$wshell.sendkeys("{TAB}")
$wshell.sendkeys("{DOWN}")

<# Press the OK button #>
$wshell.sendkeys("`"OK`"~")

 

 

 

 

Microsoft Edge Chromium and ClickOnce

The new Microsoft Edge Chromium browser doesn’t provide native support for ClickOnce as of version 81.0.416.6.

It will by default NOT work to launch a Smart Office ClickOnce install point link in Edge Chromium.

To enable ClickOnce support in Edge Chromium:

  1. Enter edge://flags link in Edge Chromium browser.
  2. Scroll down to ClickOnce Support setting and select ‘Enable’ from the dropdown list.
  3. Restart the browser.
Note: This setting will be overridden if your organization configures the 
"Allow users to open files using the ClickOnce protocol" policy. – Windows

The Edge Chromium will always prompt the user before launch of a 
ClickOnce link because Chromium doesn't rely on the Windows Security Zones.

ScriptUtil.LoadAssemblyFromUrl() is Obsolete

The ScriptUtil.LoadAssemblyFromUrl method was marked obsolete in v10.2.1.0 HF32. A workaround for M3 scripts using the method is to replace with the following code:

var assembly = Assembly.Load(WebReader.GetRequestBinary(url));

In between version 10.2.1.0.333 (HF32) and 10.2.1.0.385 (HF42) the obsolete method will
fail in silence and just return null.

This has been changed in 10.2.1.0.389 (HF43) and an InvalidOperationException will be thrown when calling the method.

JSON support in REST data service – Part III

Create, Update or Delete a User

In the third post we continue to use the fake online REST service to add operations to create, update and delete a user.

Mashup_REST_JSON_DELPATCHPUT

We add three DataOperation declarations to create, update and delete a user and set the OutputType to be JSON to easily show the request response in a textbox as a text string. The online service will return a correct response but no changes will actually be done in the service database, this is just for testing purposes.

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>
<mashup:DataOperation Name="UpdateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{id}" />    
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="PUT" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>
<mashup:DataOperation Name="DeleteUser">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/{id}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="DELETE" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>

We also add a button for each operation with a trigger event. All the property values for the current item the DataPanel will be added in the request body, so just declare the Parameter bindings for values that may change.

<Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Update User">
   <Button.CommandParameter>
      <mashup:Events>
         <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="UpdateUserPost">
         <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
         <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
         <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
         </mashup:Event>
      </mashup:Events>
   </Button.CommandParameter>
</Button>

 

Here is the Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
   <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="1*" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" />
      <ColumnDefinition Width="10" />
      <ColumnDefinition Width="1*" />
   </Grid.ColumnDefinitions>
   <Grid.Resources>
      <x:String x:Key="PostTemplate"><![CDATA[      
               { 
                 "name": "{name}",
                 "username": "{username}",
                 "email": "{email}",
                 "address": {
                    "street": "{street}",
                    "suite": "{suite}",
                    "city": "{city}",
                    "zipcode": "{zipcode}",
                    "geo": {
                       "lat": "0.0",
                       "lng": "0.0"
                    }
                 },
                 "phone": "{phone}",
                 "website": "{website}",
                 "company": {
                    "name": "{companyName}",
                    "catchPhrase": "{companyCatchPhrase}",
                    "bs": "{companyBs}"
                 }                 
               }
            ]]></x:String>
   </Grid.Resources>
   <mashup:DataListPanel DockPanel.Dock="Top" Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
      <mashup:DataListPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataListPanel.DataService>
      <mashup:DataListPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
            <mashup:Event SourceName="UsersList" TargetName="UserDetails" SourceEventName="CurrentItemChanged" TargetEventName="Get">
               <mashup:Parameter SourceKey="id" TargetKey="userId" Type="Numeric" />
            </mashup:Event>
         </mashup:Events>
      </mashup:DataListPanel.Events>
      <DockPanel LastChildFill="False">
         <ListView DockPanel.Dock="Top" Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
            <ListView.View>
               <GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
                  <GridView.Columns>
                     <GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
                     <GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
                     <GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
                     <GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                  </GridView.Columns>
               </GridView>
            </ListView.View>
         </ListView>
      </DockPanel>
   </mashup:DataListPanel>
   <mashup:DataPanel Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" Name="UserDetails">
      <mashup:DataPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="Get">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{userId}" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="CreateUserPost">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users" />
                  <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
                  <mashup:DataParameter Key="REST.InputType" Value="json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="UpdateUserPost">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/{id}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="PUT" />
                  <mashup:DataParameter Key="REST.InputType" Value="json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
               <mashup:DataOperation Name="DeleteUser">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/{id}" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.HttpMethod" Value="DELETE" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataPanel.DataService>
      <mashup:DataPanel.Events>
         <mashup:Events></mashup:Events>
      </mashup:DataPanel.Events>
      <Grid Margin="10,0,10,0">
         <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="0" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="24" />
            <RowDefinition Height="Auto" />
         </Grid.RowDefinitions>
         <DockPanel Grid.Row="1" LastChildFill="False">
            <TextBlock DockPanel.Dock="Left" Text="Company" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CCompany" Width="220" Margin="5" Text="{Binding [company].[name]}" />
         </DockPanel>
         <DockPanel Grid.Row="3">
            <TextBlock DockPanel.Dock="Left" Text="Name" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CName" Width="220" Margin="5" Text="{Binding [name]}" />
            <TextBlock DockPanel.Dock="Left" Text="UserName" Width="60" Margin="0,0,10,0" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CUserName" Margin="5" Text="{Binding [username]}" />
         </DockPanel>
         <DockPanel Grid.Row="5">
            <TextBlock DockPanel.Dock="Left" Text="Email" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CEmail" Width="220" Margin="5" Text="{Binding [email]}" />
            <TextBlock DockPanel.Dock="Left" Text="Website" Width="60" Margin="0,0,10,0" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CWebsite" Margin="5" Text="{Binding [website]}" />
         </DockPanel>
         <DockPanel Grid.Row="7" LastChildFill="False">
            <TextBlock DockPanel.Dock="Left" Text="Phone" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CPhone" Width="220" Margin="5" Text="{Binding [phone]}" />
         </DockPanel>
         <DockPanel Grid.Row="9">
            <TextBlock DockPanel.Dock="Left" Text="Catch" Width="55" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CCatch" Width="220" Margin="5" Text="{Binding [company].[catchPhrase]}" />
            <TextBlock DockPanel.Dock="Left" Text="BS" Width="60" Margin="5" VerticalAlignment="Center" />
            <TextBox DockPanel.Dock="Left" Name="CBS" Margin="5" Text="{Binding [company].[bs]}" />
         </DockPanel>
         <DockPanel Grid.Row="11">
            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Add User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="CreateUserPost">
                        <mashup:Parameter TargetKey="name" Value="{Binding ElementName=CName, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyName" Value="{Binding ElementName=CCompany, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="userName" Value="{Binding ElementName=CUserName, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyCatchPhrase" Value="{Binding ElementName=CCatch, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="companyBs" Value="{Bindng ElementName=CBS, Path=Text}" Type="String" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
            <Button DockPanel.Dock="Right" HorizontalAlignment="Right" Margin="5" Content="Update User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="UpdateUserPost">
                        <mashup:Parameter TargetKey="email" Value="{Binding ElementName=CEmail, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="website" Value="{Binding ElementName=CWebsite, Path=Text}" Type="String" />
                        <mashup:Parameter TargetKey="phone" Value="{Binding ElementName=CPhone, Path=Text}" Type="String" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
            <Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Delete User">
               <Button.CommandParameter>
                  <mashup:Events>
                     <mashup:Event TargetName="UserDetails" SourceEventName="Click" TargetEventName="DeleteUser">
                        <mashup:Parameter TargetKey="id" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
                     </mashup:Event>
                  </mashup:Events>
               </Button.CommandParameter>
            </Button>
         </DockPanel>
      </Grid>
   </mashup:DataPanel>
   <DockPanel Grid.Row="1" Grid.Column="2" Grid.RowSpan="2" VerticalAlignment="Stretch">
      <TextBlock DockPanel.Dock="Top" Text="Server Response:" Width="140" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Left" />
      <TextBox Name="ServerResponse" DockPanel.Dock="Top" Text="{Binding ElementName=UserDetails, Path=DataContext[json]}" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" />
   </DockPanel>
</Grid>

 

JSON support in REST data service – Part II

Post the User Post with a JSON Template

In the previous part we used the REST data service to fetch a set of users in JSON format and present them in a list view and as a JSON text string. In this example we call the same service to get related user information and send a request to add new information. The fake online service we use returns a correct JSON response but will not create anything in the database of the service. Smart Office 10.2.1.0.367 HF38 is required.

 

Mashup_REST_JSON_POST

First we define another data operation to create a user post by sending a JSON request,

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
    <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
    <mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
    <mashup:DataParameter Key="REST.InputType" Value="json" />
    <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
</mashup:DataOperation>

and we also add a Send button containing an event that collects required values and trigger the CreateUserPost operation.

<Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Send">
   <Button.CommandParameter>
            <mashup:Events>
                 <mashup:Event TargetName="PostComment" SourceEventName="Click" TargetEventName="CreateUserPost">
                 <mashup:Parameter TargetKey="title" Value="{Binding ElementName=PostTitle, Path=Text}" />
                 <mashup:Parameter TargetKey="body" Value="{Binding ElementName=PostBody, Path=Text}" />
                 <mashup:Parameter TargetKey="userId" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
             </mashup:Event>
         </mashup:Events>
    </Button.CommandParameter>
</Button>

A press of the Send button collects the parameters and creates a JSON parameter body to be part of the server request. The body will look like this.

{
   "title": "test",
   "body": "test",
   "userId": "2",
}

Notice that the userId is a string but for the service we call, assume it must be a numeric value. Imagine that we need to customize the body for the service request since the default one will not do the trick.

Let’s create a template for the body that will result in the following JSON.

{
   "title": "test",
   "body": "test",
   "userId": 2,
}

First we add the template as a resource in the Mashup code within a CDATA block.

<Grid.Resources>   
      <x:String x:Key="PostTemplate"><![CDATA[      
               { 
                 "title": "{title}",
                 "body": "{body}",
                 "userId": {userId}
               }
            ]]></x:String>
   </Grid.Resources>

Next step is to add a template reference in the data operation declaration using the InputJsonTemplate parameter .

<mashup:DataOperation Name="CreateUserPost">
    <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
    <mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />

Done! This is all that is needed to customize the server request body instead of getting the default one generated.

The full Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
	<Grid.RowDefinitions>
		<RowDefinition Height="1*" />
		<RowDefinition Height="10" />
		<RowDefinition Height="1*" />
		<RowDefinition Height="Auto" />
	</Grid.RowDefinitions>
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="1*" />
		<ColumnDefinition Width="10" />
		<ColumnDefinition Width="1*" />
	</Grid.ColumnDefinitions>
	<Grid.Resources>
		<x:String x:Key="PostTemplate"><![CDATA[		
					{ 
					  "title": "{title}",
					  "body": "{body}",
					  "userId": {userId}
					}
				]]></x:String>
	</Grid.Resources>
	<mashup:DataListPanel Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
		<mashup:DataListPanel.DataService>
			<mashup:DataService Type="REST">
				<mashup:DataService.Operations>
					<mashup:DataOperation Name="List">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
						<mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
					</mashup:DataOperation>
				</mashup:DataService.Operations>
			</mashup:DataService>
		</mashup:DataListPanel.DataService>
		<mashup:DataListPanel.Events>
			<mashup:Events>
				<mashup:Event SourceEventName="Startup" TargetEventName="List" />
			</mashup:Events>
		</mashup:DataListPanel.Events>
		<ListView Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
			<ListView.View>
				<GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
					<GridView.Columns>
						<GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
						<GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
						<GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
						<GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
						<GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
						<GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
					</GridView.Columns>
				</GridView>
			</ListView.View>
		</ListView>
	</mashup:DataListPanel>
	<mashup:DataPanel Grid.Row="0" Grid.Column="2" Grid.RowSpan="3" Name="PostComment">
		<mashup:DataPanel.DataService>
			<mashup:DataService Type="REST">
				<mashup:DataService.Operations>
					<mashup:DataOperation Name="ListUserPost">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts/?userid={userId}" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
					</mashup:DataOperation>
					<mashup:DataOperation Name="CreateUserPost">
						<mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/posts" />
						<mashup:DataParameter Key="REST.InputJsonTemplate" Value="{StaticResource PostTemplate}" />
						<mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
						<mashup:DataParameter Key="REST.HttpMethod" Value="POST" />
						<mashup:DataParameter Key="REST.InputType" Value="json" />
						<mashup:DataParameter Key="REST.OutputType" Value="application/json" />
						<!-- Change to DataItem to use return data -->
					</mashup:DataOperation>
				</mashup:DataService.Operations>
			</mashup:DataService>
		</mashup:DataPanel.DataService>
		<mashup:DataPanel.Events>
			<mashup:Events>
				<mashup:Event SourceEventName="Startup" TargetEventName="List" />				
			</mashup:Events>
		</mashup:DataPanel.Events>
		<Grid Margin="10,0,10,0">
			<Grid.RowDefinitions>
				<RowDefinition Height="Auto" />
				<RowDefinition Height="10" />
				<RowDefinition Height="Auto" />
				<RowDefinition Height="10" />
				<RowDefinition Height="Auto" />
			</Grid.RowDefinitions>
			<DockPanel Grid.Row="0">
				<TextBlock DockPanel.Dock="Left" Text="Post a comment as" Margin="0,0,5,0" />
				<TextBlock DockPanel.Dock="Left" Text="{Binding ElementName=UsersList, Path=CurrentItem[name]}" />
			</DockPanel>
			<DockPanel Grid.Row="2">
				<TextBlock DockPanel.Dock="Top" Text="Title" Margin="5" />
				<TextBox DockPanel.Dock="Top" Name="PostTitle" Margin="5" />
				<TextBlock DockPanel.Dock="Top" Text="Content" Margin="5" />
				<TextBox DockPanel.Dock="Top" Name="PostBody" Margin="5" />
				<Button DockPanel.Dock="Top" HorizontalAlignment="Right" Margin="5" Content="Send">
					<Button.CommandParameter>
						<mashup:Events>
							<mashup:Event TargetName="PostComment" SourceEventName="Click" TargetEventName="CreateUserPost">
								<mashup:Parameter TargetKey="title" Value="{Binding ElementName=PostTitle, Path=Text}" />
								<mashup:Parameter TargetKey="body" Value="{Binding ElementName=PostBody, Path=Text}" />
								<mashup:Parameter TargetKey="userId" Value="{Binding ElementName=UsersList, Path=CurrentItem[id]}" />
							</mashup:Event>
						</mashup:Events>
					</Button.CommandParameter>
				</Button>
			</DockPanel>
			<DockPanel Grid.Row="4">
				<TextBlock DockPanel.Dock="Top" Text="Server Response:" Width="140" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Left" />
				<TextBox Height="120" DockPanel.Dock="Top" Text="{Binding [json]}" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Top" VerticalContentAlignment="Top" TextWrapping="Wrap" AcceptsReturn="True" />
			</DockPanel>
		</Grid>
	</mashup:DataPanel>
</Grid>

 

JSON support in REST data service – Part I

In Smart Office 10.2.1.0.367 HF38 there is support for JSON when calling a REST data service from a Mashup. In this blog post I’m going to use a fake online REST service to demonstrate how it’s done.

The response data from the REST service call can be converted into a DataItem hierarchy or as a JSON string. This example will fetch a listing of users from the service and present them in a Mashup ListView and in a TextBox as a JSON string, side-by-side.

Mashup_REST_JSON_LIST

The Mashup XAML syntax will be the same as before when doing a REST service call. All you need to do now is set the REST.AcceptHeader parameter to application/json.
The REST.OutputType determines if the data response is available to the Mashup as an object hierarchy or as a JSON string. Use the value ‘DataItem’ to get DataItem hierarchy or set ‘application/json’ for a JSON string. If REST.OutputType is not set the default will be a DataItem hierarchy.

<mashup:DataListPanel.DataService>
    <mashup:DataService Type="REST">
        <mashup:DataService.Operations>
            <mashup:DataOperation Name="List">
                <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
            </mashup:DataOperation>
        </mashup:DataService.Operations>
    </mashup:DataService>
</mashup:DataListPanel.DataService>

To set the first column in the ListView to the company name use the binding below.

DisplayMemberBinding="{Binding [company].[name]}"

To access the JSON string when that is set as output type simply bind to ‘json’.

Text="{Binding [json]}"

Here is the complete Mashup code if you would like to try it out.

<Grid Margin="15,10,15,15" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:m3="clr-namespace:MForms.Mashup;assembly=MForms" xmlns:sys="clr-namespace:System;assembly=mscorlib">
   <Grid.RowDefinitions>
      <RowDefinition Height="1*" />
   </Grid.RowDefinitions>
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" />
      <ColumnDefinition Width="10" />
      <ColumnDefinition Width="1*" />
   </Grid.ColumnDefinitions>
   <mashup:DataListPanel DockPanel.Dock="Top" Name="UsersList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3">
      <mashup:DataListPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.OutputType" Value="DataItem" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataListPanel.DataService>
      <mashup:DataListPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
         </mashup:Events>
      </mashup:DataListPanel.Events>
      <DockPanel LastChildFill="False">
         <ListView DockPanel.Dock="Top" Name="ListViewCompany" ItemsSource="{Binding Items}" Style="{DynamicResource styleListView}" ItemContainerStyle="{DynamicResource styleListViewItem}">
            <ListView.View>
               <GridView ColumnHeaderContainerStyle="{DynamicResource styleGridViewColumnHeader}">
                  <GridView.Columns>
                     <GridViewColumn Header="Company" DisplayMemberBinding="{Binding [company].[name]}" />
                     <GridViewColumn Header="City" DisplayMemberBinding="{Binding [address].[city]}" />
                     <GridViewColumn Header="Name" DisplayMemberBinding="{Binding [name]}" />
                     <GridViewColumn Header="Email" DisplayMemberBinding="{Binding [email]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                     <GridViewColumn Header="Longitude" DisplayMemberBinding="{Binding [address].[geo].[lng]}" />
                  </GridView.Columns>
               </GridView>
            </ListView.View>
         </ListView>
      </DockPanel>
   </mashup:DataListPanel>
   <mashup:DataPanel Grid.Row="0" Grid.Column="2" Name="JsonString">
      <mashup:DataPanel.DataService>
         <mashup:DataService Type="REST">
            <mashup:DataService.Operations>
               <mashup:DataOperation Name="List">
                  <mashup:DataParameter Key="REST.BaseAddress" Value="https://jsonplaceholder.typicode.com/users/" />
                  <mashup:DataParameter Key="REST.AcceptHeader" Value="application/json" />
                  <mashup:DataParameter Key="REST.OutputType" Value="application/json" />
               </mashup:DataOperation>
            </mashup:DataService.Operations>
         </mashup:DataService>
      </mashup:DataPanel.DataService>
      <mashup:DataPanel.Events>
         <mashup:Events>
            <mashup:Event SourceEventName="Startup" TargetEventName="List" />
         </mashup:Events>
      </mashup:DataPanel.Events>
      <Grid Margin="10,0,10,0">
         <TextBox Text="{Binding [json]}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" AcceptsReturn="True" VerticalContentAlignment="Top" TextWrapping="Wrap" />
      </Grid>
   </mashup:DataPanel>
</Grid>

Mashup Converter with Zero Focus

In Smart Office 10.2.1.0.367 HF 38 there is a new value converter that converts a number to a string label focusing on zero; equals, above or below.

Mashup_Converter_Zero

The default behavior for the value converter is:

value < 0 returns NA
value = 0 returns No
value > 0 returns Yes
otherwise for non numeric input it returns an empty string

 Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}}"

 

The return labels for negative, zero and positive values can be customized by adding the ConverterParameter in the data binding containing three strings separated by _ or – characters. If the parameter is incorrect formatted the converter falls back to the default values and logs the issue if the log is in DEBUG mode.

Setting the following ConverterParameter will change the return labels to become Negative, Zero, Positive.

 Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}, ConverterParameter=Negative_Zero-Positive}"

 

Here is a the full code for you to try the converter out.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="clr-namespace:Mango.UI.Controls;assembly=Mango.UI" xmlns:mashup="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI" xmlns:utils="clr-namespace:Mango.UI.Services.Mashup;assembly=Mango.UI">
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="10" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
   </Grid.RowDefinitions>
   <TextBlock Grid.Row="0" Text="Test of AboveBelowZeroConverter. Default: Return NA for negative values, No for zero and Yes for positive values, otherwise empty string" Margin="10" />
   <DockPanel Grid.Row="2" Margin="10" LastChildFill="False">
      <TextBlock DockPanel.Dock="Left" Text="Enter a value:" Margin="5" />
      <TextBox DockPanel.Dock="Left" Name="InputValue" Height="24" Width="60" VerticalAlignment="Top" HorizontalAlignment="Stretch" />
      <TextBlock DockPanel.Dock="Left" FontSize="16" Foreground="Green" Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}}" Margin="5" />
   </DockPanel>
   <DockPanel Grid.Row="3" Margin="10" LastChildFill="False">
      <TextBlock DockPanel.Dock="Top" Text="Custom return value. Set binding 'ConverterParameter=negative zero-positive' (Three strings separated by _ or - character, if invalid converter falls back to default)" Margin="5" />
      <TextBlock DockPanel.Dock="Top" FontSize="16" Foreground="Green" Text="{Binding ElementName=InputValue, Path=Text, Converter={utils:AboveBelowZeroConverter}, ConverterParameter=Negative_Zero-Positive}" Margin="150,5,5,5" />
   </DockPanel>
</Grid>