Hyperlink columns in M3 lists

This post is a follow-up to a question in the comment section on a previous post called Adding a new column in a M3 List. The question was how to add a clickable hyperlink in a new list column. This could be solved in a generic way by creating your own data template and template selector or by reusing existing functionality in MForms. In this post I will just cover the second part by reusing existing functionality. The more generic solution for custom content in list cells might be covered in a future post.

The example script in the end of this post shows how to a new list column with a hyperlink. When the link is clicked a message dialog is shown which shows the content of the first list cell in the selected list row. The script can be used as a starting point for doing something more useful such as launching a new task with parameters that uses data from the selected list row.

Script overview
The Init function subscribes to the Requested event so that it can disconnect event handlers and avoid memory leaks. It then uses the Dispatcher to call the InitializeColumn method with a slight delay to let the panel render before it is modified.

The InitializeColumn function adds a click handler for hyperlink clicks. The MForms list also has a handler for this but we will construct the column so that the standard MForms handler does not handle the hyperlink click. The next thing is to check if the column has already been added using the HasColumn function. If the column is not in the list it is added using the CreateColumn function. The next step is to add the data for the new column to each list row. This is only done if the extra list cell has not been added already which would be the case for the first list rows after a page down request for example.

The CreateColumn function creates a new GridViewColumn and sets an instance of ListCellTemplateSelector as the cell template selector. The Hyperlink property on the ListCellTemplateSelector is set to an empty instance of the Hyperlink class which is part of the personalization classes in Smart Office. When the Hyperlink instance is empty a hyperlink will be generated but the MForms click handler for hyperlink clicks will ignore it which means that we can handle the click instead.

The OnClickHyperlink function handles the click event for the added hyperlink. In this example I just get the value for the first list cell of the selected list row and show it in a message dialog.

M3_HLCOL

import System;
import System.Windows;
import System.Windows.Controls;
import System.Windows.Data;
import System.Windows.Media;
import System.Windows.Markup;
import System.Windows.Threading;
import MForms;
import Mango.UI;
import Mango.UI.Services.Lists;
import System.Windows.Documents;

package MForms.JScript {
   class HyperlinkColumnTest {

      var columnTag = "LinkColumn";
      var debug;
      var controller;
      var listView;

      public function Init(element : Object, args : Object, controller : Object, debug : Object) {
         this.debug = debug;
         this.controller = controller;

         controller.add_Requested(OnRequested);

         var action : Action = InitializeColumn;
         Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input, action);
      }

      public function OnRequested(sender : Object, e : RequestEventArgs) {
         try {
            if (controller != null) {
               controller.remove_Requested(OnRequested);
               controller = null;
            }
            if (listView != null) {
               var handler : RoutedEventHandler = OnClickHyperlink;
               listView.RemoveHandler(Hyperlink.ClickEvent, handler);
               listView = null;
            }
         } catch (ex) {
            debug.Error("OnRequested", ex);
         }
      }

      private function InitializeColumn() {
         try {
            this.listView = controller.PanelState.ListControl.ListView;

            var handler : RoutedEventHandler = OnClickHyperlink;
            listView.AddHandler(Hyperlink.ClickEvent, handler);

            var gridView = listView.View;
            var columnCount = gridView.Columns.Count;
            var lastIndex = columnCount - 1;
            var columnHeader = "Link";
            var linkText = "Link";

            if (!HasColumn(gridView, columnTag)) {
               columnCount++;
               lastIndex++;
               gridView.Columns.Add(CreateColumn(controller.PanelState.ListControl, lastIndex, columnHeader));
            }

            for (var row in listView.Items) {
               if (row.Items.Length < columnCount) {
                  AddCellsToRow(row, columnCount);
                  row.Items[lastIndex] = linkText;
               }
            }

            listView.Items.Refresh();
         } catch (ex) {
            debug.Error("InitializeColumn", ex);
         }
      }

      public function OnClickHyperlink(sender : Object, e : RoutedEventArgs) {
         try {
            if (e.OriginalSource != "System.Windows.Documents.Hyperlink") {
               return;
            }

            e.Handled = true;
            var row = listView.SelectedItem;
            if (row != null) {
               var message = "Value in first column: " + row[0];
               ConfirmDialog.ShowInformationDialog("Hyperlink clicked", message);
            }
         } catch (ex) {
            debug.Error("OnClickHyperlink", ex);
         }
      }

      private function HasColumn(gridView, tag) {
         for (var column in gridView.Columns) {
            if (column.Header.Tag == tag) {
               return true;
            }
         }
         return false;
      }

      private function AddCellsToRow(row, cellCount) {
         var items = new Object[cellCount];
         row.Items.CopyTo(items, 0);
         row.Items = items;
      }

      private function CreateColumn(listControl, index, headerText) {
         var column = new GridViewColumn();

         var selector = new ListCellTemplateSelector(index, listControl.Columns);
         selector.Hyperlink = new Mango.UI.Services.Customization.Hyperlink();
         column.CellTemplateSelector = selector;

         var header = new GridViewColumnHeader();
         header.Tag = columnTag;
         header.VerticalContentAlignment = VerticalAlignment.Top;
         var textBlock = new TextBlock();
         textBlock.Text = headerText;
         textBlock.Margin = new Thickness(2);
         header.Content = textBlock;
         column.Header = header;
         return column;
      }
   }
}

