Programatically Scrolling Through Lists and M3

Been a little while since my last post as we have just gone live with 10.1, but here goes…

So, one of my staff uses PMS050 panel A, and it doesn’t really have much in the way of easily getting down through the list to the product that you desire, so roll on jscript!

This was an interesting problem and not one that was really all that obvious to solve. The PressKey from IInstanceController method that Lawson provide didn’t seem to work, so I needed another way.

But first, there is a little other detail that we should discuss. When an M3 panel loads and has a ListView it will only retrieve enough records to populate the list + a few more. So your item master (MMS001) there may be several thousand entries, however when you first load MMS001 it will only retrieve maybe 40 records and depending on the size of the list display 30. When you scroll down the list the next group of records will be retrieved and added to the ListView.

This is pretty nifty and makes the solution scalable when there are large amounts of records. This creates a big problem if you have a situation where you have a customer order with 100 lines, with jscript if you enumerate the ListView you’ll only get the number or records that M3 has currently loaded in to the ListView, you won’t get all of those other lines – so you effectively need to scroll through the entire list first before you try enumerating.

Using System.Windows.Forms.SendKeys.SendWait () we can simulate a keypress.

So, the first thing that we need to do is import the Assembly.

import System.Windows.Forms;

Then we need to set the focus to the ListView

var listControl = gcontroller.RenderEngine.ListControl;
listControl.Focus();

And finally, do the actual send

System.Windows.Forms.SendKeys.SendWait(“{PGDN}”);

And putting it all together…

import System;
import System.Windows;
import System.Windows.Controls;
// used for the SendWait
import System.Windows.Forms;
import MForms;

package MForms.JScript {
   class TestSendKey {
   		var debug, button, content, gcontroller;
		public function Init(element: Object, args: Object, controller : Object, debug : Object)
		{
			try
			{
				this.gcontroller = controller;
				
				content = controller.RenderEngine.Content;
				
				button = new Button();
				button.Content = "Page Down";
				Grid.SetColumnSpan(button, 10);
				Grid.SetColumn(button, 10);
				Grid.SetRow(button, 0);
				content.Children.Add(button);
				button.add_Click(OnClick);
				button.add_Unloaded(OnUnloaded);
			}
			catch(ex)
			{
				debug.WriteLine(ex.Message);
			}
      }

		public function OnClick(sender: Object, e: RoutedEventArgs) 
		{
			try
			{
				// retrieve the ListView
				var listControl = gcontroller.RenderEngine.ListControl;

				// set the focus to the ListView
				listControl.Focus();
				// 'press' the Page Down key
				System.Windows.Forms.SendKeys.SendWait("{PGDN}");
				System.Windows.Forms.SendKeys.SendWait("{PGDN}");
			}
			catch(ex)
			{
				debug.WriteLine(ex.Message);
			}
		}

		public function OnUnloaded(sender: Object, e: RoutedEventArgs) 
		{
			button.remove_Click(OnClick);
			button.remove_Unloaded(OnUnloaded);
		}

   }
}

Update 20101122 – please be aware that SendKeys will send the key depression to the currently active window!

This entry was posted in Development, M3 / MoveX. Bookmark the permalink.

