Cascading BAQCombos on a Dashboard

I have created a dashboard that has project ID as part of its criteria. If I just use that out of the box as a filter I see the project description instead of seeing the project ID which is desired. Instead of using the default project combo, I created a BAQ combo that points to a BAQ that only returns the project ID. That works fine. I then want to have a second combo that returns project phase data based on the selected project ID. I created another BAQ combo to fill it in, but I can’t seem to get the filter right. I have read where people use EpiBinding:BO.field to get the filter based on another field on a form. However, I am working with a dashboard that doesn’t have EpiBinding data. Someone made a comment you could do something like YourForm:field.Text but I don’t really know what the name of the dashboard would be to fill that in properly. What do I need to do to get the second BAQCombo to filter off of the selection in the first?

Not sure from your description but…
I wonder if you can do them with parameters in the BAQ instead of setting up your filtering at the dashboard?

Where I might use trackers in a dashboard for simpler filters as they are fast/easy.
When I get to more complicated filtering, I start looking at BAQ parameters instead, which (usually) offer more/better control.

1 Like

I am trying to get the second combo to be based on the first. When I choose a project, I want the WBS Phase IDs for the project to be filled into the combo. I also have the first combo tied to a query since I want the project ID not the description. The first combo as a BAQ combo works fine, but I don’t know how to tie the second BAQ Combo to the user selection in the first.

Did you ever figure out how to accomplish this? I am currently trying to do the exact same thing with Resource Groups and Resources

I have not accomplished this. It got put on the back burner but I do need to revisit it.

Anyone have an idea?

I have a few custom forms where I use cascading combos. I had to do it kind of manually. I didn’t use EpiBindings.
In the customization for your dashboard, add a combo boxe. In my form I am starting with a PartNum text box, and a revision number combobox (technically an EpiUltraCombo). After the user enters a value in part number, this event fires off:

	private void MyPartNum_Leave(object sender, System.EventArgs args)
	{
		if (MyPartNum.Text!="")  
		{
		getRevs();
		cmbRevs.ForceRefreshList();
		}
	}

This does three things. First it checks to make sure the user entered a value in the box. If they did, it runs the GetRevs function, we will get to that shortly. Then it calls ForceRefreshList to make sure the next combo gets updated.
The GetRevs function looks like this:

private void getRevs()
	{
		cmbRevs = (Ice.Lib.Framework.EpiUltraCombo)csm.GetNativeControlReference("a03dd350-74a0-4382-b1cb-251c5e41ed9a");

		DynamicQueryAdapter dqa = new DynamicQueryAdapter(oTrans);
		dqa.BOConnect();		
		QueryExecutionDataSet qeds = dqa.GetQueryExecutionParametersByID("getPartRev");
		qeds.ExecutionParameter.Clear();

		qeds.ExecutionParameter.AddExecutionParameterRow("part", MyPartNum.Text, "nvarchar", false, Guid.NewGuid(), "A");

		dqa.ExecuteByID("getPartRev", qeds);
		if (dqa.QueryResults.Tables["Results"].Rows.Count > 0)
		{
			cmbRevs.DataSource = dqa.QueryResults.Tables["Results"];
			cmbRevs.DisplayMember = "PartRev_RevShortDesc";
			cmbRevs.ValueMember = "PartRev_RevShortDesc";
			cmbRevs.DropDownStyle = Infragistics.Win.UltraWinGrid.UltraComboStyle.DropDownList;
			oTrans.NotifyAll();
		}
		else
		{
		cmbRevs.DataSource = "";
		cmbRevs.DisplayMember = "";
		cmbRevs.ValueMember = "";
		oTrans.NotifyAll();
		MessageBox.Show("Part Not Found in Any Open Jobs!");
		}
	}

This needs a BAQ (getPartRevs) to pull the list of eligible values, and it dynamically assigns a parameter to help filter the results.

To make use of this you will certainly need to include the Ice.Contracts.BO.DynamicQuery in the Custom Assembly Reference Manager.

Here is the complete custom code for the form where I use cascading combos. This form collects data in a UD table by calling a UD method in my UBAQ. You probably don’t need to worry about that stuff if you are just interested in the cascading combos.

