More than one UBAQ in a Dashboard?

Good morning,
I have created a custom Dashboard that contains several updatable BAQs with my own custom BPM actions. The individual UBAQs work well, and the actions all work in the Dashboard as expected.

However, when I try to edit more than one updatable BAQ in this dashboard, I get the error below.
I believe you can reproduce the error by simply making two UBAQs each with a checkbox field. Then put them both into a dashboard. If you click in one UBAQ to check a box, then try to click any other field in any other UBAQ (or BAQ) within that dashboard, you should get the error below.

I have unsuccessfully attempted to squeeze some information out of our EpiCare Support, but they say it is outside of their realm. After three weeks of back and forth they finally gave up and sent me to the internet. So far you all have been very helpful. I am hoping that someone has dealt with this issue before.

It seems like the Dashboard, or the UBAQ is trying to process some kind of update event whenever the focus leaves the BAQ grid. At least that’s what the trace seems to indicate. Can this be disabled?

I can provide any other information you need to help sort out this issue. I have the trace but I am not sure if the information in it should be redacted. Is it safe to send out trace information to the internet from our Pilot server?

Application Error

Exception caught in: Infragistics4.Win.UltraWinDock.v12.2

Error Detail

Message: BAQUpdate
Program: Infragistics4.Win.UltraWinDock.v12.2.dll
Method: FireEvent

Client Stack Trace

at Infragistics.Win.UltraWinDock.UltraDockManager.FireEvent(DockManagerEventIds id, EventArgs e)
at Infragistics.Win.UltraWinDock.UltraDockManager.NotifyPossibleActivePaneChange()
at Infragistics.Win.UltraWinDock.UltraDockManager.ActivatePane(DockableControlPane pane, Boolean force)
at Infragistics.Win.UltraWinDock.DockableControlPane.Activate()
at Infragistics.Win.UltraWinDock.PaneCaptionUIElement.OnMouseDown(MouseEventArgs e, Boolean adjustableArea, UIElement& captureMouseForElement)
at Infragistics.Win.UIElement.OnMouseDown(MouseEventArgs e, Boolean adjustableArea, UIElement& captureMouseForElement)
at Infragistics.Win.UIElement.OnMouseDown(MouseEventArgs e, Boolean adjustableArea, UIElement& captureMouseForElement)
at Infragistics.Win.TextUIElementBase.OnMouseDown(MouseEventArgs e, Boolean adjustableArea, UIElement& captureMouseForElement)
at Infragistics.Win.ControlUIElementBase.ProcessMouseDownHelper(Object sender, MouseEventArgs e)
at Infragistics.Win.ControlUIElementBase.ProcessMouseDown(Object sender, MouseEventArgs e)
at Infragistics.Win.Utilities.ProcessEvent(Control control, ProcessEvent eventToProcess, EventArgs e)
at Infragistics.Win.UltraWinDock.DockControlBase.OnMouseDown(MouseEventArgs e)
at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

I found a reference to Infragistics EventManager:

Is there some way I can use the EventManager to disable the update check when moving between UBAQs within this dashboard? I see there is a property called AllEventsEnabled.

Can I insert some custom code in the dashboard customization?

I have tried a few iterations but I am not sure that I am going in the right direction.
Thanks for your time!
Nate

I’m not sure how helpful it is to say this, but we have given up on updatable dashboards for anything other than the simplest tasks because of problems like you’re getting. uBAQs are fantastic tools. The dashboards aren’t.

After wrestling repeatedly with the black box logic of dashboards, we invested the time putting together some standard code we use to handle the uBAQs directly through the Dynamic Query adapter, and now we use that almost exclusively, because we can build complex BAQs as you’ve done successfully here and use as many as we like in any arbitrary screen knowing how they’re going to behave, with the interaction between them under our own control. I’m sure it’s possible to do most of what we do with more Epicor-approved methods, but there’s very little way of finding out.

That said, I don’t want to be discouraging on this particular case and I hope someone has an answer for the exact problem. I’ll be keeping an eye hoping to learn something.

1 Like

Thanks for the input, Daryl. This is not very encouraging, but I think we can still come up with something. There are a lot of big brains here. If we let it steep long enough, someone will nudge me in the right direction. Until that tea is done brewing, perhaps you could expand a bit on how you utilize the dynamic query adapter in any arbitrary screen? The only way I know to build UI in Epicor is with a dashboard. Can I embed my queries (UBAQs) in a custom form that is not a dashboard?

Thanks for your time!
Nate

1 Like

I cannot reproduce your problem. I have a dashboard that contains 2 UBAQs, one update CustCnt and one update Customer.

I did not allow Multiple Row Update on both of them. I can click on checkbox after one another on each grid. Do you have a dashboard that has the simple UBAQ for me to test?

