Iterating Through Panels (Sheets) To Find Specifically Named Control

@dhewi / @knash :

Odd. It seems to recognize:

foreach (Control ctrl in frmNonConf.Controls)

… but not:

foreach (Control control in frmNonConf.Controls )


Unless I’m clinically drunk, the syntax appears to be the same.

(Working on the rest of the code now…)

The second param in the ForEach is the temp variable name, no?

is “control” a reserved word, while “ctrl” isn’t?

@ckrusen :

Correct. That would be the variable which contains the control “object” in the list of possible controls on the given form.

I’d say you’re on to something there. It very well may be a reserved word/phrase, which would explain the compiling errors when that specific variable name was used.

Well… I do have some success iterating through all of the controls, one level at a time, to ultimately find the ones associated with “Request Move” on each panel/sheet/tab. From there, it was just a matter of making those controls “not visible”. In order to do this, I had to drill down through a total of eight (8) levels of control “layers” in order to isolate exactly what I needed.

I’m going to work up a little “How To” that covers exactly what I did, then I’ll post that information here for anyone who might be interested. That documentation will also help me remember what the heck I did.

I’m sure there’s a simpler way, but based on all the information suggested here (thanks to all who participated in the discussion to help me out), this appeared to be the most logical way to approach my issue.

More to follow…


@ckrusen – After further being told by my supervisor that this sort of customization might need to be applied to other form modules, I’m going to stick with the custom code method instead of manually editing the control properties across each control object (which is still a viable solution).

1 Like

Count me in as VERY interested.

Does your code need to know the complete structure a head of time? Or can it dynamically find objects that have sub-objects, that will need drilling through?

If anyone is familiar with jQuery in web programming, you know how versatile the selector system is. You could have found all the objects in question with one line of code. And actually set the visibility property in that same line.

$("#chkRequestMove").hide();

I ended up using a variant of @knash’s solution, whereby, you’d need to reference the specific names of each control through each level of iteration, one at a time. Admittedly, this seems to be a pain in the seat cushion, but these types of control names are specific to each form module, so you’d need to uncover their identities in order to work with them eventually.

It does find the objects it needs “dynamically”, but only one level at a time. You’ll see why when I post the documentation. A quick hint is that, when I tried to iterate through controls on the “form level”, I assumed that a simple “foreach (Control)” statement would find all control objects embedded in the form. Oh… but no… we can’t have that simplicity, can we? That statement merely listed the “docking area” controls - nothing else. I then had to iterate through the controls on the “docking area” to uncover “dockable windows” only… and so on and so on.

As long as you reference the control names you need to drill down, it works rather well. I just wish that I knew how to indicate, “find controls of type [checkbox]” only - and - if that type of control does not exist at the form level, drill down until you find them. I believe that would be the dynamic nature you’re looking for (I’d prefer that approach, as well).

Nonetheless, as long as you isolate the names of each control you need to iterate through at each level, it appears to be rather fool-proof (see @knash’s example which shows how he needed to be very specific with the control names that he was iterating through).

I’m working on the documentation now. Hopefully, I won’t be typically long-winded while typing everything up.

More to follow…

2 Likes

If it helps, the code snippet I posted does both things you’re wishing for: it dives as deep as you need, all by itself, and finds controls of a given type (EpiButton in this snippet). The key is that it’s recursive.

As far as finding which ones you want, one approach that has worked for me is comparing the EpiBinding property - if it’s linked to the field you want then take the action you want. That way you don’t need to know what the names of the controls are either.

1 Like

Might sound like a crazy idea, but what about taking it the next level and having a function that looks up user codes, you can then apply that to all your custom forms loads. Then all you need to know is the information to update the user code with what ever object name and property/value and set the inactive flag in the usercode if you want.

Not sure how well that would work but it’s an idea.

Greetings All.

This is how I approached the issue (and I apologize ahead of time for the length of this post, but I tend to like details because I walk away with a better understanding - so I prefer to explain things with that in mind).

Initially, I merely wanted to see what controls were available on the “NonConformance” form and I had assumed that a simple call to the “Controls” collection of the form object would show me ALL of the controls. That did not seem to be the case (at least in the manner I started coding the solution):

The results did not line up well with my expectations, as I had assumed I would see something related to the “Request Move” checkbox… and certainly more than 12 total hits.

My only recognition of “Sonoma” is that its a well-known county in California… or a race track also located in California… or that it’s a clothing line that typically distributes to JCPenney, so I tried to stick with results that seemed to be logical:

  • windowDockingArea2
  • windowDockingArea1

Ok. So, I decided to look for possible controls contained within those specific controls:

Gee… 1 control listed for “windowDockingArea2”. Again, not what I had expected.

I decided to go a bit deeper, taking a lead from Ken Nash’s example above:

… and deeper:

… and deeper:

Ok. Well, here, I started to get a clue that the nine (9) hits might correspond with the nine (9) tab panels contained within the “NonConformance” form module. But, I needed to validate that, so I went a bit further:

Great. Now I’m getting the actual names for the panel controls. Yet, I still need to work within the panel object to iterate through the controls on that level:

