Manipulating the Rows in ListViews – Kinda

My previous posting I talked about using web services, specifically I used the example to retrieve the Free Cap Units for an item and yes, I chose that for a good reason.

We use the Free Cap Units to represent the number of cartons we can fit in to a 20′ container. We have a modification which takes the quantity a user entered in to OIS101 and divides it against the Free Cap Units – this gives our staff an idea of how much of a 20′ container product is consuming. We have a percentage per line and a total percentage

My ultimate aim was to use web services to extract the free cap units per item (using a SQL Web Service), create my own calculations, add a total TextBox and add an extra column to the ListView which will contain the percentage. Sounds pretty straight forward? Well five days of fairly solid testing and I still haven’t come to a solution that I liked – which may well be forest for the trees.

I have a solution, but it is a very distinct hack (firmly under the “How Far is Too Far” category) and I’m less than happy with it – I suspect that this is something that would need to be developed with the SDK.

The basic premise is that we need to add a column, and then remove each item in the list, add the new piece of data and then add the item back in to the listview.

My investigation in to this is what todays post is about…

If you’ve played around with WPF and ListViews you’d probably be thinking that Lawson used bindings – but not so – I’m guessing that Lawson have done this so they can better handle situations where an order has a large number of lines (if you remember from a previous post I mentioned that Smart Office grabs rows in groups of around 30 records).

Eitherway, we can prove that they aren’t using bindings through Reflection.

 

 

            // Retrieve the ListControl
            var lvListControl = controller.RenderEngine.ListControl;
            // Retrieve the ListView from the ListControl
            var lvListView = lvListControl.ListView;
            // Retrieve the GridView, the GridView contains the columns
            var gvGrid : GridView = lvListControl.GridView;
            if(null != gvGrid)
            {
            	if(gvGrid.Columns.Count >= 1)
            	{
                        // Here we retrieve the binding for the first column in the Grid
            		var bndBinding : Binding = gvGrid.Columns[0].DisplayMemberBinding;
            		if(null != bndBinding)
            		{
                                // there is a binding, so show the path
            			MessageBox.Show(bndBinding.Path);
            		}
            		else MessageBox.Show("No Binding");	// there was no binding
            	}
            }
            else MessageBox.Show("No Grid");

I dropped this in to the Init() function for OIS101 which yielded the message “No Binding”

So, the next thing that we need to do is look at the more traditional approach of how ListViews work. So we need to take a look at the type of object the items in the list are. To do so I used the following code.

 

            if(lvListView.Items.Count >= 1)
            {
            	MessageBox.Show("lvListView.Items Type = " + lvListView.Items.GetType() + Environment.NewLine + "lvListView.Items[0] Type = " + lvListView.Items[0].GetType());
            	var lviItem = lvListView.ItemContainerGenerator.ContainerFromItem(lvListView.Items[0]);
            	if(null != lviItem)
            	{
            		MessageBox.Show("Content = " + lviItem.Content);
            	}
            	else MessageBox.Show("No ListView Item");
            }
            else MessageBox.Show("No items in listview");

 

This shows us that we are using Mango.UI.Services.Lists.ListRow objects, an class that is derived from Mango.UI.Services.Lists.IListRow. If we then take a look at the ListRow object then we can see some nifty things but frustratingly after some experimentation nothing that obviously allows us to change anything.

Interesting tidbit, I spent a while trying to understand how the framework knew how to extract the data

Apparently there is a “DefaultMemberAttribute”, how nifty 🙂

But as per usual, getting side-tracked. Adding a column to a ListView is really easy and it looks like Lawson have provided us with lots of clues in the functions available in the ListControl object. We have AddListColumn()

And some properties that talk about Columns

Looks pretty straight forward?

	private function addColumn(alcListControl : ListControl, astrColumnName : String)
	{
	    // create a MForms.ListColumn
            var lcListColumn : MForms.ListColumn = new MForms.ListColumn();
            if(null != lcListColumn)
            {
            	// Add the ListColumn to the ListControl
            	alcListControl.AddListColumn(lcListColumn);

            	// here we add our column name to the Names, Columns and Headers
            	alcListControl.ColumnNames.Add("FreeCap");
            	alcListControl.Columns.Add("FreeCap");
            	alcListControl.Headers.Add("FreeCap");
            	// and here we set the text that will appear in the header
            	alcListControl.GridView.Columns[alcListControl.GridView.Columns.Count-1].Header = astrColumnName;
            }

	}

 