Presumably you’re familiar with building UI through customisation? As long as you call in the right adapters and contract objects, you can use all the standard customisation tools to build what you like.

In many cases you can piggyback off something that’s already there. If you want something totally new, there’s nothing stopping you creating a dashboard with nothing in it at all, then using the deployed blank dashboard as a canvas.

Dynamic Query is the code behind dashboards in any case. So doing that is hooking into the same systems as you do when creating a dashboard with the standard tools, just things happen in the way you explicitly want. You can access a BAQ via Dynamic Query, create an EpiDataView from it, and bind controls in the normal way. You can also utilise parameters more cleanly, which allows you to be more selective about the data returned in many cases.

1 Like

I guess the only thing that is different is that I set both BAQs to multiple dirty rows. I am not sure how or if I can attach a dashboard and the uBAQs.

The idea is that I have two essential uBAQs I need to review in the same dashboard. The first one lists releases that are open in our system but do not match to releases the customer wants open. Call this ourUnmatchedRels. The second BAQ lists releases that our customer wants to have open that don’t match releases in our system. Call this xUnmatchedRels. In each BAQ there are two options for how to process the row. (To be a match, Part, PO, Rev, Date, and Quantity must match.)

ourUnmatchedRels
Use xDate = Change date of our release with a matching quantity to an open date from customer
Close Release = Close this release in our system because it doesn’t match any releases in the customer file, and we don’t need to update it.

xUnmatchedRels
NewRelease = Create a new release in our system with this information
UpdateRelease = Update an existing unmatched release in our system to a new quantity with the same date

As we go down through one list, the information will be used to decide which checkbox to tick in the other list. For example here are the rows for a single part in both queries.

You can see that on the left side, there are 4 of our open releases that need to be dealt with. The query calculated that they should be closed. On the right side, there are 4 releases that the customer wants open. Essentially the customer just changed the quantity on 4 releases. Looking at the left side I can see that I do not want to close those releases, instead I will click ‘UpdateRelease’ for each of the releases on the right side. This will change the release quantity to match the customer quantity.

After I click those boxes and run my custom actions, the releases will be updated, and then I can refresh my queries which should result in the four release in question being removed so that I can continue working on the other parts.

I hope this helps!
Thanks!
Nate

This sounds like a great option. I will start working with this now. I have built some customization, but not enough to do what you are saying. Essentially I need a blank canvas, with two UBAQs side by side. (see image in my last reply).

I think I would accomplish this using the UltraGrid object. Right?

You can access a BAQ via Dynamic Query, create an EpiDataView from it, and bind controls in the normal way.

Can you expand on this a bit, or show me to an example? Thanks!
Nate

Here’s where I’ve been through a very similar discussion before …

You may find the whole thread interesting, I’m guessing.

1 Like

This looks great. I am working through it now. I have created a dashboard with only a tracker view, then removed everything from it and deployed it. Now I am in customization. I created two ultrgrids. How do I get each to show the unfiltered results from a specific uBAQ.

I am not sure where that code you referenced should be inserted. In the InitializeCustomCode?
Once I do, what sets the results of the uBAQ to the ultragrid?

I have been searching on this for a while, but coming up short. There are lots of detailed examples, but I just need one simple example to put everything into perspective for me.

Multiple dirty rows between UBAQ’s and dashboards are a little counter intuitive. In a BAQ, the multiple rows lets you make changes and update once. And because you have to hit the update button, it’s more convenient. In a dashboard, if you don’t allow multiple dirty rows, the save happens as soon as you change rows, automatically. So making the BAQ not allow dirty rows, actually makes the dashboard a lot easier to use. The grid won’t actually change until you hit refresh (the the things that you are not updating, or other grids affected by the data). So I pretty much never use allow multiple dirty rows in a dashboard because there are problems like you are experiencing.

2 Likes

We package up the bits of the code we use repeatedly and put it in a class outside “Script” in the customisation to save it getting complicated when you have more than one BAQ, in which case the set-up of it all is called from InitializeCustomCode. But you can call it later than that if it happens to suit your needs better.

If you do set everything up within InitializeCustomCode and make sure your EpiDataViews are created at that stage, then the next time you re-open your dashboard and go into the customization you’ll find those EpiDataViews in the standard list of data objects, in which case you can bind them to the UltraGrids in Properties exactly as usual. You then need a way of making one react to the other, I’m assuming. We do that in a non-standard way too, so I’m not sure how far to take you down that road.

I don’t think I have anything I’d call “simple” to share, because our standard code has now turned into one of those swiss army knives that’s several inches wide. But if you get stuck I can send something through.

