Use MessageBox.Show in BPM "Execute Custom Code" block

I know BPM’s have “Show Message” block, but I’m trying to display info from inside my custom code block.

Initially it will be for debugging, But I may wish to have a MessageBox with OK and CANCEL buttons, and the custom code execution is driven by the users choice.

I’m not grasping how get the System.Windows.Forms namespace available to my custom code. Do I need an entry on the “Usings” tab, or added on the “References” tab?

Calvin

		string body = "Your Message Here";
		this.PublishInfoMessage(body, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "FirstVar","SecondVar");
2 Likes

This won’t have a OK/Cancel, only an OK but it will throw a message.

Throwing the message will by okay for debugging.

Is there a away to allow user interaction during a BPM?

Or is that a really really bad idea? - because the BPM could get called from some unexpected source, and get hung up waiting for someone to respond

A BPM data form might be able to route that, but you would have to have downstream BPM logic to either execute or stop with an exception.

I was just looking at BPM Data Forms. Do you know if the can be called from Exec Custom Code blocks?

I’m not a fan of graphical programming (absolutely detest LabView). I’d much rather have just a Start block, a single Condition block (for a first pass cut at whether to run my custom code), and a Execute Custom Code block.

Followup to whether a BPM Form can be called from within Custom code…

Can parameters be passed to the BPM form (either in Custom Code, or when called via the BPM Workflow designer) ?

Absolutely. The BPM data form is designed to work with Call Context variables, and you can set those to whatever you want. I wouldn’t recommend attempting to create your BPM data form within code, but I’m sure it can be done.

Aaron,

I am new to BPM Custom Code and still learning. How do you call a BPM Data form from a Custom code. I have a if statement and want to call a BPM Data form if it’s not null. Can you send me a sample code? See my code below:
Erp.Tables.PartCost PartCost;

