Web Form Development: Advanced Mode

Overview

Advanced mode is designed specifically for developers by providing full control over every aspect of a web form. This allows for more flexibility and provides more possibilities for which functions can go into the web form.

Creating the web application

Suggested development tools

  • Visual Web Developer Express 2015 or later

  • Visual Studio 2015 or later

Web form installation directory

We strongly suggest that you put all your web forms in the \wfgen\wfapps\webforms directory (e.g. \wfgen\wfapps\webforms\MyWebForm).

Note that if you modify your WorkflowGen process and you then need to modify the associated MyWebForm because of these changes, you should duplicate MyWebForm beforehand and create another IIS application. Otherwise, the two versions of the process will use the same modified MyWebForm.

Create the application in IIS

The web form application must be declared as an application in IIS in order to be recognized as a .NET web form application. Follow these instructions to declare your web form directory as an IIS application:

For IIS 7 and later

  1. Open IIS Manager.

  2. Navigate to your web form location, which should be in the Default Web Site node under \wfgen\wfapps\webforms\MyWebForm.

  3. Right-click on MyWebForm and choose Convert to Application.

  4. Select the application pool used by your site and another specific application pool.

  5. Click OK.

Creating the project with Visual Studio

Create the project

  1. Open Visual Studio and choose File > New Web Site.

  2. Select ASP.NET Web Site.

  3. In the Location drop-down list, select File system.

  4. Click Browse and select the location of your ASP.NET web site.

  5. Click OK.

Create a typed DataSet

