How to create a tabbed dialog
A tabbed dialog is a Site Manager dialog that has an embedded tab control object, in other words, a combination of a dialog and a tabbed view. It is usually used to add complex objects, for example a Retail Item.
Create the dialog
To create a tabbed dialog you can create a simple dialog as presented in How to create a dialog, in which you add a tab control.
Functions overview
Tabbed dialog functions
A list of commonly used functions when creating a tabbed dialog:
Base functions |
Description |
protected override void OnClosing |
When closing the tabbed dialog we can call the tabSheetTabs.SaveUserInterface() method in case any tab needs to save something on the user interface. |
void btnOK_Click |
When we press OK, the data from all tabs is gathered and validated, then if everything is valid, we save the new object. |
void btnCancel_Click |
Closes a dialog and no data is changed. |
protected override IApplicationCallbacks OnGetFramework() |
Needs to return the Site Manager framework instance. This instance is usually kept in the: PluginEntry.Framework. |
Tab functions
A list of commonly used functions when creating tab used in a tabbed dialog:
Base functions |
Description |
void LoadData(
|
The function is called by the tabbed dialog via the tabControl.SetData(..) function. isRevert parameter is the same parameter as the tabbed dialog gets and indicates whether the user is reverting or not. context parameter is usually the ID of the object that is being edited by the tabbed dialog. internalContext is usually the data object that the tabbed dialog is editing. Note that the text above describes the usual setup but that is not always the case. Sometimes we do not have a context we are working with and sometimes the internalContext parameter is more complex. You need to view the tabControl.SetData() function of the tabbed dialog to see what it is really sending. |
void SaveSecondaryRecords() |
This function is called by the tabControl.SaveSecondaryRecords() and can be used to save additional data after saving the main object. |
object OnViewPageMessage( |
This function is called by the tab control via the tabControl.SendViewPageMessage(..) function. All tabs registered to the tab control will receive the message and we can check for a particular message and execute some functionality based on each message. |
void IDialogTabViewWithRequiredFields |
This function is called by the tab control via the tabControl.AllRequiredFieldsAreValid(..) function. This function is used to validate the tab and return a validation result. |
bool SaveData() |
This function is called by the tabControl.GetData() which will populate the main object with all the properties from each tab, before the actual save can be made. |
void GetAuditDescriptors( List<AuditDescriptor> contexts) |
Connects the tab with an audit view. See chapter 3.7 Auditing for more details. |
void OnDataChanged(..) |
This function is described in the different kinds of Site Manager views. |
void OnClose() |
This function is used to clear any data when the tab is discarded. Usually you can leave this empty. |
Sample code walkthrough
Below we can see examples of the basic functions used in a tabbed dialog and tabs used in the dialog:
public NewRetailItemDialog(bool allowCreateAnother) : this()
{
cbCreateAnother.Enabled = allowCreateAnother;
}
public NewRetailItemDialog()
{
retailItem = new RetailItem();
InitializeComponent();
tabSheetTabs.AddTab(new TabControl.Tab(Resources.General,
DialogPages.NewRetailItemGeneralPage.CreateInstance, true));
tabSheetTabs.Broadcast(this, RecordIdentifier.Empty);
tabSheetTabs.SetData(false, RecordIdentifier.Empty, retailItem);
}
Here we can see the contructors of the new retail item dialog. The first one is used when the user checks the Create Another option.
The second constructor initializes the retail item with a new empty item, then adds the first tab in the tab control. The next line sends a message to all other plugins to be able to add tabs from other plugin into our dialog as described in How to create a tabbed view. The last line sets the data in all registered tabs, calling the LoadData(..) method from each tab.
To add a tab from the same plugin, you can simply add it in the constructor of our tabbed dialog as presented in the Constructors section above.
To add a tab from another plugin, first you need to make sure that in the constructor of our tabbed dialog, you call the tabSheetTabs.Broadcast(..) function. Then you need to add code to your IPlugin implementing class (usually called PluginEntry).
The code you need to add is in the Init function of the PluginEntry.
internal static IConnectionManager DataModel = null;
internal static IApplicationCallbacks Framework = null;
...
public void Init(IConnectionManager dataModel,
IApplicationCallbacks frameworkCallbacks
{
DataModel = dataModel;
Framework = frameworkCallbacks
...
Controls.TabControl.TabPanelConstructionHandler +=
PluginOperations.ConstructTabs;
}
Here we are telling the Site Manager that the function PluginOperations.ConstructTabs exists and that in it we want to add tabs to other views or dialogs.
Let’s take a look at that code:
internal static void ConstructTabs(object sender,
TabPanelConstructionArguments args)
{
if (args.ContextName == "LSOne.ViewPlugins.RetailItems.Dialogs.NewRetailItemDialog")
{
args.Add(new TabControl.Tab(Properties.Resources.BarCodes,
new PanelFactoryHandler(DialogPages.NewRetailItemBarCodePage.CreateInstance),
true, true), 50);
}
}
In the args.ContextName we have the name and namespace of the dialog that we are going to add the tab into.
Within the if statement we add the tab. In the code above we see that we are adding the NewRetailItemBarCodePage tab. The first parameter of the tab is the header text for the tab, the second parameter is a method that returns an instance of the tab (this method is usually located within the tab itself). The next two parameters specify if the tab has required input to be set and if they are fulfilled by default when loading the tab. The last parameter is the priority number of the tab. This number determines where the tab is located among the other tabs.
When you click the OK button of the tabbed dialog, first you can validate all tabs and if they are valid, save the data. If they are not valid you can show a warning message.
private void btnOK_Click(object sender, EventArgs e)
{
FieldValidationArguments args = tabSheetTabs.AllRequiredFieldsAreValid();
if (args.Result == FieldValidationArguments.FieldValidationEnum.TabHasMissingFields)
{
MessageDialog.Show(string.Format(Resources.TabRequiresInput, args.TabName) , MessageBoxIcon.Error);
}
else
{
SpinnerDialog dlg = new SpinnerDialog(Resources.FewMinutesMessage, () => SaveData());
dlg.ShowDialog();
ItemID = retailItem.ID;
if (cbCreateAnother.Checked)
{
retailItem = new RetailItem();
bool handled;
tabSheetTabs.SendViewPageMessage(this, "CreateAnother", null, out handled);
tabSheetTabs.SetData(false, RecordIdentifier.Empty, retailItem);
tabSheetTabs.SelectedTab = tabSheetTabs[0];
}
else
{
DialogResult = DialogResult.OK;
Close();
}
}
}
private void SaveData()
{
tabSheetTabs.GetData();
Providers.RetailItemData.Save(PluginEntry.DataModel, retailItem);
tabSheetTabs.SaveSecondaryRecords();
}
The code above is from the main tabbed dialog. When the user presses the OK button, first we tell all tabs to validate their inputs and return a validation result. If the result is valid we call the SaveData() method. In this method, first we use the tabSheetTabs.GetData() to get the data from all tabs into our retail item, then we save the item. Lastly we call the tabSheetTabs.SaveSecondaryRecords() to tell all tabs to save the secondary records, if there are any.
After saving the data, we check if the Create Another option is checked, and if it is we send a message to all tabs to clear their inputs and reset the data so we can create another item. Otherwise, we close the dialog.
The following code will show how to respond to message broadcasts from the tab control, on a tab used in a tabbed dialog.
public void SaveSecondaryRecords()
{
if (cmbBarCodeSetup.SelectedData.ID != "" && tbBarCode.Text.Trim() == "" ||
cmbBarCodeSetup.SelectedData.ID == "" && tbBarCode.Text.Trim() != "")
{
errorProvider1.SetError(linkBarCode, Properties.Resources.BarcodeFieldsNotfilledIn);
return;
}
if (tbBarCode.Text.Trim() != "")
{
BarCode barCode = new BarCode();
barCode.ItemID = retailItem.ID;
barCode.ItemBarCode = tbBarCode.Text;
barCode.BarCodeSetupID = cmbBarCodeSetup.SelectedData.ID;
Providers.BarCodeData.Save(PluginEntry.DataModel, barCode, RecordIdentifier.Empty);
}
}
void IDialogTabViewWithRequiredFields.RequiredFieldsAreValid(FieldValidationArguments args)
{
if (barCodeValid)
{
args.Result = FieldValidationArguments.FieldValidationEnum.Valid;
}
else
{
args.Result = FieldValidationArguments.FieldValidationEnum.OtherInvalid;
args.ResultDescription = Resources.BarcodeNotCorrectlySetup;
}
}
public object OnViewPageMessage(object sender, string message, object param, ref bool handled)
{
switch (message)
{
case "CreateAnother":
tbBarCode.Text = "";
barCodeValid = true;
errorProvider1.Clear();
return null;
}
return null;
}
Here we see three methods that are being called by the TabControl from the main tabbed dialog.
The first method is called when calling the tabSheetTabs.SaveSecondaryRecords() method from the main dialog, after saving the main record. In this method we save a new barcode using the ID of the new retail item that was just created.
The second method is called when calling the tabSheetTabs.AllRequiredFieldsAreValid() method from the main dialog. This will validated the current tab and return the corresponding validation result.
The last method listens to messages sent by the tab control with the tabSheetTabs.SendViewPageMessage(..) method. In this example, if we receive a message "Create another", we clear the tab and set it as valid to be able to create another retail item. We can of course add more cases and listen to different messages.
Next task is to exercise creating tabs: Lesson 5 - Plugins: views, tabs and dialogs