foreach (var ttOrderDtl_iterator in (from ttOrderDtl_Row in ttOrderDtl
where (string.Equals(ttOrderDtl_Row.RowMod, IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase) || string.Equals(ttOrderDtl_Row.RowMod, IceRow.ROWSTATE_UPDATED, StringComparison.OrdinalIgnoreCase))
select ttOrderDtl_Row))
{
var ttOrderDtlRow = ttOrderDtl_iterator;

PartCost = (from PartCost_Row in Db.PartCost
		where string.Compare(PartCost_Row.Company, ttOrderDtlRow.Company, true) == 0 &&
					PartCost_Row.PartNum == ttOrderDtlRow.PartNum &&
					(PartCost_Row.StdBurdenCost + PartCost_Row.StdLaborCost + PartCost_Row.StdMaterialCost + PartCost_Row.StdMtlBurCost + PartCost_Row.StdSubContCost) < ttOrderDtlRow.UnitPrice
					select PartCost_Row).FirstOrDefault();

if(PartCost != null)
	{
		***** Code here to call BPM Data Form *****
	{

/CallContext.Current.ExceptionManager.AddBLException(“SAVE NOT ALLOWED! Unit price of Part is below Standard Cost. Please correct and try again.”);/

}

Can you do that without writing additional code.Simply put your query in a Condition where it returns true / false, then call the BPM Form IF true

A MUCH better way would be to route your BPM logic using call context BPM variable values from inside your custom code block, then use conditional blocks to route the BPM to either call a data form or not.

That said, I have never really looked into calling the BPM data form through code, but it can be done using the following lines according to the BPM source code. I

this.BpmDataFormProcess("Your Form Name Here");
this.BpmDataFormPublish("Your Form Name Here |a|", "");

Again, this isn’t a good way to accomplish your task and isn’t going to function properly without more investigation, but it can be done for what it’s worth.

1 Like

I started using just the query but I need to “total” all standard costs from the PartCost table. I can’t seem to find a standard condition query that will allow me to do a calculated field. Again, I am fairly new on this BPM coding and still learning. What I am trying to do is prompt the user (in Sales Order Entry) with a yes/no option if the unit price is below Standard cost.

I’d use a UI customization, in which case you can use a DialogResult class to route Yes/No/Cancel on a message box.

`DialogResult dialogResult = MessageBox.Show("Message Body", "Message Title", MessageBoxButtons.YesNo);`
if(dialogResult == DialogResult.Yes)
{
//do something if yes
}
else if(dialogResul == DialogResult.No)
{
//do someting else
}
1 Like

Hi Aaron. I`m using this example but would like to use different names for my buttons. Currently it only works using the default buttons.

DialogResult result = MessageBox.Show(Inputs.CleatError.Value,“Choose Low or High option or Cancel”,MessageBoxButtons.YesNoCancel,MessageBoxIcon.Exclamation);

image

Hey Brad, I think if you are wanting a custom dialog box, you’ll have to code one up yourself and then invoke it. It’s kind of a pain in the a$$ but definitely doable.

In the example below, I use several different custom dialog prompts and hopefully you can get the idea of how to do it.

// **************************************************
// Custom code for MainController
// Created: 12/19/2016 4:02:57 PM
// **************************************************
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.Text;

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

		this.btnGetCode.Click += new System.EventHandler(this.btnGetCode_Click);
		this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
		// 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.btnGetCode.Click -= new System.EventHandler(this.btnGetCode_Click);
		this.btnReset.Click -= new System.EventHandler(this.btnReset_Click);
		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
	}
	
	private void btnReset_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		//clear fields
		txtShipViaCode.Clear();	
		txtCarrierCode.Clear();	
		txtServiceCode.Clear();
		txtBoxCode.Clear();
		txtIceCode.Clear();
	}

	private void btnGetCode_Click(object sender, System.EventArgs args)
	{
			//local variables to build out Ship Via Code
		string carrierCode = String.Empty;	
		string promptService = String.Empty;//used for temp storage
		string serviceCode = String.Empty;
		string boxCode = String.Empty;		
		string dryIce = String.Empty;
		StringBuilder builder = new StringBuilder();

		//call carrier class
		string promptCarrier = CarrierPrompt.ShowDialog(oTrans,"Choose Carrier:", "Choose Carrier");
		txtCarrierCode.Text = promptCarrier;	
		if(!String.IsNullOrEmpty(promptCarrier))
		{
			//switch on carrier and prompt service options
			switch (promptCarrier)
			{
				case "F":							
					promptService = FedExServicePrompt.ShowDialog(oTrans,"Choose Service Option:", "Choose FedEx Service");
					//Mutators : when service code ends in 0, change carrierCode to "G" and trim service option code.
					if(!String.IsNullOrEmpty(promptService))
					{
						if(promptService.Length >1)
						{	
							carrierCode = "G";	
							serviceCode = promptService.Substring(0,1); //trim the 0 off the code
							builder.Append(carrierCode).Append(serviceCode);
						}	
						else
						{
							carrierCode = "F";
							serviceCode = promptService;
							builder.Append(carrierCode).Append(serviceCode);				
						}	
						//MessageBox.Show(builder.ToString());						
					}
					break;
				case "U":
					promptService = UPSServicePrompt.ShowDialog(oTrans,"Choose Service Option:", "Choose UPS Service");
					if(!String.IsNullOrEmpty(promptService))
					{
						carrierCode = "U";
						serviceCode = promptService;
						builder.Append(carrierCode).Append(serviceCode);
						//MessageBox.Show(builder.ToString());	
					}						
					break;
				case "M":
					promptService = MiscServicePrompt.ShowDialog(oTrans,"Choose Service Option:", "Choose Miscellaneous Service");
					if(!String.IsNullOrEmpty(promptService))
					{
						carrierCode = "M";
						serviceCode = promptService;
						builder.Append(carrierCode).Append(serviceCode);	
						//MessageBox.Show(builder.ToString());
					}						
					break;
				case "C":
					promptService = CourierServicePrompt.ShowDialog(oTrans,"Choose Service Option:", "Choose Courier Service");
					if(!String.IsNullOrEmpty(promptService))
					{
						carrierCode = "C";
						serviceCode = promptService;
						builder.Append(carrierCode).Append(serviceCode);	
						//MessageBox.Show(builder.ToString());
					}						
					break;					
			}
			txtCarrierCode.Text = carrierCode;
			txtServiceCode.Text = serviceCode;
			if(!String.IsNullOrEmpty(promptService))
			{
				//dry ice check,if yes, proceed to box class, if no, set boxCode to 00. 
				dryIce = DryIcePrompt.ShowDialog(oTrans,"Choose Dry Ice Option:", "Choose Dry Ice Option");
				txtIceCode.Text = dryIce;
				if(!String.IsNullOrEmpty(dryIce))
				{
					switch(dryIce)
					{
						case "Yes":
							boxCode = BoxTypePrompt.ShowDialog(oTrans,"Choose Box Option:", "Choose Box Option");
							builder.Append(boxCode);
							txtBoxCode.Text = boxCode;
							break;
						case "No":
							boxCode = "00";
							builder.Append(boxCode);
							txtBoxCode.Text = boxCode;						
							break;
					}
					//MessageBox.Show(builder.ToString());
					//set final ship via code field value here
					txtShipViaCode.Text = builder.ToString();
				}
			}
		}
	}
}
public static class CarrierPrompt
{
	private static DataTable dtCarrier;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption,
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboCarrier = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; //TODO cancel key
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboCarrier);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'Carrier'";
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtCarrier = adapterUserCodes.UserCodesData.UDCodes;

		comboCarrier.DataSource = dtCarrier;
		comboCarrier.DisplayMember = "LongDesc";
		comboCarrier.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboCarrier.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }

}

public static class FedExServicePrompt
{
	private static DataTable dtSerivceOption;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption, 
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; //150, 75
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'FedExOpts'";
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtSerivceOption = adapterUserCodes.UserCodesData.UDCodes;

		comboService.DataSource = dtSerivceOption;
		comboService.DisplayMember = "LongDesc";
		comboService.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }
}

public static class UPSServicePrompt
{
	private static DataTable dtSerivceOption;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption,
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; 
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'UPSOpts'";
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtSerivceOption = adapterUserCodes.UserCodesData.UDCodes;

		comboService.DataSource = dtSerivceOption;
		comboService.DisplayMember = "LongDesc";
		comboService.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }
}
	
public static class MiscServicePrompt
{
	private static DataTable dtSerivceOption;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption, 
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; 
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'MiscOpts'";
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtSerivceOption = adapterUserCodes.UserCodesData.UDCodes;

		comboService.DataSource = dtSerivceOption;
		comboService.DisplayMember = "LongDesc";
		comboService.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }
}

public static class CourierServicePrompt
{
	private static DataTable dtSerivceOption;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption, 
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; 
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'CourierOpt'";//notice no "Opts", too short for field length :P
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtSerivceOption = adapterUserCodes.UserCodesData.UDCodes;

		comboService.DataSource = dtSerivceOption;
		comboService.DisplayMember = "LongDesc";
		comboService.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }
}

public static class DryIcePrompt
{	
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption, 
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; 
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;	
	
		String[] yesNoArray = {"Yes", "No"};
		comboService.DataSource = yesNoArray;
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
    }
}

public static class BoxTypePrompt
{
	private static DataTable dtSerivceOption;
    public static string ShowDialog(object oTrans, string text, string caption)
    {
	
        Form prompt = new Form()
        {
            Width = 350,
            Height = 175,
            FormBorderStyle = FormBorderStyle.FixedDialog,
            Text = caption,
            StartPosition = FormStartPosition.CenterScreen
        };
        Label textLabel = new Label() { Left = 50, Top=20, Text= text };
        //TextBox textBox = new TextBox() { Left = 50, Top=50, Width=400 };	
		//this is the combo for carrier
		ComboBox comboService = new ComboBox() { Left = 50, Top=50, Width=250 };
        Button confirmation = new Button() { Text = "Next...", Left=50, Width=100, Top=75, DialogResult = DialogResult.OK };
		Button cancel = new Button() { Text = "Cancel", Left=155, Width=100, Top=75, DialogResult = DialogResult.Cancel}; 
        confirmation.Click += (sender, e) => { prompt.Close(); };
       // prompt.Controls.Add(textBox);
		prompt.Controls.Add(comboService);
        prompt.Controls.Add(confirmation);	
		prompt.Controls.Add(cancel);
        prompt.Controls.Add(textLabel);
        prompt.AcceptButton = confirmation;
		
		//call UserCodes adapter and filter it
		UserCodesAdapter adapterUserCodes = new UserCodesAdapter(oTrans);
		adapterUserCodes.BOConnect();
		SearchOptions opts = new SearchOptions(SearchMode.AutoSearch);
		opts.PreLoadSearchFilter = "CodeTypeID = 'BoxOpts'";
		opts.DataSetMode=DataSetMode.RowsDataSet;
		adapterUserCodes.InvokeSearch(opts);
			
		dtSerivceOption = adapterUserCodes.UserCodesData.UDCodes;

		comboService.DataSource = dtSerivceOption;
		comboService.DisplayMember = "LongDesc";
		comboService.ValueMember = "CodeDesc";
	
		//show user results and pass code ID
        return prompt.ShowDialog() == DialogResult.OK ? comboService.SelectedValue.ToString() : "";	
		//cleanup adapter
		adapterUserCodes.Dispose();
    }
}
2 Likes

Thanks for the example code Aaron. This definitely helps. Yes, this will be a pain in the a$$. Lol.

1 Like

A less coding, but more hacky way, would be to use a BPM form on a UD table. You code would invoke a BPM, and that BPM would call the BPM form. You could return data (text entered, button pressed, etc…) via the UD table it is all tied to.

2 Likes