Adding a new column in a M3 List

Every now and then we do get questions about manipulating the M3 List. It is possible to add a column using JScript but there is no supported API for the different manipulations. But that does not stop the creative people out there. Now I would like to give you a heads up becuase the list implementation has changed slightly in Lawson Smart Office 10.

You can no longer access the Items property on the listview directly.

If you get the following error in your script: “Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead”, you need to use the ItemsSource on the listview instead.

We hope to introduce a supported API in coming version but until then this is the new way to do it in LSO 10. Note however that implementations details might change and you can only expect support for the APIs documented in the JScript Developers guide for M3.

Below is what the added column, “My Column”, will look like in CRS610.

Below is a JScript that will add a column header and a few rows.

 import System;
 import System.Windows;
 import System.Windows.Controls;
 import System.Windows.Media;
 import System.Collections;
 import Mango.UI.Services.Lists;

package MForms.JScript {
   class AddColumnSimple {
      var listView;

      public function Init(element: Object, args: Object, controller : Object, debug : Object) {
         debug.WriteLine("Script Initializing.");
         if(element != null) {
            debug.WriteLine("Connected element: " + element.Name);
         }
         try {
            listView = controller.RenderEngine.ListViewControl;
            var listControl = controller.RenderEngine.ListControl;
            var columns = listView.View.Columns; // System.Windows.Controls.GridViewColumnCollection

            // Add header
            var columnHeader = new GridViewColumnHeader();
            columnHeader.Content = "My Column";
            var column = new GridViewColumn();
            column.Header = columnHeader;
            column.CellTemplateSelector = new ListCellTemplateSelector(columns.Count, listControl.Columns);
            columns.Add(column);

            var rows:IList;
            if (listView.ItemsSource != null){
                debug.WriteLine("New version of ListView");
                rows = IList(listView.ItemsSource); // Use the ItemsSource to get data
            }else{
                debug.WriteLine("Old version of ListView");
                rows=listView.Items; //Get the items
            }

            var columnCount=columns.Count;

            for (var i = 0; i < rows.Count; i++) {
               var row = rows[i];
               var newItems = new String[columnCount];
               row.Items.CopyTo(newItems,0);
               row.Items=newItems;
               // Replace row
               rows.RemoveAt(i);
               rows.Insert(i,row);
            }

            // The new placeholder is in the list - add some data
            var index=columns.Count-1;

            // Replace with a for loop to loop all rows
            rows[0].Items[index] = "Extra data row 1";
            rows[1].Items[index] = "Data row 2";

         } catch (ex: Exception) {
            debug.WriteLine(ex);
         }
       }
   }
}

The code is very basic and should be considered as a starting point.

Thibaud has a more detailed post in his blog.
Be sure to read the comments since there are issues on scrolling and threading that you should consider.

A general note on any “Get data per list row” scenario is that it is not a good idea. Those kind of modifications should be done in the M3 program.
On the other hand, if you already have access to the data and the new column data is calculated from already existing columns, then you have an excellent scenario for this kind of trick!