Terrific. Now I’m down to the group controls level. At this point, I made the assumption (based on the object tree on the form customization view) that the checkbox control would reside within the just-discovered group controls, but I noticed that Epicor did not maintain standard nomenclature for the groups. So, I merely looked at each group name and created a conditional statement against those names. Before adding that code, I knew that I did not have to continue looking for specific control names once my existing code arrived at the “dockableWindow” control level, because this level represents the tab panels (all I need to do is hunt through those panel controls). So, I went back to modify the code a bit, as explained here:

I replaced that code with the conditional statement that identifies only the “Material Reporting” group and included the logic necessary to isolate and modify the properties of the “Request Move” checkbox:

09


In any event, this works.

Efficient code? Nope - never said it was. But based on my level of skill and knowledge, this is how I approached it.

I will also concede that there are support issues with this approach, especially if Epicor decides to change their naming conventions for controls contained within the “NonConformance” form module. That’s a large drawback in this solution that you’d need to be aware of – because that would force you to edit the code with the updated nomenclature.

I was hoping to implement something like what Daryl Hewison had illustrated, but I didn’t see how iteration below the form level (where my code found only 12 controls in the collection) would have gone deeper.

If you decide to use this sort of approach, just be aware that there is vulnerability should you upgrade to a different version of Epicor. But - if you’re documenting your customizations well, then you’ll be aware of the need to review this type of customization against any possible nomenclature changes initiated by Epicor in future versions.

[By the way, we’re limping along here on v10.1.400.14]

4 Likes

After posting the previous novel concerning the solution I worked on, I went back to take a better look at Daryl Hewison’s suggestion and it appears that I missed a pretty obvious line in his code that enables further iteration through child controls contained within the parent:

else if (c.HasChildren)

That’s pretty important - because it completely negates all of the nonsense I posted above, but it also helps me understand how Daryl was automating the process of iterating through to the next level of controls without knowing anything about the controls themselves.

Using Daryl’s example, I came up with this code block, which eliminates the need to know the names of any of your form module controls, especially level by level –

Again, on form load, I declare/define the form by GUID:

Erp.UI.App.NonConfEntry.NonConfForm frmNonConf;
frmNonConf = (Erp.UI.App.NonConfEntry.NonConfForm)csm.GetNativeControlReference("<PLACE YOUR GUID HERE>");

I then define the control name parameter that I want to search on (in my case, the checkbox control and its label control end with the phrase “RequestMove”):

string strCtrlName = "RequestMove";

I then make the call to the method that performs the work by passing in the form object (as the parent control) and the control search string:

modifyCheckbox(frmNonConf, strCtrlName);

The method looks like this:

private void modifyCheckbox(Control parentControl, string strControlName)
{
	foreach (Control ctrl in parentControl.Controls)
	{
		if (ctrl.Name.EndsWith(strControlName))
		{
			ctrl.Visible = false;
		}
		else if (ctrl.HasChildren)
		{
			modifyCheckbox(ctrl, strControlName);
		}
	}
}

Tested and working, simply by editing the “Visible” property value in code and nothing else:


Thanks, Daryl… now I understand it!

1 Like

Couldn’t you just (easier) set the Property of the EpiDataView to ReadOnly? Then you don’t have to worry about what control it is?

Nice deep dive though, in all seriousness :slight_smile:

2 Likes

Jose:

Just to help me with a foundation… is the advantage there that it would completely remove processing on the “RequestMove” field within the data view (and I’m guessing make the control object for that field inaccessible on the form due to underlying business logic), as compared to the approach of removing the control altogether?

@Aaron_Moreng

Sure… kick a guy when he’s down. :woozy_face:
Interestingly enough, you’ve selected a fine depiction of how I went about this whole thing (imagine being stuck in the same office with me).

1 Like

Right you wouldn’t ahve to go looking for the controls rather control the data it is bound to and set the rule there.

@josecgomez:

I have been assigned a similar requirement for a checkbox control on the Order Entry form, as well. So, I’ll approach this new task by manipulating the data view and get a feel for how that works in comparison. It certainly seems a lot simpler than waving a magic wand over the form controls.

Thanks for that suggestion, José. Much appreciated.

@josecgomez.sixs, @BA-Quest

How does using the EpiDataView property method translate to the users experience?

Does setting the EpiDataView to R/O affect the appearance of the control on the form?

If the control appears unchanged (still changeable), what happens when a user changes it then tries to save? Any kind of warning or exception? Or does the update of the record proceed, but with that one field unchanged?

EpiDataViews are EpiBound to the fields. Setting properties, like readonly affects the bound controls as well

1 Like

The control should not be changeable of the data view is read-only

-Jose

1 Like

Thanks for the deep dive, I found it quite refreshing to see the process documented for a change!

@josecgomez is right that setting a data property is easier, more efficient and more maintainable, and I think you’ll fine the relevant controls “grey out” so users won’t be confused. But it’s nice to know all the alternatives, and there are definitely times when there’s good reason not to let users know some options or controls even exist …

1 Like