You'll need to create a new DataSet item in your project and add a Table1 DataTable into it. Follow these steps in order to add a new DataSet in your web project:

  1. Right-click on your project and select Add new item...

  2. Choose DataSet, then click Add.

  3. A pop-up will appear asking you to choose a data connection; click Cancel because a DataSet can't be attached to a specific data source (it will instead be sent dynamically at runtime).

  4. Right-click in the empty space and select Add > DataTable.

  5. Rename DataTable1 as Table1.

  6. Add all of your form field names and the IN, OUT, and INOUT parameters as columns in this table.

    ✏️ Note: It's necessary to rebuild your web form each time you change this DataSet definition.

  7. Add a DataSet variable to your Default.aspx.cs code and instantiate it in the default constructor of your class. If your class name is Form, the code will look like this for now:

    public partial class Form : WorkflowGen.My.Web.UI.WebForms.WorkflowPage
    {
     protected DataSet1 formData;
    
     public Form() : base()
     {
         formData = new DataSet1();
     }

Obtaining detailed error messages

By default, there will be no web.config file in your web project if you're using C# as your development language in the Microsoft Visual Studio IDE. In order to see complete error messages when you want to debug, you must have a web.config file.

To add a default web.config file to your project, do the following:

  1. Right-click on your project name, then select Add new item...

  2. Choose Web configuration file, then click OK.

In order to be able to see complete error messages, change the following properties in the web.config file:

  1. Make sure this line is set to "true":

    <compilation debug="true" />
  2. Make sure this is not commented and that mode="Off":

    <customErrors mode="Off" defaultRedirect="GenericErrorPage.htm">
        <error statusCode="403" redirect="NoAccess.htm" />
        <error statusCode="404" redirect="FileNotFound.htm" />
    </customErrors>

Basic implementation

Reference

You must add a reference to WorkflowGen.My.dll to your web project, then add this instruction at the beginning of your web form:

using WorkflowGen.My.Web.UI.WebForms;

Class inheritance

Your web form should inherit from the WorkflowPage class contained within the WorfklowGen.My.Web.UI.WebForms namespace.

Using Advanced mode

When using the WorkflowPage Advanced mode, you must specify that you're not using Simple mode. Use the following code to do this:

public partial class Form : WorkflowGen.My.Web.UI.WebForms.WorkflowPage
{
    protected DataSet1 formData;

    public Form() : base()
    {

// Set the SimpleMode property to False to use Advanced mode
    IsSimpleMode = false;
    formData = new DataSet1();
    }
}

Page_Load method

The first thing that should be done in the Page_Load method is call the FillFormData() method to fill the data in your form data. The Page_Load is also the place to make any changes to WorkflowPage properties. You can give a DataSet or an XmlDocument to the FillFormData() method.

protected void Page_Load(object sender, System.EventArgs e)
{
    // Fill your form data here
    FillFormData(formData);

    // Bind the form data to the web form fields. (This is not mandatory,
    // it depends on how you want to manage your form data)

if (!Page.IsPostBack)
    {

BindFormDataToFields(formData);
    }

    // Any additional treatment goes here
    // ...
}

Saving form data

Form data should be saved on any postback that modifies the form data. If you don’t save it before the end of the postback, any changes will be lost because the FillFormData() method in the Page_Load event will override the changes with the old data.

In order to save your form data to EFORMASPX, you have to call the SaveFormData() method from WorkflowPage. You can give a DataSet or an XmlDocument to the SaveFormData() method.

protected void AnyServerSideEventModifyingFormData(object sender, ...)
{
    // Modify the form data ...
    formData.Tables["TableExpense"].Rows.RemoveAt(e.Item.ItemIndex);

    // Rebind the control(s) to see the changes on the web form
    DataList1.DataBind();

    // Save the form data
        this.SaveFormData(formData);
}

Updating form data with the web form values

You'll usually want to update your form data with the web form values before submitting the form data to WorkflowGen. This can be done for all the fields that are bound in the Table1 of your form data by calling the SaveFieldsData(DataSet) method. The method will try to find controls in the page that have the same IDs as the Table1columns, and when it finds one, it will update the form data value with the control value.

protected void Submit_Click(object sender, System.EventArgs e)
{
    // Automatically update the form data with the webform values
    this.SaveFieldsData(formData);
}

This method only supports DataSets. The fields that are updated must be in a table named Table1.

Submitting to workflow

Once it's time to submit everything to the workflow (via EFORMASPX), you have to call the WorkflowPageSubmitToWorkflow() method. This method needs to receive your form data if you want to save it at the same time. You can also manually separate these calls by calling SubmitToWorkflow without any parameter; however, if you do this, your form data won’t be updated if you don’t explicitly call SaveFormData(formData) before submitting.

protected void Submit_Click(object sender, System.EventArgs e)
{

// Any treatment before submitting goes here (e.g.: validation, data modification etc.)
    // ...

    // Submits everything to the workflow and creates the form archive if needed.
    this.SubmitToWorkflow(formData);
}

Field validation management

Overview

WorkflowPage automatically creates RequiredFieldValidator controls in your page to validate the fields that were written to the FORM_FIELDS_REQUIRED parameter in all of your EFORMASPX actions. These validations can be put on any of the following control types:

  • TextBox

  • RadioButtonList

  • DropDownList

  • ListBox

  • FileUpload

  • HtmlInputFile

If you want to have a personalized message, you have to change the RequiredFieldsErrorMessage property. Be sure to have the {0} symbol in your personalized message. If you don't put a {0} tag somewhere in your message, an exception will be thrown because it wouldn't be logical to have a list of required field messages without knowing which fields need to be filled.

The RequiredFieldValidators that are created have their ValidationGroup properties set to WFGENPage. This means that the validation of those validators will be done only if the control that triggers the validation has its ValidationGroup property set to the same value. By default, if you have a SubmitButton button control in your page, its ValidationGroup is automatically modified. If you use another ID for the submit button, you have to set the ValidationGroup yourself on it, or else the required fields won’t be validated as you would expect when submitting the page.

Data type validation

When you specify the data type of a field using the FieldDataType attribute, validators are automatically inserted into your page to validate the DATE and NUMERIC data types.

  • DATE fields are validated using a RangeValidator with the MinimumValue property set to DateTime.MinimumValue and the MaximumValue property set to DateTime.MaximumValue.

  • NUMERIC fields are validated using a RangeValidator with the MinimumValue property set to Double.MinimumValue and the MaximumValue property set to Double.MaximumValue.

The RangeValidator controls have the following naming convention: WFGEN_RV_FIELD_ID.

If you create your own RangeValidator controls to validate the fields on which you have set the FieldDataType attribute, WorkflowPage won’t automatically create a RangeValidator for those fields.

The RangeValidator controls that are created have their ValidationGroup properties set to WFGENPage. This means that the validation of those validators will be done only if the control that triggers the validation has its ValidationGroup property set to the same value. By default, if you have a SubmitButton button control in your page, its ValidationGroup is automatically modified. If you use another ID for the submit button, you have to set the ValidationGroup on it yourself, or else the fields won’t be validated as you would expect when submitting the page.

Custom validation

If you want to make your own personalized validations, you can always create your RequiredFieldValidator or RangeValidator controls on the fields where you have specified the FieldDataType attribute, and WorkflowPage will not automatically create RequiredFieldValidator or RangeValidator controls for those particular controls.

If you want to make more validations on any of your fields using other .NET validators (such as CompareValidator, CustomValidator, or RegularExpressionValidator controls), you'll need to change the ValidationGroup properties of those validators to WFGENPage; otherwise, the validation won't be made for the targeted controls when the form is submitted.

📌 Example

<asp:RegularExpressionValidator ID="RegValidatorExample" runat="server"
ErrorMessage="Your error message" ControlToValidate="YourControl"
Display="Dynamic" ValidationGroup="WFGENPage" ValidationExpression="\w+([-
+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
</asp:RegularExpressionValidator>

Read-only fields

Overview

WorkflowPage can automatically make some fields read-only or disabled (depending on the control types) for the fields in the FORM_FIELDS_READONLY parameter. The following control types will be set to read-only or disabled if they are found in FORM_FIELDS_READONLY:

  • TextBox

  • RadioButton

  • RadioButtonList

  • CheckBox

  • CheckBoxList

  • DropDownList

  • ListBox

  • FileUpload

  • HtmlInputFile

Field colorization management

Overview

WorkflowPage automatically modifies the appearance of the FORM_FIELDS_REQUIRED and FORM_FIELDS_READONLY controls according to its FieldsColorization property.

The FieldsColorization property is an instance of an enumeration that can have the following values:

  • Automatic

  • CSS

  • None

Automatic

Automatic mode uses the following properties to colorize the fields:

  • RequiredFieldsBorderColor

  • ReadOnlyFieldsBorderColor

Some control types have their borders colorized while some others have their text colors modified (for aesthetic purposes).

The following controls will have their text color (ForeColor property) modified if the color is not Color.Empty in their corresponding properties:

  • DropDownList

  • CheckBoxList

  • RadioButtonList

The rest of the controls will have their border colors modified if the color is not Color.Empty in their corresponding properties.

CSS mode

Instead of modifying the style directly, CSS mode modifies the controls' CssClass properties as shown in the example below, which gives you much more flexibility to have the exact look you want in your web form.

📌 Example

  • If TextBox1 has its CssClass property set to FieldValueCell and it is a required field, CssClass1 will be changed to FieldValueCell-required.

  • If TextBox1 has no CssClass property, CssClass will be changed to Required.

None mode

None mode will not modify the color or the style of any controls in your web form. This mode completely deactivates automatic colorization or CSS colorization.

Form archive management

CSS stylesheet

You can customize the look of your form archive by using the form_archive.css file.

Your CSS styles should be located in a sub-directory called \css and your form archive style sheet file should be called form_archive.css, since WorkflowPage will look for this particular location and file name to find it, then replace the form archive styles with the styles contained in this file.

If those default values are not what you want to use, you can always change the FormArchiveCssPath property, whose default value is \css\form_archive.css. If no form_archive.css file is created, the form archive will have the same look as the web form, but the fields will all be set to read-only or disabled.

Hiding fields

By default, the button in your page with the SubmitButton ID will be automatically hidden in the form archive.

If you want to hide other fields in the form archive, use the FORM_FIELDS_ARCHIVE_HIDDEN parameter. The controls listed in this parameter will automatically be hidden when creating the form archive.

Controls that are not runat=server in your web form will not be hidden.

You'll sometimes want to do some customization of the archived form's layout. In order to do this, you'll need to override the WorkflowPage ChangeFormArchiveLayout() method.

protected override void ChangeFormArchiveLayout()
{
    // Perform custom treatments here
    // ...

    // Make sure you call the base ChangeFormArchiveLayout if you want the fields to
    // automatically be set to read-only and hide fields that are listed in 
    // FORM_FIELDS_ARCHIVE_HIDDEN
    base.ChangeFormArchiveLayout();
}

If you want even more flexibility with form archive creation, you can override the GetFormArchive() method. This method has to return a string containing the form archive's HTML.

protected override string GetFormArchive()
{
    // Build the html of the form archive

    // ...

    // Return the html result

return htmlResult;
}

File attachment management

When you have a FileUpload control in your web form, you'll want to use the SaveFileAttachment() method in WorkflowPage. This method is used to save the file to the \upload sub-directory in the EFORMASPX storage path and retrieve the file name of the posted file.

protected void Submit_Click(object sender, System.EventArgs e)
{
    // ...

    // Save the file attachment to EFORMASPX and update the file name in the form data
    formData.Tables["Table1"].Rows[0]["TEST_UPLOAD"] = 
SaveFileAttachment(TEST_UPLOAD.PostedFile);

    // ...
}

If you have multiple file attachments in a single action, you should consider renaming the posted files manually, because if the user sends two different files that have the same name, the files will override each other. To save the file attachment with a specified name, use the overloaded SaveFileAttachment() method.

protected void Submit_Click(object sender, System.EventArgs e)
{

// ...

    // Save the file attachment to EFORMASPX and update the file name in the form data
    formData.Tables["Table1"].Rows[0]["TEST_UPLOAD"] = 
SaveFileAttachment(TEST_UPLOAD.PostedFile,
"FileNameYouChoose.ext");

    // ...
}

Resource management

Adding resources to your web project

  1. Right-click on your website root, then select Add new item...

  2. Choose Resource File and enter the name you want for your resource file.

  3. Click Add. This file will contain the default resources for your web form (en-US).

  4. Add a resource file for every language you want to manage for your web form. The file name must end with culture-code.resx.

    📌 Example: Strings.fr-FR.resx would be the file name for the French language from France.

When you want to use the resources in your web form, you just have to use the System.Resources namespace, and all your resources will automatically be strongly typed. This means that you can access all of the content of your resources files with object properties.

this.RequiredFieldsErrorMessage = Resources.Strings.RequiredErrMsg;

GridView management

Overview

This section will explain how to use a GridView in Advanced mode.

DataTable

The first thing you need to do is add a DataTable to your form data's DataSet that represents the managed data of the GridView.

It's important that this DataTable have a primary key.

Business object

Now, you need to design a business object that will encapsulate the possible operations on your DataTable. The following is an example of a business object for the PEOPLE_LIST DataTable:

public class PeopleListBusinessObject
{
    // Reference to the people_list DataTable
    private FormData.PEOPLE_LISTDataTable peopleListTable;

    /// <summary>
    /// Constructor that affects the reference of the peopleListTable
    /// </summary>
    /// <param name="peopleListDataTable">La référence dont vous avez besoin pour remplir votre
     /// objet métier</param>
    public PeopleListBusinessObject(FormData.PEOPLE_LISTDataTable
peopleListDataTable)
    {
        peopleListTable = peopleListDataTable;
    }

    /// <summary>
    /// Rrenvoie la table de données de la liste des personnes qui
    /// sera utilisée par ObjectDataSource
    /// </summary>
    /// <returns>A PEOPLE_LISTDataTable</returns>
    public FormData.PEOPLE_LISTDataTable GetPeopleList()
    {
        return peopleListTable;
    }

    /// <summary>
    /// Insère / met à jour des données dans le DataTable PEOPLE_LIST
    /// qui sera utilisé par le ObjectDataSource
    /// </summary>
    /// <param name="ID">The Person ID</param>
    /// <param name="firstName">Prénom de la personne</param>
    /// <param name="lastName">Nom de famille de la personne</param>
    public void UpdateOrInsertPerson(int ID, string firstName, string lastName)
    {
        FormData.PEOPLE_LISTRow tempRow = peopleListTable.Rows.Find(ID) as FormData.PEOPLE_LISTRow;!

        if (tempRow != null)
        {
            // we found a row, so we are updating it
            tempRow.FIRSTNAME = firstName;
            tempRow.LASTNAME = lastName;
        }
        else
        {
            // we did not find a row with the requested ID, it means we are inserting
            peopleListTable.Rows.Add(firstName, lastName);
        }
    }

    /// <summary>
    /// Deletes a person from the PEOPLE_LISTDataTable, will be used by the
    /// ObjectDataSource
    /// </summary>
    /// <param name="ID"></param>
    public void DeletePerson(int ID)
    {
        FormData.PEOPLE_LISTRow tempRow = peopleListTable.Rows.Find(ID) as FormData.PEOPLE_LISTRow;
        peopleListTable.Rows.Remove(tempRow);
    }
}

ObjectDataSource

The next step is to put an ObjectDataSource in your page and link it to your business object. To do this:

  1. Drag and drop the ObjectDataSource onto your web form in Design view.

  2. Click Configure Data Source.

  3. Choose the business object you created earlier.

  4. Select the appropriate method for each data operation:

    ✏️ Note: The GridView doesn't have the integrated functions to support Insert operations, so we'll use the UPDATE section in order to insert items. Do not choose any method for the INSERT section.

  5. Click Finish.

  6. Add the ObjectCreating event to manage the ObjectDataSource creation with the business object class.

    protected void PeopleListObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
        {
        // Attach the instance of the PEOPLE_LIST to the
        // PeopleListObjectDataSource
        e.ObjectInstance = new PeopleListBusinessObject(formData.PEOPLE_LIST);
        }

GridView

  1. Drag and drop a GridView onto the web form, choose the ObjectDataSource you created in the previous step as the DataSource, and check the operations you want enabled in your GridView. (See Enabling insertion in GridViews below for instructions on how to enable inserting in the GridView.)

  2. Set the DataKeyNames property if you want to enable editing and inserting in your GridView.

  3. It's recommended to set the ID field to read-only by clicking Edit columns... and setting the ID bound field's ReadOnly property to True.

Enabling insertion in GridViews

The workaround for insertion in a GridView is to add a button to manually insert a new row in the PEOPLE_LIST DataTable when someone clicks on it. To do this:

  1. Drag and drop a LinkButton under your GridView, then double-click on the LinkButton and add the following code to it:

    protected void InsertPerson_Click(object sender, EventArgs e)
     {
     // Insert a new row in the PEOPLE_LIST data table, change the
    EditIndex so
     // that we can fill in the new person's information and then rebind the
     // PEOPLE_LIST.
     formData.PEOPLE_LIST.AddPEOPLE_LISTRow("", "");
     PEOPLE_LIST.EditIndex = formData.PEOPLE_LIST.Rows.Count - 1;
     PEOPLE_LIST.DataBind();
     }
  2. This code doesn't support paging and sorting, so you'll have to detect the new EditIndex yourself if you want to enable paging and sorting in your GridView. This is because the EditIndex is not necessarily the PEOPLE_LIST.Rows.Count - 1 when you use these functions.

Updating your form data OnRowDeleted and OnRowUpdated

The last step is to define the OnRowDeleted and OnRowUpdated events for the GridView to update your form data upon every change. To do this, add the following code:

protected void PEOPLE_LIST_RowUpdated(object sender,
 GridViewUpdatedEventArgs e)
    {
        // When we update a row, we save the data
        SaveFormData(formData);
    }
protected void PEOPLE_LIST_RowDeleted(object sender,
 GridViewDeletedEventArgs e)
    {
        // When we delete a row, we save the data
        SaveFormData(formData);
    }

Customizing the FormData schema and separating form data from the form parameters

Customizing the FormData schema

In the previous section, you were instructed to put all the parameters and your form data in the same DataTable named Table1, but you can always use another DataSet name and another DataTable name if you want.

Note, however, that if you don't use the Table1 name for the DataTable, you need to use the PARAMS_XPATH parameter in all of your actions to instruct EFORMASPX and WorkflowPage where they should locate the parameters and the form data. This parameter is an XPath that points to this table. If you name your DataSet MyFormData and you name your default table MyParameters, the PARAMS_XPATH parameter should contain MyFormData/MyParameters, otherwise your process won't work.

When you define a custom schema this way, you also have to include a template FORM_DATA file in your project with the same empty schema:

<?xml version="1.0" encoding="utf-8" ?>
<MyFormData>
    <MyParameters>
    </MyParameters>
</MyFormData>

Separating form data from the form parameters

You can separate the action parameters from your form data, but you'll have to use the complete XPath location of your form data as the parameter name if you want to use them as action parameters. For example, if you have a field called REQUEST_FIRSTNAME in your web form and you want to put this data in another table (e.g.MyData), you would declare the parameter with the name /MyFormData/MyData/REQUEST_FIRSTNAME.

Note that if you use custom XPath expressions for your action parameter names, you'll also need to upload a custom FORM_DATA to your process that contains empty nodes for those parameters.

The following is a complete example of a process action that would use separate DataTables for the parameters and for the form data:

<?xml version="1.0" encoding="utf-8" ?>
<MyFormData>
    <MyParameters>
    </MyParameters>
    <MyData>
        <REQUEST_NUMBER />
        <REQUEST_FIRSTNAME />
        <REQUEST_LASTNAME />
        <REQUEST_DATE />
    </MyData>
</MyFormData>

DataSet structure

Action parameters

Using web UserControls in web forms

If you're planning on using UserControls to encapsulate certain parts of your web forms that will be repeated from one web form to another, you can use them with WorkflowGen.My.

You must have the following information to use them correctly:

  • For each control that is present in the UserControl, WorkflowGen.My will create a node in Table1 of the FormData dataset, prefixed by the ID of the user control in the page.

    For example, if you have a UserControl in your page with the ID HEADER_UC that contains a TextBox with the ID FIRST_NAME, the field will be accessible through the FormData with the name HEADER_UC.FIRST_NAME.

  • If you want to access this field through a WorkflowGen parameter, you'll have to use this convention to access the field. If you want to use a macro that sends the user’s first name in the HEADER_UC.FIRST_NAME field , you'll have to use this name as the parameter name in the WorkflowGen action parameter.

  • If you want to set a field situated in a UserControl to be considered as required by WorkflowGen.My, you still use the above convention to list this field in the FORM_FIELDS_REQUIRED parameter.

Last updated