21 thoughts on “Adding a new column in a M3 List

  1. potatoit

    Hi Karin,

    great post, if Smart Office is now using the ItemsSource now does that mean the behaviour of retrieving the records has changed at all? Or does it use an ObservableCollection and just add the extra items to that and then the ListView automatically populates?
    If it uses an ObservableCollection will that be exposed (and documented) to let us subscribe to it? I could see that being very useful. :-)

    if you’re looking at introducing APIs around the ListViews, it would be really nice if the TextBoxes in the ColumnHeaders were exposed as properties – along with a method which would apply the filter.
    Eg. the “Item Number” TextBox in the MMS001/B1 ListView

    Cheers,
    Scott

    Reply
  2. norpe

    The TextBoxes for the position fields in the column headers are already exposed by the PositionFields property of the ListControl class.

    To apply a filter from JScript you would set the values in the TextBoxes and then apply the filters using:
    controller.PressKey(MNEProtocol.KeyEnter);

    Reply
  3. David

    Is it possible to add a column to an S3 form (such as PO20.1) using this technique?
    As a broader question on S3 and scripting, how do you attach a script to a list view of a form?
    OR do you attach the script to the form view?

    I have watched all of the scripting videos and read the Developers Guide but don’t see how to attach scripts to a list view.

    Reply
    1. karinpb Post author

      Hi David,
      I checked with a collegue and this is his reply.

      “Adding a column to an S3 List is only done from the Choose Columns dialog. To open, tight-click on a column header and select “More” or from the menus select Tools -> Personalization -> Choose Columns.

      Scripting in S3 can only be attached to the standard forms view and not the list view.”

      Regards
      Karin

      Reply
  4. Jean Monchery

    Thanks Karin,
    That really helped me out. I have successfully added a new column, but I am not able to display my updated rows as I scroll up.

    Reply
    1. karinpb Post author

      Hi Jean,
      Do you mean scroll up or scroll down? Like PageDown or PageUp.
      My guess is that you mean page down. As you page down new rows are retrieved from the backend and if you only add rows when the form is loaded you will only have the initial 33 (or 66) so you need to add your column data for those new rows as well.

      This blog might help you get started .

      Reply
  5. Mikael H

    Hi. You said “A general note on any “Get data per list row” scenario is that it is not a good idea”. What I understand you could get bad performance, correct ?
    It is a pity because this is a really good example how to show the good aspects of LSO-scripts instead of M3-modifications for a customer. Is it possible for you to explain the downsides a bit more and how we could avoid the performance problems with for example adding a new column based on MI-calls (MDBREADMI) ? Thanks a lot /mikael

    Reply
    1. norpe

      The best solution for this is for the BE program to return the data. If the program is using View lists it might be possible to add the column in the View. For static lists I guess the program has to be modified.

      The reason we say it is not a good idea is that there are a lot of potential issues with doing this both on the client and on the server. If each program start results in 33 additional MI-transaction calls that could be an issue with a lot of users. If the users than starts to page down quickly in the list this will generate hundreds of MI-calls, for each user.

      It is also very difficult to make a correct implementation of this behavior. The script should stop making calls if the user navigates to another panel, perhaps it should not load additional data for list row that are not visible yet etc. Covering all possibilities and error handling is complicated for this scenario.

      We’re not saying that you should never do this, but if you do you should be aware of the consequences, test with more than one user a a time, make sure there are no performance issues and make sure that the script implementation is solid.

      Reply
  6. Pingback: How to add a column to a list (continued) « M3 ideas

  7. Amanda & TOve

    Hi
    We have just found your blog, great!
    We have a problem in a Mashup we are trying to build.
    We have one listpanel CRS610 and one listpanel MMS002, we wan’t to use information from this panels to trigger a new listpanel (OIS320)
    Customer from CRS610 and Item from MMS002. However we can’t figure out how to set up the events, when we choose cuurentItemChanged in one list this overwrites the value from the other. (either custoerm or item is blank) is there a way to work past this issue? thanks in advance!

    Reply
    1. karinpb Post author

      Hi,
      The solution is to bind the customer and item from the respective list using the CurrentItem property on the ListPanel. You can read about the property in the API documentation found under the help menu. The reason the value is empty now is because it’s not available in the panel. You need to add cuno / itno with the value set using a Binding.

      In that case you will have both values when both lists have a selection.

      If you only want to load OIS320 when both customer and item is selected you can add a button for triggering OIS320, or try adding conditions in the event.

      I can try and create an example tomorrow…

      Reply
  8. Gaston

    Karin,

    I did apply most of the same code to add a new column to the OIS320/B List, the value for this new column is a calculation from another column within the same list. My script was successfully tested and then released into Production. However, one user in Production does not see the new column in the list as soon it opens OIS320/B, the new column only appears for this user after pressing Action/F8-Scroll Forward. In my case, as soo as I open OIS320 I can see the new column and all works fine. Do you know if perhaps a setting is missing for this user or if it is script related?

    Thank you,
    Gaston

    Reply
  9. Jean Monchery

    Hi Karin,
    While trying to copy a B Panel list view
    (var newItems = new String[row.Items.length];
    row.Items.CopyTo(newItems, 0) ) I got an error because the B Panel I was copying had editable cells. How can I add editable cells as my custom column?

    Reply
    1. karinpb Post author

      Hi!
      What error message did you get exactly? Are you in a version where you cannot use ItemsSource instead? Becuase if you manipulate Items and are getting an error I’m suspecting that you get a framework element which is attached to the visual tree so you can not move it around without hitting the issue that it already has a parent. I don’t know how to fix this on the top of my head. Set parent to null? What’s the program? And I assume you don’t want to add an editable cell, but some kind of view only value? (calculated I hope)

      Reply
  10. Jean Monchery

    The version of LSO that I’m currently using is 10.0.4.1.39, and I am using the ItemSource to get the data. the error I am getting is “Error: At least one element in the source array could not be cast down to the destination array type”. The program I am working with is MWS420/B1, I want to be able to manipulate the editable cells.

    Reply
    1. norpe

      In editable lists the row will contain EditableCell (Mango.UI.Services.Lists.EditableCell) objects instead of strings for the cells that can be edited. If you just need to get a cell value you should always use a utility method called GetCellText in the MFormsUtil class.

      Example: Gets the text for the first cell
      var text = MForms.MFormsUtil.GetCellText(listRow, 0);

      If you need to set values the EditableCell object has a Text property.

      Reply

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 )

Connecting to %s