12 Responses to Programatically Scrolling Through Lists and M3

  1. Karin says:

    Strange that you did not get the PressKey from IInstanceController to work. We use it all the time. You will need to use at least the OnRequestedComplete event:
    this.controller.add_Requested(OnRequested);
    this.controller.add_RequestCompleted(OnRequestCompleted);

    So the init would look similar to this:

    public function Init(element: Object, args: Object, controller : Object, debug : Object)
    {
    // Set the member variables for later use
    this.controller = controller;
    this.content = controller.RenderEngine.Content;
    this.debug=debug;

    // Add event-handlers
    this.controller.add_Requested(OnRequested);
    this.controller.add_RequestCompleted(OnRequestCompleted);
    }

    It is important that you unregister depending on the type of request… so that the script gets unregistered 🙂

    public function OnRequested(sender: Object, e: RequestEventArgs)
    {
    if(e.CommandType == MNEProtocol.CommandTypePage) {
    debug.WriteLine("Page command");
    // This is before the request for more lines is done
    } else {
    debug.WriteLine("Other command, disconnecting event");
    controller.remove_Requested(OnRequested);
    }
    }

    public function OnRequestCompleted(sender: Object, e: RequestEventArgs) {
    if(e.CommandType == MNEProtocol.CommandTypePage) {
    debug.WriteLine("Page command");
    // Do your stuff here. Check for ITNO or make a new KeyPress
    } else {
    debug.WriteLine("Other command, disconnecting event");
    controller.remove_RequestCompleted(OnRequestCompleted);
    }
    }

    • potatoit says:

      Hi Karin,

      the PressKey() method works when using the documented strings (F1-F24 or ENTER), however I could never get it to work when using the PageDown command PGDN, or NEXT, Key.PageDown.ToString() etc but could never get the correct combination.

      Other attempts I managed to get page down events to work, however it would never retrieve new records.

      I did find it interesting that using the code that you have provided, the requests occur only when new records are retrieved from the server. I’ll have to have a bit more of a muck around with it. 🙂

      Thank you.

      Cheers,
      Scott

      • Karin says:

        Hi Scott,
        O forgot to put the page down call in there.
        It’s mformsInstanceController.PageDown() .

        So you don’t need to use the SendKey(). I use the PageDown(). As I understand it this method will always receive the next chunk of rows from M3. It can be 33 or 66 rows depending on the screen resolution. So if you are looking for a specific item, ITNO, then I would first look in the loaded list and if it is not there I would do a PageDown() util I find it or until there are no more lines. There is however a drawback with this appraoch… if there are many lines the request(s) should be able to cancel by the user. So there would have to exist some kind of escape for the end user. Like support escape button and perhaps a “cancel search” button.

        The methods I used are asynchronous (M3 server will be called on another thread). You do a PageDown and then you have to wait for the RequestCompleted. The request completed is fired when the reply from M3 is received. That’s is why I’m checking for page down and disconnecting – beacuase it can just as well be a new panel.

      • potatoit says:

        Hi Karin,

        brilliant – that looks like a great approach. Using the request completed solves a number of issues for me here and in other areas.

        I had seen the PageDown() method in the IInstanceController and tried to use it – but that was before I discovered the property which indicates if there are more records – so when it didn’t return any more records (because the server hadn’t completed the request) my code would exit and I had the impression it wasn’t doing anything of much use. :-[

        Thank you for setting me straight! 🙂

        Cheers,
        Scott

  2. Sam N says:

    Hi Scott,
    I was just trying to automate mass invoice re-prints from ARS200 -> OIS680. The idea is that the user goes to ARS200 filters by payer selects the invoices that he is interested in , right clicks and reprints invoice.

    So, I was planning on writing JScript code to automate simulation of {ENTER} keystroke to cycle through all the invoices sent to OIS680.

    The problem is the below line does not seem to work despite the fact that the focus is on the WFYEA4 field :

    System.Windows.SendKeys.Controls.Send(“ENTER”);

    To put it simply I am not able to figure out how to automate “Next ” button within any M3 program using JScript.

    Also is there way to use MForms automation in the same program without having to open another program.

    Thanks .
    Sam

    • potatoit says:

      Hi Sam,

      have you tried using the IInstanceController.PressKey() method?

      so you’d do something like
      controller.PressKey(MNEProtocol.KeyEnter)

      And sorry, I don’t quite follow your other question around MForms automation.

      Cheers,
      SCott

      • Sam N says:

        Hi Scott,
        That was brilliant.

        aInstanceController = controller;
        aInstanceController.PressKey(“F3”); //for closing window
        aInstanceController.PressKey(“F3”); // for Return

        Did the trick for me. Thank you for your very quick response. I find the code snippets in your blog extremely useful. Karin is cool to she addressed the second bit of my question.

        Thanks a ton.
        Sam

      • potatoit says:

        Not a problem at all. 🙂

    • karinpb says:

      Hi Sam,
      There is no way to run MForms automation in the current program. I get that this is something that you would like to be able to do in an easier way in the script and it is something we will consider for future versions.

      • Sam N says:

        Hi Karin,
        Thanks for your immediate response. It would be nice to be able to run MForms automation in the current program. It would be of great help to guys like me who are new to JScript coding within M3.

        Also, it would be nice to have the functionally of reprinting invoices in OIS680 by customer or CO number rather than just the invoice numbers. I think it is a basic functionality that M3 is lacking.

        Thanks a lot.
        Sam

  3. Sam N says:

    Hi Guys,

    I have written some JScript code to automate bulk printing of invoices from ARS200 -> OIS680. It works and could probably be refined a bit more. I am posting it here so that it could possibly be useful for someone out there.

    import System;
    import System.Data;
    import System.Collections.Generic;
    import System.Windows;
    import System.Windows.Controls;
    import System.Windows.Media;
    import System.Windows.Forms;
    import System.Drawing;
    import System.ComponentModel;
    import MForms;
    import MForms.Extensions;
    import Lawson.Search;
    import Mango.Core;
    import Mango.Search
    import Mango.UI;
    import Mango.UI.Services;
    import Mango.Services;
    import System.Windows.Input;
    import Mango.UI.Utils;
    import Mango.UI.Core;
    import Mango.UI.Core.Util;
    import Mango.UI.Services;
    import System.Web;
    import Mango.UI.Services.Messages;

    package MForms.JScript {
    class OIS680{
    var controller;
    var debug;
    var pressedKey;
    var i = 0;
    var button;

    public function Init(element : Object, args : Object, controller : Object, debug : Object) {
    var content : Object = controller.RenderEngine.Content;
    this.controller = controller;
    this.debug = debug;
    controller.add_RequestCompleted(OnRequestCompleted);
    debug.WriteLine(“Script initialized”);
    button1();
    }
    public function button1() {
    button = new Button ();
    button.Content = ‘Multiple Inv >’ ;
    button.Background = Brushes.YellowGreen;
    button.Foreground = Brushes.White;
    button.BorderBrush = Brushes.YellowGreen;
    button.FontWeight = FontWeights.Bold;
    button.ToolTip = ‘Click button if processing multiple invoices from ARS200’;
    button.HorizontalAlignment = HorizontalAlignment.Left;
    Grid.SetColumn (button, 40);
    Grid.SetColumnSpan (button,98);
    button.VerticalAlignment = VerticalAlignment.Top;
    Grid.SetRow (button,15)
    Grid.SetRowSpan(button,23);
    controller.RenderEngine.Content.Children.Add (button);
    button.add_Click(OnClick);
    button.add_Unloaded(OnUnloaded) ;

    }
    public function OnClick (sender:Object, e: RoutedEventArgs){
    controller.PressKey(“ENTER”);
    i++;
    }
    public function OnUnloaded (sender : Object, e:RoutedEventArgs){
    sender.remove_Click(OnClick);
    sender.remove_Unloaded(OnUnloaded);
    }

    public function OnRequesting(sender : Object, e : CancelRequestEventArgs) {
    }
    public function OnRequestCompleted(sender : Object, e : RequestEventArgs) {
    debug.WriteLine(“OnRequestCompleted(): ‘” + e.CommandType + “‘ = ” + e.CommandValue);
    debug.WriteLine(“Script unloaded”);
    if (isOdd(i)){
    runPresskey1();
    }
    else{
    if (e.CommandValue == “F3”)
    runPresskey2();
    }
    }
    public function runPresskey1(){

    controller.PressKey(“F3”);
    debug.WriteLine(“F3”);
    i++;
    }
    public function runPresskey2(){
    controller.PressKey(“ENTER”);
    debug.WriteLine(“ENTER”);
    i++;
    }
    public function isOdd(num) { return num % 2;}

    }
    }

  4. Rene Bottern says:

    Thanks Sam! Very useful bit of code 🙂
    /Rene

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