// **************************************************
// Custom code for MainController Open Job Inventory (Shipping Excel File)
// Created: 1/14/2021 10:45:51 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 System.Reflection;
using Infragistics.Shared;
using Infragistics.Win;
using Infragistics.Win.UltraWinGrid;
using System.Diagnostics;
using Ice.Core;
using Erp.Adapters;

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 EpiUltraCombo cmbBins;
	public EpiUltraCombo cmbRevs;
	public EpiUltraCombo cmbJobs;
	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

		this.epiButtonDelRow.Click += new System.EventHandler(this.epiButtonDelRow_Click);
		this.epiButtonAddRow.Click += new System.EventHandler(this.epiButtonAddRow_Click);

		this.MyPartNum.Leave += new System.EventHandler(this.MyPartNum_Leave);
		this.epiRevs.Leave += new System.EventHandler(this.epiRevs_Leave);
		this.baqComboC1.Leave += new System.EventHandler(this.baqComboC1_Leave);
		this.epiButtonC1.Click += new System.EventHandler(this.epiButtonC1_Click);
		this.epiJobs.Leave += new System.EventHandler(this.epiJobs_Leave);
		// End Wizard Added Custom Method Calls
	}

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

		this.epiButtonDelRow.Click -= new System.EventHandler(this.epiButtonDelRow_Click);
		this.epiButtonAddRow.Click -= new System.EventHandler(this.epiButtonAddRow_Click);

		this.MyPartNum.Leave -= new System.EventHandler(this.MyPartNum_Leave);
		this.epiRevs.Leave -= new System.EventHandler(this.epiRevs_Leave);
		this.baqComboC1.Leave -= new System.EventHandler(this.baqComboC1_Leave);
		this.epiButtonC1.Click -= new System.EventHandler(this.epiButtonC1_Click);
		this.epiJobs.Leave -= new System.EventHandler(this.epiJobs_Leave);
		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
	}

	private void BAQRunCustomAction(EpiDataView iEdv, string iActionID)
	{
	    BAQDataView BAQView = (BAQDataView)iEdv;
	    Assembly assembly = Assembly.LoadFrom("Ice.Lib.EpiClientLib.dll");
	    Type t = assembly.GetType("Ice.Lib.Framework.BAQUpdater");
	    BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
	    MethodInfo mi = t.GetMethod("BAQRunCustomAction", bf);
	    object[] param = new object[] { BAQView, iActionID};
	    mi.Invoke("Ice.Lib.Framework.BAQUpdater", param);
	}



	private void epiButtonDelRow_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		// Run custom code in BPM
		string message = "These parts must be added to the inventory through Inventory Transfer before deleting them here. Are you sure you want to delete the selected records?";
        string caption = "Are you sure?";
        MessageBoxButtons buttons = MessageBoxButtons.YesNo;
        DialogResult result;

        // Displays the MessageBox.

        result = MessageBox.Show(message, caption, buttons);

        if(result == DialogResult.Yes)
        {
		var edvV = oTrans.Factory("V_UD_OpenJobInventory_1View");
		BAQRunCustomAction(edvV, "DeleteRows");
 	   RefreshPage();
		}
	}
	private void RefreshPage()
	{
		MainController.AppControlPanel.HandleToolClick("RefreshTool", new 
		Infragistics.Win.UltraWinToolbars.ToolClickEventArgs(MainController.MainToolManager.Tools["RefreshTool"], null));
		MyPartNum.Text="";
		epiRevs.Value="";
		MyQuantity.Text="";
		epiStatus.Value="";
		epiUltraComboC1.Value="";
		baqComboC1.Value="";
		epiJobs.Value="";
		MyNotes.Text="";
		MyDate.Text="";
	}


	private void epiButtonAddRow_Click(object sender, System.EventArgs args)
	{
		//Verify text values before adding record
		if (MyPartNum.Text == "")
		{
		MessageBox.Show("Part Number Missing!");
		return;
		}
		if (epiJobs.Value.ToString() == "")
		{
		MessageBox.Show("Job Lot Number Missing!");
		return;
		}
		if (epiRevs.Value.ToString() == "")
		{
		MessageBox.Show("Revision Number Missing!");
		return;
		}
		if (baqComboC1.Value.ToString() == "")
		{
		MessageBox.Show("Warehouse Missing!");
		return;
		}
		if (epiUltraComboC1.Value.ToString() == "")
		{
		MessageBox.Show("Bin Number Missing!");
		return;
		}
		if (epiStatus.Value.ToString() == "")
		{
		MessageBox.Show("Status Missing!");
		return;
		}
		if (MyQuantity.Text == "")
		{
		MessageBox.Show("Quantity Number Missing!");
		return;
		}
		if (MyDate.Text == "")
		{
		MessageBox.Show("Date Missing!");
		return;
		}
			
		EpiDataView edvCallContextBpmData = ((EpiDataView)(this.oTrans.EpiDataViews["CallContextBpmData"]));
		System.Data.DataRow edvCallContextBpmDataRow = edvCallContextBpmData.CurrentDataRow;
		edvCallContextBpmDataRow["Character01"] = MyPartNum.Text;
		edvCallContextBpmDataRow["Character03"] = epiRevs.Value.ToString();
		edvCallContextBpmDataRow["Character07"] = epiStatus.Value.ToString();
		edvCallContextBpmDataRow["Character02"] = epiJobs.Value.ToString();
		edvCallContextBpmDataRow["Character05"] = baqComboC1.Value.ToString(); //warehouse
		edvCallContextBpmDataRow["Character04"] = epiUltraComboC1.Value.ToString(); //bin
		edvCallContextBpmDataRow["Character06"] = MyNotes.Text;
		edvCallContextBpmDataRow["Number01"] = MyQuantity.Text;		
		edvCallContextBpmDataRow["Date01"] = MyDate.Text;
		
		var edvV = oTrans.Factory("V_UD_OpenJobInventory_1View");
		BAQRunCustomAction(edvV, "AddRow");

 	   RefreshPage();
		
	}


	private void getJobs()
	{
		cmbJobs = (Ice.Lib.Framework.EpiUltraCombo)csm.GetNativeControlReference("ab3e6a13-bcc2-45bb-8f27-e93f11951026");

		DynamicQueryAdapter dqa = new DynamicQueryAdapter(oTrans);
		dqa.BOConnect();		
		QueryExecutionDataSet qeds = dqa.GetQueryExecutionParametersByID("Jobs");
		qeds.ExecutionParameter.Clear();

		qeds.ExecutionParameter.AddExecutionParameterRow("part", MyPartNum.Text, "nvarchar", false, Guid.NewGuid(), "A");
		qeds.ExecutionParameter.AddExecutionParameterRow("rev", epiRevs.Value.ToString(), "nvarchar", false, Guid.NewGuid(), "A");

		dqa.ExecuteByID("Jobs", qeds);
		if (dqa.QueryResults.Tables["Results"].Rows.Count > 0)
		{
			cmbJobs.DataSource = dqa.QueryResults.Tables["Results"];
			cmbJobs.DisplayMember = "JobHead_JobNum";
			cmbJobs.ValueMember = "JobHead_JobNum";
			cmbJobs.DropDownStyle = Infragistics.Win.UltraWinGrid.UltraComboStyle.DropDownList;
			oTrans.NotifyAll();
		}
		else
		{
		MessageBox.Show("No Open Jobs!");
		}
	}


	private void getBins()
	{
		cmbBins = (Ice.Lib.Framework.EpiUltraCombo)csm.GetNativeControlReference("886dc91a-429b-4b5b-b900-9e87438facfc");

		DynamicQueryAdapter dqa = new DynamicQueryAdapter(oTrans);
		dqa.BOConnect();		
		QueryExecutionDataSet qeds = dqa.GetQueryExecutionParametersByID("Bins");
		qeds.ExecutionParameter.Clear();

		qeds.ExecutionParameter.AddExecutionParameterRow("Ware", baqComboC1.Value.ToString(), "nvarchar", false, Guid.NewGuid(), "A");

		dqa.ExecuteByID("Bins", qeds);
		if (dqa.QueryResults.Tables["Results"].Rows.Count > 0)
		{
			cmbBins.DataSource = dqa.QueryResults.Tables["Results"];
			cmbBins.DisplayMember = "WhseBin_BinNum";
			cmbBins.ValueMember = "WhseBin_BinNum";
			cmbBins.DropDownStyle = Infragistics.Win.UltraWinGrid.UltraComboStyle.DropDownList;
			oTrans.NotifyAll();
		}
		else
		{
		MessageBox.Show("No Bins in Warehouse!");
		}
	}