17 thoughts on “Hyperlink columns in M3 lists

  1. Daniel Henningsver

    Which version of Smart Office is required for this to work? I tired to apply this script but I only get a column with header created. No links on the lines.

    Reply
    1. norpe Post author

      It should at least work with Smart Office 10.0.5.x but it might work with slightly older versions. The lowest version I have verified it to work with is 10.0.5.4.

      Reply
    2. norpe Post author

      The script required Smart Office 10.0.5.2 since ListCell was used with Hyperlink. Turns out you don’t actually have to use ListCell in this case so the script can be modified to work in 10.0.5.1 and probably somewhat older versions as well.

      Replace these lines:
      var cell = new ListCell();
      cell.Text = linkText;
      row.Items[lastIndex] = cell;

      With this line:
      row.Items[lastIndex] = linkText;

      I will update the script in the post as well.

      Reply
  2. Newton

    I think the script is triggering on clicks on the list header and scrollbars as well. To solve this I added a condition in the OnClickHyperlink to look for the source of the click.

    e.OriginalSource==”System.Windows.Documents.Hyperlink”

    Reply
  3. Nishant

    Hi,
    I need to give a link at every row of the list which will take the user to another program whith the details of the selected row.
    Can you help me out with this….

    Reply
    1. karinpb

      You need to investigate if the program you would like to navigate to supports bookmarks and if you have all the data (keys) available in the list.
      That is the first step. Once that is done you build the URL but remember to URLencode the parameters after building the data together. In Mforms there is a class MForms.Mashup.Bookmark that can be used to create the bookmark.
      I have done a hardcoded example that calculates the URL you need to appy this to your example and get the live data from the row. The example shows how to build a bookmark in JScript. You can check the MForms.Mashup.Bookmark documentation in the Mashup SDK API documentation or in the API documentation for the Smart Office SDK. Since the class is in the Mashup namespace it should be part of the API documentation that is available in the Mashup Designer.

      This is a simple example of building the bookmark so you need to combine that with the other information on how to get data from the row.

      import System;
      import System.Windows;
      import System.Windows.Controls;
      import MForms.Mashup;
      import MForms;
      import Mango.UI.Services;

      package MForms.JScript {
      class ButtonTest {
      var content, debug, button;
      var controller;

      public function Init(element: Object, args: Object, controller : Object, debug : Object) {
      debug.WriteLine("Script Initializing.");
      if(element != null) {
      debug.WriteLine("Connected element: " + element.Name);
      }
      this.controller = controller;
      this.debug = debug;
      content = controller.RenderEngine.Content;

      var button : Button = new Button();
      button.Content = "Test";
      button.Name = "addShortcutButton";
      Grid.SetColumnSpan(button, 10);
      Grid.SetRowSpan(button, 3);
      Grid.SetColumn(button,50);
      Grid.SetRow(button,1);
      content.Children.Add(button);
      controller.add_Requested(OnRequested);
      this.button = button;
      button.add_Click(OnClick);

      }

      public function OnClick(sender: Object, e: RoutedEventArgs) {

      var bookmark = new Bookmark();
      bookmark.Program="MMS001";
      bookmark.Table="MITMAS";
      bookmark.KeyNames="MMCONO,MMITNO";
      bookmark.Option="5";
      bookmark.Panel="E";
      bookmark.AddKeyValue("MMITNO","1000"); // TODO you should get the value from the selected line
      bookmark.AddKeyValue("MMCONO",UserContext.CurrentCompany)
      var uri = bookmark.ToUri();
      debug.WriteLine(uri);
      DashboardTaskService.Current.LaunchTask(uri);

      }

      public function OnRequested(sender: Object, e: RequestEventArgs) {
      button.remove_Click(OnClick);
      controller.remove_Requested(OnRequested);
      }
      }
      }

      Reply
  4. Chaitanya Shah

    HI,
    This Blog is really Good for Infor Developer

    I am New Developer of Infor.I have done scripting exercise its working perfect but where to uploaded jscript file?

    Please help me!

    Reply
    1. karinpb

      Hi,
      Both M3 and S3 have a ScriptDevelopers guide as part of the documentation. It contains information on how to upload the jscript file. You must be an Smart Office Administrator to upload the jscript file and the process differs with version. The Scripting Guide also contains important information that you should read before deploying jscripts. Those guides are part of the download zip when you download the software. Contact your system administrator or Infor support and they’ll help you further.

      Reply
  5. Doms Molina

    Hi Karin,

    Is it possible to add a hyperlink to existing columns? Hyperlink will have to come from a certain fields retrieved via API.

    Reply
    1. karinpb

      Yes, it’s possible. But I don’t have an example for it. Just make sure that you get the values you need via API once the link is clicked and not before that. This is good requirement and I have created an enhancement suggestion to allow a personlization to be a link that will trigger a JScript. Such an enhancement would make this scenario easier to achieve.

      Reply
      1. karinpb

        Great. Ask him to write a post on it. I did find an example of showing an image as a tooltip.

        	private function CreateToolTip(presenter : Object, itemNumber : Object) {
        	// Create and run worker
        	var worker = new BackgroundWorker();
        	worker.add_DoWork(OnDoWorkGetDafImage);
        	worker.add_RunWorkerCompleted(OnRunWorkerCompletedGetDafImage);
        	var args = new Object[2];
        	args[0] = presenter;
        	args[1] = itemNumber;
        	worker.RunWorkerAsync(args);
        }
        
        You get a hold of the preview URL from IDM and then generate the image in a tooltip.
        
        public function OnRunWorkerCompletedGetDafImage(sender : Object, e : RunWorkerCompletedEventArgs) {
        	try {
        
        		if (e.Result != null) {
        			var inArgs = e.Result;
        			var presenter = inArgs[0];
        			var itemNumber = inArgs[1];
        			var url = inArgs[2];
        
        			if (url != null) {
        				// Create bitmap image
        				var bi = new BitmapImage();
        				//outStream.Position = 0;
        				bi.BeginInit();
        				//bi.StreamSource = outStream;
        				bi.UriSource = new Uri(url, UriKind.Absolute);
        				bi.EndInit();
        
        				var image,
        				stPanel,
        				tBlockItem,
        				tBlockItemName;
        
        				// Initialize image object
        				image = new Image();
        				image.Stretch = Stretch.None;
        				image.HorizontalAlignment = HorizontalAlignment.Left;
        				image.VerticalAlignment = VerticalAlignment.Top;
        				image.Source = bi;
        
        				// Initialize textblock item number object
        				tBlockItem = new TextBlock();
        				tBlockItem.HorizontalAlignment = HorizontalAlignment.Center;
        				tBlockItem.VerticalAlignment = VerticalAlignment.Top;
        				tBlockItem.Text = itemNumber;
        
        				// Initialize textblock item name object
        				tBlockItemName = new TextBlock();
        				tBlockItemName.HorizontalAlignment = HorizontalAlignment.Center;
        				tBlockItemName.VerticalAlignment = VerticalAlignment.Top;
        				tBlockItemName.Text = itemName;
        
        				// Initialize stackpanel object
        				stPanel = new StackPanel();
        				stPanel.Children.Add(image);
        				stPanel.Children.Add(tBlockItem);
        				if (itemNameColumn >= 0) {
        					stPanel.Children.Add(tBlockItemName);
        				}
        
        				// Set tooltip
        				presenter.ToolTip = stPanel;
        			} else {
        				presenter.ToolTip = "No image found in IDM";
        			}
        
        		} else {
        			Log("Function OnRunWorkerCompleted : Result is null");
        		}
        
        	} catch (err) {
        		Log("Function OnRunWorkerCompleted : Error : " + err);
        	}
        }
        
        

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s