Interesting. I never noticed that before.
Now that I am looking. I can see that there is a field in the BAQ called “Allow Multiple Row Update”.
That is currently checked in both of my uBAQs. When I look in the dashboard grid view under Updatability, the checkbox “Multiple Dirty Rows” is checked and grayed out.

I am still working on creating the dashboard in my own customization rather than out of the box grid views.

Thanks Daryl! This has been quite helpful. I will head down this road a bit. Thankfully, I only need to return the unfiltered results of each BAQ every time I refresh the page. There is no communication between the BAQs other than the primate pounding the keys. :wink:

1 Like

So here is my super simple approach to get my syntax down. Unfortunately I get some errors.

// **************************************************
// Custom code for MainController
// Created: 7/8/2019 9:38:17 AM
// **************************************************
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Ice.BO;
using Ice.UI;
using Ice.Lib;
using Ice.Adapters;
using Ice.Lib.Customization;
using Ice.Lib.ExtendedProps;
using Ice.Lib.Framework;
using Ice.Lib.Searches;
using Ice.UI.FormFunctions;
using Infragistics.Win.UltraWinGrid; 

public class LoadMyGrids
{
DynamicQueryAdapter queryAdapter = new DynamicQueryAdapter(oTrans);
queryAdapter.BOConnect();
queryAdapter.Execute("myUBAQName", queryExecutionData);
MyUltraGridName.DataSource = queryAdapter.QueryResults.Tables["Results"];
}
public class Script
{
	// ** Wizard Insert Location - Do Not Remove 'Begin/End Wizard Added Module Level Variables' Comments! **
	// Begin Wizard Added Module Level Variables **

	// End Wizard Added Module Level Variables **

	// Add Custom Module Level Variables Here **

	public void InitializeCustomCode()
	{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
		// Begin Wizard Added Variable Initialization

		// End Wizard Added Variable Initialization

		// Begin Wizard Added Custom Method Calls

		// End Wizard Added Custom Method Calls
		LoadMyGrids;
	}

	public void DestroyCustomCode()
	{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
		// Begin Wizard Added Object Disposal

		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
	}
}

Producing errors:

Compiling Custom Code …

----------errors and warnings------------

