Configurator C# Code entry and use of reflection (is it possible)

We have just installed E10 Saas and are having a number of issues getting to grips with the configurator in terms of inserting C# code into it and also trying to understand the Configurator Object Model - if there is one. As a heads up we use C# and also the Infragistics controls so are well versed in OOP and all its aspects.

Ideally we want to data drive the fields in the configurator from any source where we can sub select certain text boxes, check boxes and combo’s to be displayed on the screen or a tab depending on the product selected off a main tab.

The problem regarding only having unique object names for the controls for the whole configurator i.e the tabs do not act like conventional containers which contain controls so you can have a control with the same name in two containers was our first issue and seems to be completely illogical but we accept that. We produce plastic bags and have something like 50 bag type grouped into around 10 bagtype groups so uur idea was to have a Tab for each group and the main front page to be where the bag required is selected which then immediately shows the dimensions of the group the bagtype belongs in on a tab for operator input.

As I said, ideally we would like to drive this “entry tab” from a table holding field names and x,y positions but if we have to have a tab for each group then this is not the end of the world as long as the operator can only enter data into the bagtype group selected on the main page.

Example code in On Loaded Page Event:

Error Result:

If there is an object model available for the data fields/objects held in the configurator then we can’t find it despite lots of research and there doesn’t seem to be any E10 advanced technical guides, they are all E9.

Additional gripes are the inability to rename an object once it has been placed on the canvas, the inability to copy and paste an object - unless anyone has a solution to this. I ended up writing a form designer outside the configurator which is then imported into the grid list view of objects in the configurator as it is more efficient but really you shouldn’t have to do this in a dev environment.

Also sometimes when entering C# code into the editor you get partial intellisense, but most times you simply lock the application which white screens and results in having to reload.

My only comment here is that I don’t really feel the configurator on cloud is fit for purpose in its current state - again unless someone can prove me wrong.

HELP!

You accurately point out some flaws.

That said I feel like there is probably a better way to achieve what you want. Instead of having tons of disabled inputs, perhaps a couple of context sensitive inputs which have different meaning/uses depending on the initial choices. Hard to be more specific without knowing exactly what you need to achieve.

Evan,
Thanks for your response and yes … they do as you indicate seem to be major flaws in the overall design. It is certainly not fit for purpose in its current form for complicated installations like ours.

The thing is that if I could get access to the underlying objects on the display canvas I could easily re-engineer them so as to make a control multi purpose. Yes I can address each control by name but it would be much easier to instigate C# reflection to address the controls dynamically at run time and thence change their “context contents” to give it a simpler description. Trouble is I don’t know how to address the objects say using a

foreach (Object oObj in )
{}

for all controls on the design area or it would be simple. I understand that the output from the configurator (Phase 1 Sales/Quotation) will have to be put into fixed fields in order to pass on to the manufacturing stage but that is no problem and I appreciate that the handover interface cannot be Dynamic. However having to handle something like 700 unique field names using longhand coding fills me with dread!

We are moving from a 25 year old bespoke system written and supported by myself and my team which is written in a fully OOP environment where subclassing, inheritance, polymorphism etc are taken for granted. Using the current configurator is like going back into the dark ages of Basic where the only method of debugging was to throw out “print statements” when required to track progress.

Thanks for your reply.
Dave

This thread might give you some ideas.

1 Like

Its actually possible to do some of that, but the more you do stuff under the hood the more your configurators break on each update, and then Epicor can’t automatically make web versions for the configurators, etc. In general, Epicor doesn’t support good coding practices for customizations or configurators. They are getting better with server side code with the addition of Epicor Functions, but managing your code, source control, following programming patterns, etc won’t be very feasible. If these are really important to you, try to make most of your code either in Epicor Functions, or in external systems that interact with Epicor via REST.

Some examples of things you can do in the configurators:

Clearing all inputs without addressing them all manually:

try{
Inputs.CmbWireDiameter.Value = "";
if (IgnoreList != null)
{
	var temp = IgnoreList.ToList();
	temp.Add("DecNominalLength");
	temp.Add("DecNominalWidth");
	temp.Add("DecAperture");
	temp.Add("LapSelection");
	temp.Add("ISBLength");
	temp.Add("DecWidth");
	temp.Add("DecCustomerDiscount");
	temp.Add("ChrCustomerID");

	IgnoreList = temp.ToArray();
}
Inputs.MarkSpec.Value = false;
foreach(Control ctrl in Inputs.hook2box.Control.Parent.Controls)
{
	var ControlName = ctrl.Name.ToString();
	if (IgnoreList.Contains(ControlName))
	{
		continue;
	}
	var start = DateTime.UtcNow;
	if (ctrl is Ice.Lib.Framework.EpiTextBox)
	{
		((InputControlValueBound<Ice.Lib.Framework.EpiTextBox,String>)Inputs[ControlName].Value).Value = "";
	}
	else if (ctrl is Ice.Lib.Framework.EpiUltraCombo)
	{
		((InputControlValueBound<Ice.Lib.Framework.EpiUltraCombo,String>)Inputs[ControlName].Value).Value = "";
	}
	else if (ctrl is Ice.Lib.Framework.EpiNumericEditor)
	{		
		if (((InputControlValueBound<Ice.Lib.Framework.EpiNumericEditor,Decimal>)Inputs[ControlName].Value).Value != 0)
		{
			((InputControlValueBound<Ice.Lib.Framework.EpiNumericEditor,Decimal>)Inputs[ControlName].Value).Value = 0m;
		}
	}
}
Inputs.ISBLength.Value = 39m;
Inputs.DecWidth.Value = 39m;
Inputs.DecLToleranceMinus.Value = 0.25m;
Inputs.DecAperture.MinimumDecimal = 0m;
Inputs.DecAperture.Value = 0m;
Inputs.DecAperture.MinimumDecimal = 0.5m;
Inputs.LapSelection.Value = "No Side Laps";
}
catch (Exception e)
{
	MessageBox.Show("There was an error clearing the inputs");
	MessageBox.Show(e.ToString());
}