private void getRevs()
	{
		cmbRevs = (Ice.Lib.Framework.EpiUltraCombo)csm.GetNativeControlReference("a03dd350-74a0-4382-b1cb-251c5e41ed9a");

		DynamicQueryAdapter dqa = new DynamicQueryAdapter(oTrans);
		dqa.BOConnect();		
		QueryExecutionDataSet qeds = dqa.GetQueryExecutionParametersByID("getPartRev");
		qeds.ExecutionParameter.Clear();

		qeds.ExecutionParameter.AddExecutionParameterRow("part", MyPartNum.Text, "nvarchar", false, Guid.NewGuid(), "A");

		dqa.ExecuteByID("getPartRev", qeds);
		if (dqa.QueryResults.Tables["Results"].Rows.Count > 0)
		{
			cmbRevs.DataSource = dqa.QueryResults.Tables["Results"];
			cmbRevs.DisplayMember = "PartRev_RevShortDesc";
			cmbRevs.ValueMember = "PartRev_RevShortDesc";
			cmbRevs.DropDownStyle = Infragistics.Win.UltraWinGrid.UltraComboStyle.DropDownList;
			oTrans.NotifyAll();
		}
		else
		{
		cmbRevs.DataSource = "";
		cmbRevs.DisplayMember = "";
		cmbRevs.ValueMember = "";
		oTrans.NotifyAll();
		MessageBox.Show("Part Not Found in Any Open Jobs!");
		}
	}


	private void MyPartNum_Leave(object sender, System.EventArgs args)
	{
		if (MyPartNum.Text!="")  
		{
		getRevs();
		cmbRevs.ForceRefreshList();
		}
	}


	private void epiRevs_Leave(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		if (epiRevs.Value.ToString()!="")  
		{
		getJobs();
		cmbJobs.ForceRefreshList();
		MessageBox.Show("If the revision is not listed below, please enter the correct revision in the Notes section.");
		//MessageBox.Show("Getting Jobs for part: " + MyPartNum.Text + " rev: " + epiRevs.Value.ToString());
		}
	}

	private void baqComboC1_Leave(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		if (baqComboC1.Value.ToString()!="")  
		{
		//MessageBox.Show("Trying to Bet Bins...");
		getBins();
		epiUltraComboC1.ForceRefreshList();
		}
	}

	private void epiButtonC1_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
	RefreshPage();
	}

	private void epiJobs_Leave(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		if (epiJobs.Value.ToString()!="")  
		{
		MessageBox.Show("The system does not track split lots. If this job has been split, please document the split lot information in the Notes section.");
		}
	}
}