Error: CS1519 - line 24 (24) - Invalid token ‘(’ in class, struct, or interface member declaration
Error: CS1519 - line 25 (25) - Invalid token ‘(’ in class, struct, or interface member declaration
Error: CS1519 - line 25 (25) - Invalid token ‘)’ in class, struct, or interface member declaration
Error: CS1519 - line 26 (26) - Invalid token ‘=’ in class, struct, or interface member declaration
Error: CS1519 - line 26 (26) - Invalid token ‘[’ in class, struct, or interface member declaration
Error: CS1001 - line 26 (26) - Identifier expected
Error: CS1519 - line 26 (26) - Invalid token ‘“Results”’ in class, struct, or interface member declaration

** Compile Failed. **

I have added the Ice.Contracts.BO.DynamicQuery.dll assembly.

If you are going to pull the code out into a class within the customisation then you need to get a bit formal about it. Your class needs to have a constructor and methods of its own and your code within “Script” instantiates an object from that class and calls the methods. It’s a bit different from working in a BPM where no such approach is really possible, but in many ways it’s closer to what anyone who’s done any OO code training is familiar with.

As I say, our standard code has grown to the point where you’d have to wade through the irrelevancies, but if you want I can send it to you with the instructions my team uses and you could pick out the bits that help.

1 Like

I think that would be helpful. I am more familiar with OOP than this weird Epicor grab-bag of coding. :stuck_out_tongue:
Thank you!

1 Like

It is an odd system to work in unless (I presume) it’s the only thing you’ve ever known. I, for one, find it a constant struggle to know when to go all-in on the niche ways of doing things, when to revert to something more familiar but less approved … and, most of all, when to do nothing at all and tell users “that’s just the way Epicor works”.

But uBAQs and Dynamic Query have been very solid and flexible for us, including through upgrades, so I’m less hesitant mentioning them as something to lean on for solutions.

1 Like

This was really helpful. I pulled out some code from the private message you sent.
I have created a blank dashboard with an empty tracker view.
Inside this I added a customization with two UltraGrids.
I included your entire “Region Classes”, and implemented them in the following way.

public class Script
{
	// ** Wizard Insert Location - Do Not Remove 'Begin/End Wizard Added Module Level Variables' Comments! **
	// Begin Wizard Added Module Level Variables **

	// End Wizard Added Module Level Variables **

	// Add Custom Module Level Variables Here **
	private Dictionary<string,DynData> baqs;
	
	private DynData ddxOpenOrders;
	private DynData ddmyOpenOrders;

private void InitialiseCode()
	{
	baqs = new Dictionary<string,DynData>();
		ddxOpenOrders = new DynData("x_ThisWeeksUpdate_OpenOrders", oTrans);
		ddxOpenOrders.Initialise(
		new string[] {}, //BAQ parameter names, or this line null if no parameters
		new string[] {}, //BAQ parameter defaults, one for each parameter in the same order, or null if none
		new string[] {"Calculated_SortKey"}, //as many field identifiers as needed for a unique row reference
		new string[] {"Date", "xQty"}, //full list of the BAQ field CAPTIONS that need to be visible in a grid
		new int[] {100,100}, //column width per visible field, one for each in the above string array
		new string[] {}, //list of BAQ field captions that need special formating, or null if none
		new string[] {}, //matching list of format strings to the above
		epiUltraGridC1); //the name of the EpiUltraGrid you want the data to appear in
	baqs["x_ThisWeeksUpdate_OpenOrders"] = ddxOpenOrders;
	//ddxOpenOrders.GetData();

	baqs = new Dictionary<string,DynData>();
		ddmyOpenOrders = new DynData("x_ThisWeekUpdate_OurUnmatched", oTrans);
		ddmyOpenOrders.Initialise(
			new string[] {}, //BAQ parameter names, or this line null if no parameters
			new string[] {}, //BAQ parameter defaults, one for each parameter in the same order, or null if none
			new string[] {"Calculated_OrderLnRel"}, //as many field identifiers as needed for a unique row reference
			new string[] {"OrderLnRel", "OurDate"}, //full list of the BAQ field CAPTIONS that need to be visible in a grid
			new int[] {100,100}, //column width per visible field, one for each in the above string array
			new string[] {}, //list of BAQ field captions that need special formating, or null if none
			new string[] {}, //matching list of format strings to the above
			epiUltraGridC2); //the name of the EpiUltraGrid you want the data to appear in
		baqs["x_ThisWeekUpdate_OurUnmatched"] = ddmyOpenOrders;
		//ddmyOpenOrders.GetData();
	}
	
private void DestroyCode()
	{
		foreach (DynData dd in baqs.Values)
		{
			dd.CloseDown();
		}
	}
private void baseToolbarsManager_ToolClick(object sender, ToolClickEventArgs args)
	{
		switch (args.Tool.Key)
		{
			case "SaveTool":
				foreach (DynData dd in baqs.Values)
                          {
                              dd.Save();
                          }
				break;
			case "RefreshTool":
				foreach (DynData dd in baqs.Values)
                          {
                              dd.GetData();
                          }

				break;
		}
	}
	
public void InitializeCustomCode()
	{
	InitialiseCode();

		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
		// Begin Wizard Added Variable Initialization

		// End Wizard Added Variable Initialization

		// Begin Wizard Added Custom Method Calls

		// End Wizard Added Custom Method Calls
	this.baseToolbarsManager.ToolClick += new ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	}

	public void DestroyCustomCode()
	{
	DestroyCode();

		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
		// Begin Wizard Added Object Disposal

		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
	this.baseToolbarsManager.ToolClick -= new ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	}


	private void MainController_Load(object sender, EventArgs args)
	{
		// Add Event Handler Code


	}
}

I also manually linked the EpiBindings for the two grids to the correct underlying queries. You are right, I could only do this after I had entered the code, saved, and reopened the dashboard. I found that I didn’t need to explicitly list all the fields from each BAQ I wanted in the code. Once the EpiBindings were linked correctly. I just set the displayed fields using the columns property for each UltraGrid.

Now both queries work well. They return all the data I want, and most importantly they do not give an error when switching between grids. But I don’t have my custom actions automatically included in the Actions menu like they are when I created a dashboard with multiple BAQ grids.

I think I am still missing one big step now. How do I utilize my custom actions in each BAQ? The actions rely on the checkboxes to determine how to process the record, so I think the checkboxes are the only things actually being updated by the BAQ Update.

Thank you so much for being so patient and helpful!
Nate

OK, first things first - if you’re sticking with EpiDataView binding then you don’t need a lot of the code I sent you and you’re better off stripping it out to save confusion. That includes the sort key, and all the arrays that govern which columns show in the grid and their formatting. Those are all in there to enable things you won’t be using, and setting the EpiBinding will override them.

Custom Actions can be accessed via the Dynamic Query adapter method RunCustomAction. You can see the signature of that by looking at Object Explorer when in customisation mode. We don’t tend to use Custom Action much, but my approach would be to add a new method into the class so that you only need to call it with the actionid and can leave the dataset etc within the object.

It’s overall easiest to trigger anything like that with a button on the screen, because you have wizards to create the event handling code for you then. You can manually add items to the menu if you like, but that’s slightly more involved and a separate chunk of code.