Running a BAQ and changing the Font and Color for an Input:


try
{

object[] @BAQID = new object[]
	{
		"PianoDiscountV2"
	};
	var OTrans = ((InputControlValueBound<EpiTextBox, string>)Inputs["JobComments"].Value).Control.EpiTransaction;
	var BAQ = Ice.Lib.Framework.AdapterHelper.GetAdapterInstance(OTrans as ILaunch, "DynamicQueryAdapter");
	var ExeParm = ProcessCaller.InvokeAdapterMethod(OTrans, "DynamicQueryAdapter", "GetQueryExecutionParametersByID", @BAQID);
	var ep = ExeParm.GetType().GetProperty("ExecutionParameter").GetValue(ExeParm, null);
	var ClearMethod = ep.GetType().GetMethod("Clear");
	ClearMethod.Invoke(ep, null);
	var AddParamMethod = ep.GetType().GetMethod("AddExecutionParameterRow", new Type[] { typeof(string), typeof(string), typeof(string), typeof(bool), typeof(Guid), typeof(string) });
	object[] @params3 = new object[]
	{
		"Customer",
		Context.CustomerID,
		"nvarchar",
		false,
		Guid.NewGuid(),
		"A"
	};
	AddParamMethod.Invoke(ep, @params3);
	object[] @params4 = new object[]
	{
		"PianoDiscountV2",
		ExeParm
	};
	var ExecuteMethod = BAQ.GetType().GetMethod("ExecuteByID", new Type[] {typeof(string),  ExeParm.GetType()});
	ExecuteMethod.Invoke(BAQ, @params4);
	var results = BAQ.GetType().GetProperty("QueryResults").GetValue(BAQ, null) as System.Data.DataSet;
	var ResultTable = results.Tables["Results"];
	
	if (ResultTable.Rows != null && ResultTable.Rows.Count>0)
	{
		return (Decimal)(ResultTable.Rows[0]["UD05_Number01"]);
	}
	else
	{
		var tmp = Inputs.DecTotalDiscPrice.Control.Appearance;
		tmp.FontData.Name = "Arial";
		tmp.BackColor = Color.FromArgb(255, 255,255,0);
		Inputs.DecTotalDiscPrice.Control.UseAppStyling = false;
		Inputs.DecTotalDiscPrice.Control.Appearance = tmp;
		return 0m;
	}
}
catch (Exception e)
{
	Inputs.ConfigurationFailed.Value = true;
	var ErrMsg = String.Format("There was an unexpected error loading the customer discount. This configuration cannot be saved. Exception: {0}", e.ToString());
	MessageBox.Show(ErrMsg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
	return 0m;
}

Again, these little tricks I am doing are not supported and might break on any update. Plus they make it so our configurators don’t work with Epicor Web Access.

1 Like

mark,
I’ll take a look at the thread. Thanks

Dave

Evan,
You are a lifesaver!!

The line
foreach(Control ctrl in Inputs.epiPcTextBox1.Control.Parent.Controls)

Gives me everything I need to iterate through the controls so now I can plough onwards!

Where did you pick up that gem from? Was it hard work and bloody mindedness or did you read it in a techie posting/document?

Either way it works but I wmust admit I would never have thought of picking a control and then looking at its “Parent.Controls” … simple but not obvious!

Thanks again, I owe you one!

Dave

It was either from using reflection and messagebox’s, ILSpy, or a post somewhere on these forms.

Epicor’s documentation is OK for the first couple of days or weeks, but you quickly reach the point were you either have to get help from someone else or figure things out for yourself.

I guess I’ll have to learn the hard way!

Thanks again and I’ll check out your other “gems” in the posted code! I am sure I will get a lot out of it.

The configurator training material is just useless unless you are of a non programming background. Generating a boat and a sandwich in the examples … and using the infamous expression builder as you can only run it in the Education Environment just made me frustrated and was a loss of about 6 hours of my life I will never get back.

As developers it is next to useless apart from giving you apoplexy about how useless it is as a development environment. Epicor really do need to step up their game as regards to the stability and documentation for real life developers.

Dave

“I think this topic has been fully covered and we can move on from it.” (LOCKED)

3 Likes