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!
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
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);
Well I’ll be…
Thank you, that certainly makes them easier to work with 🙂
Cheers,
Scott
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.
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
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.
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 .
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
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.
Pingback: How to add a column to a list (continued) « M3 ideas
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!
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…
Hi,
I did a new post on this topic. Check out my Mahups: http://lawsonsmartoffice.com/2012/12/10/m3-price-simulation-building-a-mashup-with-two-lists-that-trigger-a-third-list/
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
Hi Gaston,
Your issue has to be script related, I would look for timing issues.
hi i need to add new Checkbox to screen by using javascript
Hi,
First of all, is this in a list och detail screen? What is your scenario? Is it to show a checkbox instead of 1/0 in a list? Or something else?
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?
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)
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.
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.
Hello,
In PMS230, there is a schedule number field that uses what I think is an editable cell. I want to use jscript to make it NOT editable. What line of code could I use to disable editting on that field?
Hi Karin,
When adding a new column to a list like OIS101B, I always encounter the case where my new column header shows multiple times as a result of navigating away from the panel. How could I freeze or ontrol my column header to appear only once in the view?
Thank you,
Gaston
The list controls are cached in many cases so the script must make sure that it does not add the same column more than once. If you navigate from B to E and then back the list control will in most cases be the same. Doing a refresh (F5) on a B-panel or changing sorting order / view will create a new list and so on. To handle all these cases the script needs to check if the column already exists.
Hi norpe,
I am now checking my columns in order to show the new column one time when navigating to another pannel and back so it is not showing mutiple times.
In all of the examples I have seen, the new column always get added to the end of the list view, is it feasible to insert the new column in between existing ones?
Thank you for your help,
Gaston
It should be possible to add a new column between existing columns. The existing columns are already bound to the data using the original column index. Just make sure to test the solution so that it does not break any framework functionality such as show/hide columns in a static list etc.
I’m trying to create some kind of clickable shortcut or link in my new column. Is it possible to add an “OnClick” event for every row in this column that runs some code? I have also experimented with adding buttons instead of plain text to the column but without success. Do you have any other suggestions for how to solve this? (I don’t want the whole row to be clickable, just the content of this column. The content can be an email address or an invoice number for example and the code might call a ListOption etc.)
It should be possible by creating a template in code and using AddHandler to get the click events. I’ll try to get time to write a post about how to do that in more detail.
On the previous version of LSO (version 9.0) this code segment use to work
var border = VisualTreeHelper.GetChild(listView, 0);
var grid = VisualTreeHelper.GetChild(border, 0);
this.scrollViewer = VisualTreeHelper.GetChild(grid, 3);
this.scrollViewer.add_ScrollChanged(OnScrollChanged);
but ever since the upgrade I’ve been receiving this error:
System.ArgumentOutOfRangeException: Specified index is out of range or child at index is null. Do not call this method if VisualChildrenCount returns zero, indicating that the Visual has no children.
Parameter name: index
Actual value was 3.
at System.Windows.FrameworkElement.GetVisualChild(Int32 index)
at System.Windows.Media.Visual.InternalGet2DOr3DVisualChild(Int32 index)
at System.Windows.Media.VisualTreeHelper.GetChild(DependencyObject reference, Int32 childIndex)
is there a new way to access the scrollViewer with the new version of LSO?
Hi,
There is always a chance that the visual tree might change between versions. When we did the new style a lot has changed. You can never rely on the visual tree like that.
You can make the code more robust by using one of the method that looks for a child of a specific type, like ScrollViewer instead. I’ll check the exact method name tomorrow.
Jscript code should never expect elements in a control template to be on a specific index. This is an implementation detail that might change between versions. To find parts of a control such as the ScrollViewer the script should either traverse the visual tree or use existing helper methods for finding the element.
JScript Example using a method in the framework Helpers class:
import System;
import System.Windows;
import System.Windows.Controls;
import MForms;
import Mango.UI.Utils;
package MForms.JScript {
class FindChildTest {
var typeScrollViewer : Type = Type.GetType("System.Windows.Controls.ScrollViewer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
public function Init(element : Object, args : Object, controller : Object, debug : Object) {
var listControl = controller.RenderEngine.ListControl;
if (listControl == null) {
debug.WriteLine("No list found");
return;
}
var scrollViewer = Helpers.FindElementOfType(listControl.ListView, typeScrollViewer);
if (scrollViewer != null) {
debug.WriteLine("Found ScrollViewer");
// TODO ...
} else {
debug.WriteLine("No ScrollViewer found");
}
}
}
}
How can I read only m3 column . program name is m3 supplier invoice and column name is Inv Qty . please help me.
Hi,
I’m afraid we can’t help with specific scripts but we can help you with tips. Start by finding out the field names of those fields, the read this postfor tips on how to get the value.
If that info is not enough try and investigate the visual tree with Snoop UI. If you still have issues let me know the name of the fields. I think we have had a question on read only fields before. Have you searched the blog?
Hi
I have created a new column and new data is populating the new column.
Now to the issue, how do I make the data in the new column to be a click-able URL?
//Lasse
Check out this new post that describes how to do this.
https://smartofficeblog.com/2014/04/25/hyperlink-columns-in-m3-lists/
Pingback: Hyperlink columns in M3 lists | Developing for Infor Smart Office
I want to know if there’s a way to get and set the color of text in the browse list through a J Script instead of using personalize.
If you mean the list in the F4 Browse dialog there is no supported way of changing the colors in that list.
In the normal lists you can use custom cell templates to change color using JScript. I’ll show how to do this in a future blog post.
Hi Guys,
Feels good to be writing you again. You have inserted text values (String) in the new columns(new fields in the new column). How can I insert a new column full of check boxes instead of Strings.
Thanks.
Sam
Hi,
Yes that is possible by defining a DataTemplate. Norpe is working on an example and it will be published on the blog.
thanks alot ,, i want to Remove an existing column ,,, for example if i want to Hide the first Column ,,i used that Code in Script DLL
IList columns = (IList)listControl.Columns;
columns.Remove(columns[0]);
—–
actually the number of Columns is decreased ,, but on the View the Column STill exists ,,, i found using Google the Property AutoGenerateColumnsProperty ,, but i don’t know where i can find it . i think i need to set it to false before removing the required column.
regards
Hi,
The MForms list is generated in code and it is not a DataGrid so AutoGenerateColumnsProperty does not apply.
If it is a GridView you should be able to set the width to 0 on the GridViewColumnHeader. Removing it is not the trick.
I have a Jscript that does some calculations on a list view but I’m noticing that going back to the browse after I’ve double clicked the item the script is executed twice on all items except the item I previously selected. Is there a way to stop this from happening?
If you mean back to the browse as back to the list you must unattach your events. Going back will load the script again and you will attach the eventhandler again. Failing to detach the eventhandles will lead to memory issues.
I did detach my events by using scrollViewer.remove_ScrollChanged(OnScrollChanged). and
controller.remove_RequestCompleted(OnRequestCompleted) but it’s still re-executing the script, is there another or better way to do it?
Hi, No controller.remove_RequestCompleted(OnRequestCompleted) is the right way to do it. Can you share more of your script? What version are you running? Why are you checking the Scroll on the list?
I’ll be glad to give some more info. Im using version 10.2.0.049, I have an event to do the calculation as the user scrolls.
var typeScrollViewer : Type = Type.GetType(“System.Windows.Controls.ScrollViewer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”);
this.scrollViewer = Helpers.FindElementOfType(listControl.ListView, typeScrollViewer);
if (scrollViewer != null) {
this.scrollViewer.add_ScrollChanged(OnScrollChanged);
}
controller.add_RequestCompleted(OnRequestCompleted);
//This is the code inside OnRequestCompleted( minus the try and catch)
var controller : MForms.InstanceController = sender;
if (controller.RenderEngine == null) {
var controller;
content = null;
debug = null;
rows = null;
columns = null;
oldCount = 0,
newCount = 0;
listControl = null;
columnIndex = null;
// program is closing, cleanup
//remove any objects
scrollViewer.remove_ScrollChanged(OnScrollChanged);
controller.remove_RequestCompleted(OnRequestCompleted);
}
I added a column that adds/subtracts two values from the grid but, it calculates only 33 rows and then as you scroll down it does not calculate the values from the other rows. Anybody can help me with this?
Hi, You need to subscribe to requested and requestCompleted event so that you can re-calculate the new rows as more rows are loaded. But you have to be careful and unload the script and eventhandlers as soon at the user navigates to another panel. You also need to keep track of / check if you have already calculated a row and test with positioning etc as well. This post should help you get started.
Pingback: Adding Columns to a ListView – when there are Editable Cells | Potato IT
The script works fine except that when i press apply btn “which already exist on Smartoffice screen not added by the script” and bound to “ENTER” action, it creates another col every time i press that btn.
How should i avoid replicating the col?
Regrads
Hi,
Have you read and applied the tricks in the other comments?
The blog post is just a simple example to get you started and it is not a complete solution. In you case the init is called after any apply or refresh (F5) or whatever. You need to know exactly when the column should be added. By checking the count or by using the instance cache and store information that you have already added row etc.
Consider that any call to the server might recreate the entire panel. It is crutial that the script is de-tached correctly. Below is an example script – that is not correct – that will show how the requests are made. To play around with it you need to set the LocalScriptPath and run it locally – it is not the same to run it from the script tool.
When you add another column you must test the following: pressing F5, selecting different filters/views, selecting a list in the row and going back, scrolling down with mouse and pressing page down etc.
Good luck and make sure to read anything that you can find in blogs on this topic.
If you have a Smart Office version that has the log viewer widget make sure you have it and that debug is enabled both in user settings and in the settings on the widget. Then check the different requests and each time there is an init there is another copy of you script running and you need to detach events so that only one instance of the script is running.
Hi,
I have a JScript which changes single values in the list at runtime. I don’t use the approach to replace all listrows, because this breaks the scroll action of a user who is using the keyboard to scroll down. Instead, I change the value in the ContentPresenter. This doesn’t influence keyboard scrolling.
But when the user wants to export selected rows to Excel, I need to change the item source at runtime; therefore I added a handler to the “Export to Excel” menu and my routine changes the selected listrows, before the “Export to Excel” dialog appears (There is a special handling if only one or zero rows are selected, indicating that the user may want to export all rows). Of course replacing the listitems removes the selection state of all affected rows; therefore the app collects the selected items before they are changed and after that selects them again.
This works very fine in a lot of programs such as MMS001/002/003, PPS201, DPS170, etc etc.
But in other programs such as MMS060 or MWS060, my programmatic selection is removed in the moment when the “Export to Excel” dialog is displayed, and instead the next listrow below the last listrow of my selection becomes the selected row. Any idea about the reason why this programs react different and how to avoid this ?
Furthermore I noticed that, under the same conditions, all my variables got lost (listRows, listView, column information, instance variables); I had to re-initialize them, instance variables could only be transfered by writing and retrieving them from the instance cache. Any idea ? Again, it works without problems in most programs, only i.e. MMWS060/MWS060 are causing problems.
Shame on me 😦 I made a mistake: I realized, that I had to re-initialize listRows and listView in my handler subroutine in order to catch up a new sorting order, but defined new local variables in the subroutine and then assigned the index on the original instance variable. Using the instance variable in the subroutine too solved the problem.
I want to add a column to a list which is retrieved through API using MIWorker. The problem is now that the “on response” function is running separately from my loop through all rows. How can built in wait so it waits until it gets a response before continuing with the next row?
Hi Dirk,
I assume “on response” is the callback from the MIWorker. You store the index you are in as a varible and create the next MIWorker in the “on response” method. But I would only do it like that if the rows somehow have a dependency on eachother. It will take much longer time to complete the API calls in a serial fashion and having a call per line is generally not recommended. So what is the scenario more exactly? I think the MIRequest has a tag property that you can set to any data and then you can pick up that data in the reply. That way you wouldn’t store a single variable to keep track of the row you would pass it via the request and then that information is in the response as well. At least I think it was designed to allow passing on data like that. Check the API documentation for MIResponse and MIRequest to see if there is a field that you can pass data in so that you can keep track of the state.
Hi Karin, can you indicate where I can find exactly on the intranet API documentation about MIRequest (SDK)?
It is included in the Smart Office SDK download available if you have access to download that product. We have an alternative SDK download location in the Infor’s intranet. I’ll email you the link.
Hi,
I have a requirement. I need to create a list in PPS300/E which display data from MITLOC table on selected item number and warehouse in PPS300.
How i can implement it.
Give any suggestion.
I tried it by javascript but not succeeded as lack of knowledge about JScript, please suggest some examples.
Regards
Farhat Khan
Hi,
I have a requirement wherein I need to load images that comes from IDM. Can we load images onto the new column we just created? We are also looking into the possibility of doing this via mashup. They don’t want it only to show when an item is selected from list. They want every corresponding image to load together with the list. Is this possible?
Yes it is possible, as long as you can query IDM with the data needed to get the image. But I don’t have an example for it. Not sure what you mean via a mashup but it is possible with JScript and you can run a JScript in a list in a Mashup.
HI Karin,
How do you incorporate the image onto the new column? I’m having trouble doing this.
What size do you expect the image to be as I would guess the image will not be viewable as the height is very low. This is probably why the user cases are usually to add a link to the image if there is one or to have a bigger preview area that you load for the selected row only.
It should look like the one below. List would only adjust a little.
Ah, you are increasing the height 🙂
A bit yeah, but if we can make it small then just add a tool tip for the zoomed version it’s also fine 🙂
Hi Karlin,
While I adding a new column only column header is adding. If I run my script again the new column is adding to the Previous heading column. Can I know where I done the mistake