Good Luck!
Nate

Nate,

I am trying my hand at this, but do not know how to find what I am doing wrong. Is there some newbie error guide?

I used your example and changed the object names to what I am using but I am getting the following:
"The type ‘Script’ already contains a definition for ‘eucGetAssySeq’ "
and
" ‘GetAssySeq’: cannot declare instance members in a static class "

There is no easy newbie error guide that I know of. It is really hard to tell what might be going wrong in the code without seeing it.

My dashboard has a blank tracker view, and a grid showing the rows from a UD table.
Then I customized it. I added in the combo boxes, and text boxes, and gave them some names. At this point it is notable that I did not choose a BAQ datasource for these combo boxes from the properties menu. I am using EpiUltraCombos. I left the EpiBinding blank.

Now in the customization code we have to make events to tell when we leave the first combo. This is tricky because sometimes you can’t use the built-in wizard to insert the event elements into the code for controls that you add to the form. When you do use the wizard, it adds a line to public void InitializeCustomCode() and public void DestroyCustomCode(). Then it puts the actual event code near the bottom.

As an example:

	public void InitializeCustomCode()
	{
		this.MyPartNum.Leave += new System.EventHandler(this.MyPartNum_Leave);
         }

	public void DestroyCustomCode()
	{
		this.MyPartNum.Leave -= new System.EventHandler(this.MyPartNum_Leave);
        }
	private void MyPartNum_Leave(object sender, System.EventArgs args)
	{
		if (MyPartNum.Text!="")  
		{
                   //Do stuff with the part number like update the next combo box.
		getRevs();
		cmbRevs.ForceRefreshList();
		}
	}