Well, yup, it is very easy to add a column, but getting something meaningful displayed in that column is a challenge.

When we use this method and add data we appear to have a link to column 0, a change to column 0 effects the new column that we have added and vice versa.

Other things of note, we have to remove the ListRow and then re-add it to update.

This is what the end result looks like

(Just to confuse the issue, the Free Units is a column added by our MOD – but it was the last column added so it has been overwritten by the word POTATO).

Anyways, getting late, so here is the code. At the moment the addDataToColumnBasic() will overwrite the last column with our data. My plan of attack will probably be to choose a column which doesn’t have much in the way of useful data and overwrite it until I can figure out a clean way to handle this – or maybe put in an enhancement request 🙂

I have left some of the experimentation code in place – addDataToColumn2() is the original function that I used to explore what was happening – largely so you can get an idea that this has been a case of try, try, try again. addDataToColumn() has been left there – my attempt to mimic what Smart Office does based on what functions and properties are exposed though I am clearly missing something. Others may find the code useful and be kind enough to provide a solution 🙂

import System;
import System.Windows;
import System.Windows.Controls;
import System.Windows.Data;
import MForms;
import Mango.UI.Core;
import Mango.UI.Services.Lists;

package MForms.JScript
{
	class DisplayBindings
	{
		var gctrlControl;
		public function Init(element: Object, args: Object, controller : Object, debug : Object)
		{
			var content : Object = controller.RenderEngine.Content;
			gctrlControl = controller;

			  // retrieve the ListControl
            var lvListControl = controller.RenderEngine.ListControl;
            // extract the ListView from the ListControl
            var lvListView = lvListControl.ListView;
            // extract the GridView from the ListControl
            // the GridView actually contains the columns
            var gvGrid : GridView = lvListControl.GridView;
            if(null != gvGrid)
            {
            	if(gvGrid.Columns.Count >= 1)
            	{
            		// here we are looking to see if any of the columns are bound
            		// to specific object properties
            		var bndBinding : Binding = gvGrid.Columns[0].DisplayMemberBinding;
            		if(null != bndBinding)
            		{
            			// this should tell us the path if we have a binding
            			MessageBox.Show(bndBinding.Path);
            		}
            		else MessageBox.Show("No Binding");	// if not binding then we will get a message "No Binding"
            	}
            }
            else MessageBox.Show("No Grid");
            
            // on the assumption that we didn't have a binding, we need to discover what type
            // of object is in the rows of the ListView
            if(lvListView.Items.Count >= 1)
            {
            	MessageBox.Show("lvListView.Items Type = " + lvListView.Items.GetType() + Environment.NewLine + "lvListView.Items[0] Type = " + lvListView.Items[0].GetType());
            	var lviItem = lvListView.ItemContainerGenerator.ContainerFromItem(lvListView.Items[0]);
            	if(null != lviItem)
            	{
            		MessageBox.Show("Content = " + lviItem.Content);
            	}
            	else MessageBox.Show("No ListView Item");
            }
            else MessageBox.Show("No items in listview");
            
            // to experiemtn, uncomment the next two lines and comment out the 3rd
            //addColumn(lvListControl, "Free Cap");
            // addDataToColumn(lvListControl);
            addDataToColumnBasic(lvListControl);
            
            // finally resize the colums to fit the content
            lvListControl.ResizeColumns();
		}

		// here we add the data to an existing column (the best solution I have so far)
		private function addDataToColumnBasic(alcListControl : ListControl)
		{
			try
			{
				// loop through each item in the listview
				for(var i :int = 0; i < alcListControl.ListView.Items.Count; i++)
				{
					// we are always going to work with item 0, it will eventually
					// be removed from the list and then added back to the end
					var lrCurrentListRow : ListRow = alcListControl.ListView.Items[0];

					if(null != lrCurrentListRow)
					{
						// remove the ListRow
						alcListControl.ListView.Items.Remove(lrCurrentListRow);
						// Change the last column contents
						lrCurrentListRow.Items[lrCurrentListRow.Items.length-1] = "POTATO " + i;
						// now we add the ListRow back to the list (effectively updating)
						alcListControl.ListView.Items.Add(lrCurrentListRow);
					}
				}
			}
			catch(e)
			{
				MessageBox.Show("Exception: " + e.message);
			}
		}

		// this one will assume that we are adding a new column (so we need to 
		// uncomment 	addColumn() in the Init() function) and we create
		// a new ListRow and use as many of the functions that we can see
		// in the Object Viewer to be as true as possible to how Smart Office
		// works
		private function addDataToColumn(alcListControl : ListControl)
		{
			try
			{
				// loop through the items in the list
				for(var i :int = 0; i < alcListControl.ListView.Items.Count; i++)
				{
					// always retrieve item 0, we remove items and add them to the end of the
					// list so each loop will bring us a new item to play with
					var lrCurrentListRow : ListRow = alcListControl.ListView.Items[0];
					var newListRow : ListRow;
					if(null != lrCurrentListRow)
					{
						// remove the item from the ListView
						alcListControl.ListView.Items.Remove(lrCurrentListRow);
						
						// create a new ListRow mimicing the one that we just removed
						// the important thing is that we are adding 1 to the column count
						newListRow = new ListRow(lrCurrentListRow.Name, lrCurrentListRow.Items.length + 1, lrCurrentListRow.IsSelected, lrCurrentListRow.IsProtected, false);
						if(null != newListRow)
						{
							// now we need to loop through the items and copy them
							// from the original ListRow to the new ListRow
							for(var j : int = 0; j < lrCurrentListRow.Items.length; j++)
							{
								newListRow.Add(lrCurrentListRow.Items[j]);
							}
							// add our new column
							newListRow.Add("POTATO");
							
							// add our newly created ListRow - it won't have the
							// formatting of the original one, but this is just 
							// a test
							alcListControl.ListView.Items.Add(newListRow);
						}
						
						
					}
				}
			}
			catch(e)
			{
				MessageBox.Show("Exception: " + e.message);
			}
		}

		// add a new column to the ListControl
		private function addColumn(alcListControl : ListControl, astrColumnName : String)
		{
            var lcListColumn : MForms.ListColumn = new MForms.ListColumn();
            if(null != lcListColumn)
            {
            	// do the actual add of the column
            	alcListControl.AddListColumn(lcListColumn);
            	
            	// not sure if they were needed , but filled
            	// them in just incase
            	alcListControl.ColumnNames.Add("FreeCap");
            	alcListControl.Columns.Add("FreeCap");
            	alcListControl.Headers.Add("FreeCap");
            	
				// set the display test of the column
            	alcListControl.GridView.Columns[alcListControl.GridView.Columns.Count-1].Header = astrColumnName;
            	
            }
		}

		// this function isn't commented, it is a whole heap of code
		// that I wrote to explore and test various options, it is 
		// included here purely to illustrate some of the random
		// ideas thrown at this to try and solve the problem, again
		// this stuff assumes that we have called the addColumn() function
		private function addDataToColumn2(alcListControl : ListControl)
		{
			var jCount : int = 0;
			var kCount : int = 0;
			try
			{
				
				for(var i :int = 0; i < alcListControl.ListView.Items.Count; i++)
				{
					var itmItem = alcListControl.ListView.Items[0];
					if(null != itmItem)
					{
						alcListControl.ListView.Items.Remove(itmItem);
						jCount = itmItem.Items.Count;
						if(kCount == 0)
						{
							kCount = itmItem.ColumnCount;
						}
						itmItem.Index = 15;
						//itmItem.Items[alcListControl.ListView.Items.Count-1] = "POT" + i;
						itmItem.GridViewColumns = alcListControl.GridView.Columns;
						MessageBox.Show("Condition Count = " + itmItem.Conditions.Count + " Type = " + itmItem.Conditions.GetType());
						var cnd :  Mango.UI.Services.Lists.IListRow.CellCondition[] = new  Mango.UI.Services.Lists.IListRow.CellCondition[16];
						itmItem.Conditions = cnd;
						//MessageBox.Show(alcListControl.GridView.Columns.Count);
						
						//var tmpListCell : ListCell = new ListCell();
						
						//MessageBox.Show(tmpListCell.GetType());
						
						//var arNewArray : System.Object[] = new System.Object[16];
						
						//for(var j : int = 0; j < itmItem.Items.Count; j++)
						//{
						//	arNewArray[j] = itmItem.Items[j];
							//itmItem.Items[14] = "POT" + i;
							//itmItem.Add("POT" + i);
							
							//MessageBox.Show(itmItem.Items.GetType());
							
						//}
						//MessageBox.Show("arNewArray: " + arNewArray.GetType() + ", itmItem: " + itmItem.Items.GetType());
						//arNewArray[15] = "POT" + i;
						//itmItem.Items = arNewArray;
						//MessageBox.Show(itmItem.Items + Environment.NewLine + itmItem.Items.GetType() + Environment.NewLine + itmItem.Items[0].GetType() + " = " + itmItem.Items[0] + Environment.NewLine + itmItem.Items[0][0].GetType() + " = " + itmItem.Items[0][0] + " *** " + + itmItem.Items[2][2]);
						
						var strTemp : String;
						for(var j : int = 0; j < itmItem.Items.length; j++)
						{
							strTemp = strTemp + j + ": " + itmItem.Items + Environment.NewLine;
							strTemp = strTemp + j + ": " + itmItem.Items[j] + " = " + itmItem.Items[j].GetType() + Environment.NewLine;
							
							var itmSubItem = itmItem.Items[j];
							if(itmSubItem.GetType().IsArray == true)
							{
								for(var k : int = 0; k < itmSubItem.length; k++)
								{
									strTemp = strTemp + " - " + k + ": " + itmSubItem[k] + " = " + itmSubItem[k].GetType() + Environment.NewLine;
								}
							}
							else MessageBox.Show("Not an array");
						}
						MessageBox.Show(strTemp);
						
						var strRawData = itmItem.Items + "," + "POT" + i;
						//MessageBox.Show(strRawData);
//						itmItem.Items = strRawData.Split(",");
//						MessageBox.Show(itmItem.Items);
						
						// itmItem.Items[0][15] = "POT" + i;
						itmItem.Items = strRawData.Split(",");
						//itmItem.Items[0] = strRawData.Split(",");
						itmItem.Add("POT" + i);
						//itmItem.Items[15][15] = "POT" + i;
						alcListControl.ListView.Items.Add(itmItem);
						
						
						//gctrlControl.UpdateListRow(alcListControl.ListView.Items.Count-1);
					}
					
				}
				MessageBox.Show("addDataToColumn count = " + jCount + ", " + kCount);
				// alcListControl.ListView.
			}
			catch(e)
			{
				MessageBox.Show("Exception addDataToColumn: " + e.message + " : " + jCount);
			}
		}
		

	}
}


 

Happy coding!

This entry was posted in Development, How Far is Too Far?, M3 / MoveX. Bookmark the permalink.

6 Responses to Manipulating the Rows in ListViews – Kinda

  1. patrick Weiss says:

    He you are really a crazy guy 🙂

  2. Al says:

    Hey Scott,

    From my recollection of the jscript documentation a couple of years ago there was some documentation on adding a column to a list view. Is this the same approach or a new one?

    Cheers, Al.

    • potatoit says:

      Hi Al,

      funny you should say that. I thought that there was an example aswell. When I looked through the developer guide trying to find it I realised that the script in question adds an image beside the list.

      If I’ve overlooked something obvious, then I would be very interested to know…this isn’t a fantastic method.

      Cheers,
      Scott

  3. aivaras says:

    Hi !
    you have really nice ideas! I have made several scripts, and your examples are really helpfull. Yesterday I started to dig how to add column to the listview. Also spent a day on that, and could not understand how the class works. Then found your article exactly on this… Looks promising. However, there are still some things, I could not find how to correct – when script put additional column to ListView, and your press “Refresh” in a program, it blaims – failed to render panel. It loks, that program tries to save the new column as well. Did you face this ?

    Cheers,
    aivaras

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 )

Facebook photo

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

Connecting to %s