Here is a real example of how to add a column to a list in M3 using a Script for Smart Office. It’s an illustration of my previous post on the same subject, an implementation of what was discussed in the comments of the first post, and a continuation of Karin’s post.
In this example I will add three columns to the list of Stock Location – MMS010/B1 to display the Geographic codes X, Y, and Z, which correspond to the longitude, latitude, and altitude of a Stock Location in a Warehouse. The Geo codes are stored in MMS010/F.
Why it matters
The benefit of this kind of solutions is to avoid an M3 Java modification.
From a technical point of view, this example illustrates how to dynamically add content to an existing M3 panel, how to access the ListView’s new internals in Smart Office 10.x, how to call an M3 API, how to use a background thread, how to indicate activity in the UI thread while the background thread works, and how to use the ScrollViewer to load data at each page scroll.
Desired result
This is the desired result:
Source code
Here is the complete source code:
import System; import System.Collections; import System.Windows.Controls; import System.Windows.Input; import System.Windows.Media; import Lawson.M3.MI; import Mango.UI.Services.Lists; import MForms; /* Displays the Geo codes XYZ in MMS010/B1 in three new columns loaded by calling MMS010MI.ListLocations. Thibaud Lopez Schneider, Infor, 2012-09-27 */ package MForms.JScript { class ShowGeoCodes { /* PENDING - Horizontal align the columns contents to the right - Vertical align the columns headers to top - Auto width the columns */ var controller: Object, content: Object, debug: Object; var listView; // System.Windows.Controls.ListView var rows: System.Windows.Controls.ItemCollection; var columns: System.Windows.Controls.GridViewColumnCollection; var scrollViewer: System.Windows.Controls.ScrollViewer; var oldCount: int = 0, newCount: int = 0; var GeoCodes; // System.Collections.Generic.IList[Lawson.M3.MI.MIRecord] public function Init(element: Object, args: Object, controller : Object, debug : Object) { try { // global variables this.controller = controller; this.content = controller.RenderEngine.Content; this.debug = debug; this.listView = controller.RenderEngine.ListControl.ListView; // == controller.RenderEngine.ListViewControl this.rows = listView.Items; this.columns = listView.View.Columns; // append three new columns to the ListView var newColumns = ['Geo code X', 'Geo code Y', 'Geo code Z']; for (var i in newColumns) { var gvch = new GridViewColumnHeader(); gvch.Content = newColumns[i]; var gvc = new GridViewColumn(); gvc.Header = gvch; gvc.CellTemplateSelector = new ListCellTemplateSelector(columns.Count, controller.RenderEngine.ListControl.Columns); columns.Add(gvc); } // register the ScrollChanged event of the ListView oldCount = newCount = rows.Count; var border = VisualTreeHelper.GetChild(listView, 0); var grid = VisualTreeHelper.GetChild(border, 0); this.scrollViewer = VisualTreeHelper.GetChild(grid, 3); this.scrollViewer.add_ScrollChanged(OnScrollChanged); // load the Geo codes XYZ by calling MMS010MI var CONO = UserContext.CurrentCompany; var WHLO = ScriptUtil.FindChild(content, 'WWWHLO').Text; BeginLoadGeoCodes(CONO, WHLO); // attach event to cleanup controller.add_RequestCompleted(OnRequestCompleted); } catch (ex: Exception) { debug.WriteLine(ex); } } /* Loads the Geo codes XYZ by calling MMS010MI.ListLocations. */ function BeginLoadGeoCodes(CONO: int, WHLO: String) { controller.RenderEngine.ShowMessage('loading Geo codes...'); content.Cursor = Cursors.Wait; var record = new MIRecord(); record['CONO'] = CONO; record['WHLO'] = WHLO; var parameters = new MIParameters(); parameters.OutputFields = ['WHSL', 'GEOX', 'GEOY', 'GEOZ']; parameters.MaxReturnedRecords = 0; MIWorker.Run('MMS010MI', 'ListLocations', record, EndLoadGeoCodes, parameters); } /* Handles the response from MMS010MI.ListLocations. */ function EndLoadGeoCodes(response: MIResponse) { try { controller.RenderEngine.ClearMessage(); content.Cursor = Cursors.Arrow; if (response.HasError) { controller.RenderEngine.ShowMessage(response.ErrorMessage); } else { this.GeoCodes = response.Items; ShowGeoCodesXYZ(0, rows.Count-1); } } catch(ex: Exception) { debug.WriteLine(ex); } } /* Loads more rows on ScrollViewer. */ function OnScrollChanged(sender: Object, e: ScrollChangedEventArgs) { try { if (e.VerticalChange != 0) { oldCount = listView.Items.Count; } else { var newCount = listView.Items.Count; var diff: int = newCount - oldCount; var fromRow = oldCount; var toRow = listView.Items.Count - 1; if (diff > 0) { ShowGeoCodesXYZ(fromRow, toRow); } } } catch (ex: Exception) { debug.WriteLine(ex); } } /* Shows the Geo codes XYZ for the specified rows. */ function ShowGeoCodesXYZ(fromRow: int, toRow: int) { var rows = IList(listView.ItemsSource); for (var i = fromRow; i <= toRow; i++) { var WHSL = rows[i].Item[0]; var codes = GetGeoCode(WHSL); // replace this row by a new row that's incremented by three new columns var row = rows[i]; var oldArray = row.Items; var newArray = new String[oldArray.length + 3]; oldArray.CopyTo(newArray, 0); newArray[newArray.length-3] = codes.GEOX; newArray[newArray.length-2] = codes.GEOY; newArray[newArray.length-1] = codes.GEOZ; row.Items = newArray; rows.RemoveAt(i); rows.Insert(i, row); } } /* Returns the Geo codes XYZ for the specified WHSL. */ function GetGeoCode(WHSL: String) { var i = 0; while (i < this.GeoCodes.Count && this.GeoCodes[i]['WHSL'] != WHSL) i++; // search array if (i < this.GeoCodes.Count) return { 'GEOX': this.GeoCodes[i]['GEOX'], 'GEOY': this.GeoCodes[i]['GEOY'], 'GEOZ': this.GeoCodes[i]['GEOZ'] }; // found WHSL else return { 'GEOX': '', 'GEOY': '', 'GEOZ': '' }; // no hit } function OnRequestCompleted(sender: Object, e: RequestEventArgs) { try { var controller: MForms.InstanceController = sender; if (controller.RenderEngine == null) { // program is closing, cleanup scrollViewer.remove_ScrollChanged(OnScrollChanged); controller.remove_RequestCompleted(OnRequestCompleted); } } catch (ex: Exception) { debug.WriteLine(ex); } } } }
Result
This is the final result:
Future work
This solution loads all records from the API call with MaxReturnedRecords=0. This could be a problem when there are more than several hundred records, or when the server/network response time is bad. I yet have to find a solution to improve this.
Also, the user could scroll the page while the response of the API call hasn’t arrived yet. I yet have to improve the code for that.
Finally, in my next article I will illustrate how to achieve the same result without programming, by using Custom Lists and Mashups.
UPDATE 2012-09-27
I updated the script to detach the event handlers on program close, i.e. cleanup.
Related articles:
- Custom Lists & Mashups, how to add columns to a list with a Custom List and Mashups
- How to add a column to a list, older post on how to add columns to a list with a script
- How to add a column to a list — #Comments
- Adding a new column in a M3 List, by Karin
- BackgroundWorkers in Smart Office Scripts – Part 2 – How to disable/enable the user interface, how to indicate activity, and how to show progress
- Geocoding of Stock Locations in MMS010









