All three elements are required for your event to work at all. In this case, I wait for the user to leave the part number field. When they do it triggers the leave event and will run the getRevs() function.

getRevs updates the revision combo box with this code:

private void getRevs()
	{
		cmbRevs = (Ice.Lib.Framework.EpiUltraCombo)csm.GetNativeControlReference("a03dd350-74a0-4382-b1cb-251c5e41ed9a");

		DynamicQueryAdapter dqa = new DynamicQueryAdapter(oTrans);
		dqa.BOConnect();		
		QueryExecutionDataSet qeds = dqa.GetQueryExecutionParametersByID("getPartRev");
		qeds.ExecutionParameter.Clear();

		qeds.ExecutionParameter.AddExecutionParameterRow("part", MyPartNum.Text, "nvarchar", false, Guid.NewGuid(), "A");

		dqa.ExecuteByID("getPartRev", qeds);
		if (dqa.QueryResults.Tables["Results"].Rows.Count > 0)
		{
			cmbRevs.DataSource = dqa.QueryResults.Tables["Results"];
			cmbRevs.DisplayMember = "PartRev_RevShortDesc";
			cmbRevs.ValueMember = "PartRev_RevShortDesc";
			cmbRevs.DropDownStyle = Infragistics.Win.UltraWinGrid.UltraComboStyle.DropDownList;
			oTrans.NotifyAll();
		}
		else
		{
		cmbRevs.DataSource = "";
		cmbRevs.DisplayMember = "";
		cmbRevs.ValueMember = "";
		oTrans.NotifyAll();
		MessageBox.Show("Part Not Found in Any Open Jobs!");
		}
	}

Notably, you have to change the native control ref ID to your GUID for the combo box you added. Check the properties for the control to find EpiGuid. You will also need to make a BAQ that looks up the values you need. In this case the name of the BAQ is getPartRev. The BAQ has a parameter in it that filters for part number and returns the revisions. The BAQ is dead-simple:

select 
	[PartRev].[RevisionNum] as [PartRev_RevisionNum],
	[PartRev].[RevShortDesc] as [PartRev_RevShortDesc]
from Erp.PartRev as PartRev
where (PartRev.PartNum = @part)

Now you can start to see where some of the field names come from in getRevs(). Back in the leave event for part number, I also try to force the combo box to update after I use getRev() to give the combo box a data source. To force the update I use: cmbRevs.ForceRefreshList();

Now if you want to do this for Resource Groups and Resources, you will first need a BAQ that can return the list you expect to see in your combo box when you provide it a parameter. In my example the user types in the part numebr to start, but you will want to select the resource group from a drop down box. Since there are no parameters required for the resource group combo box, that bac be populated with a simple BAQ that does not have a parameter and just returns all the resource groups. You could add filtering in the BAQ so that only a subset of RGs you care about are returned.

Then you add a combo box for your resource group to your dashboard. Instead of an Epi Ultra Combo use a BAQ combo. Set the properties for the combo box: DynamicQueryID: Your BAQ name DisplayMember: the name of the field you want to see, ValueMember: the name of the field you want to store. These can be the same.

That should get your first combo working. Then you want to add a EpiUltraCombo for the Resource drop down. This will be populated by code similar to what I wrote above. You will have to add an event for when the user leaves the Resource Group combo. I like to make sure the user selected a value, otherwise the next BAQ won’t have a parameter value to filter by, and will either fail, or return all the resources.

I hope this helps! Good luck!
Nate