Best Practices for Code Customization

While every customization and every need will be different, we have compiled a list of best practices that we recommend you follow.  These best practices will help you add customizations at the most appropriate location for the vast majority of needs.

What is Code tabs and how they help to add customization?

Where to add customizations?

What events to handle or methods to override?

Do Not Forget the IsPostBack Flag

Code Tabs

The Code Tabs in the Design Mode show the most commonly customized functions for the currently selected control in the Page Layout Spreadsheet.  For data bound controls such as a textbox displayed on the Add or Edit page, the Code Tabs show the Set, Get and Validate functions.  Similarly, selecting a button control shows its Click function, or selecting a dropdown shows its Dropdown function. Zooming out to the Table Control or the Record Control level will display functions such as CreateWhereClause,LoadData, DataBind and SaveData that are relevant to the selected controls. 

The Code Tabs allow editing of the functions by simply modifying them within the code editor. Modification of the code in the code editor automatically places a copy of the function in Section 1, and changes the font (makes it bold) of method name to easily indicate that the function has been modified.  At any point, clicking Restore will remove all the customizations and replace the function to its originally created code.  Subsequent builds will also preserve the customizations, and the changes made will never be overwritten, unless explicitly restored.

Within the Code Tab, the functions can be modified:

  1. By a line or two of the code;

  2. Changed to call the base function followed by additional lines of code; or

  3. Changed in its entirety based on your needs.

All changes to the code will be preserved and never overwritten no matter how many times you rebuild your application. The code is well documented to ensure that it is easy to follow and change if the need arises.

Detailed documentation about the function can be viewed by clicking the Docs button. The Docs button displays customized documentation for the specific function being reviewed.  The documentation describes the function, displays a calling hierarchy, shows how to customize and even displays a link to the underlying Microsoft .NET Framework control documentation when relevant.  The documentation can also be opened in a separate window so the code and the documentation can be viewed side-by-side if necessary.

 

Where to add customizations?

In general you can handle events or override methods at the page class level, at the table or record control level or even at an individual field control level.  For example, if you want to initialize the Country field to United States on a page, you can handle the PreRender event at the Page level, at the Record Control level or even the Country field level.  So what is the best place to add this code customization?

We recommend that you add your code customizations at the Record Control class level.  There are many reasons for this including:

See Also

Where to add customizations?

What events to handle or methods to override?

Do Not Forget the IsPostBack Flag

 

What events to handle or methods to override?

Some code customizations can be performed by handling the following events.  For each event, please pay special attention to the recommended class for handling the event.

DataBind Method

If you want to initialize a value, format a value, compute a value, hide or display a control based on some criteria, or change the look and feel of a control, we recommend you override the DataBind method for the Record Control class to make these customizations.  Remember to first call the MyBase.DataBind or base.DataBind to perform the underlying functions.

C#

public override void DataBind()

{

     base.DataBind();

 

     // Pre-initialize Title if blank

     if (this.ContactTitle.Text == "")

     {

          this.ContactTitle.Text = "Manager";

     }

}

Visual Basic .NET

Public Overrides Sub DataBind()

     MyBase.DataBind()

 

     ' Pre-initialize Title if blank

     If Me.ContactTitle.Text = "" Then

          Me.ContactTitle.Text = "Manager"

     End If

End Sub

Validate Method

Two specific validators can be automatically created based on the values selected in the Property Sheet:

In addition to these two validators, you can add your own custom validation logic very easily by overriding the Validate method at the Record Control class level.  The Validate method is called from the SaveData method before the GetUIData method is called.  Note that GetUIData retrieves the data from the user interface controls and converts it into the internal format required for saving.  For example, a date specified as a text string “12-1-2006” will be converted to a Date object with the appropriate month, day and year values.

To validate fields from the user interface controls, use their user interface controls to validate the data.  Validation errors must be grouped together and an exception thrown to abort the saving of data.

C#:

public override void Validate()

{

     base.Validate();

 

     //  Additional Validation

     if (this.State.Text != "CA")

     {

          throw new Exception("State must be CA (California).");

     }

}

Visual Basic .NET:

Public Overrides Sub Validate()

     MyBase.Validate()

 

     '  Additional Validation

     If Me.State.Text <> "CA" Then

          Throw New Exception("State must be CA (California).")

     End If

End Sub

GetUIData Method

If you want to make changes to the values before they are saved, we recommend you override the GetUIData method at the Record Control class level.  The GetUIData method is called from within SaveData and retrieves all of the values from the user interface controls prior to the data being saved in the database.  You can even set fields that are not displayed to the user, such as audit control fields.

C#:

public override void GetUIData()

{

     base.GetUIData();

 

     // Set additional field values here

     this.DataSource.LastUpdateDate = DateTime.Now();

}

Visual Basic .NET:

Public Overrides Sub GetUIData()

     MyBase.GetUIData()

 

     ' Set additional field values here

     Me.DataSource.LastUpdateDate = DateTime.Now()

End Sub

CommitTransaction Method

If you want to access the Id of the record or send an email after the record is saved, we recommend you override the CommitTransaction method at the page class level.  The CommitTransaction is defined in the BasePage class and calls DBUtils.CommitTransaction.  If you override the CommitTransaction method, make sure to call the base CommitTransaction.

C#:

public override void CommitTransaction(object sender)

{

     // Call the base CommitTransaction

     base.CommitTransaction(sender);

 

     // Use the Me.CustomersRecordControl.GetRecord() to retrieve the record that was just updated.

     CustomersRecord myRecord;

     myRecord = this.CustomersRecordControl.GetRecord();

 

     // Send a confirmation email with the CustomerId.

     try

     {

          BaseClasses.Utils.MailSender email = new BaseClasses.Utils.MailSender();

          email.AddFrom("[email protected]");

          email.AddTo("[email protected]");

          email.AddBCC("[email protected]");

          email.SetSubject("Confirmation");

          email.SetContent("Thank you for your request. Your Customer Id is: " + myRecord.CustomerID);

          email.SendMessage();

 

     }

     catch (System.Exception ex)

     {

          // Report the error message to the user.

          Utils.RegisterJScriptAlert(this, "UNIQUE_SCRIPTKEY", "Could not send an email. Error was: " + ex.Message);

     }

}

Visual Basic .NET:

Public Overrides Sub CommitTransaction(ByVal sender As Object)

     ' Call the base CommitTransaction

     MyBase.CommitTransaction(sender)

 

     ' Use the Me.CustomersRecordControl.GetRecord() to retrieve the record that was just updated.

     Dim myRecord As CustomersRecord

     myRecord = Me.CustomersRecordControl.GetRecord()

 

     ' Send a confirmation email with the CustomerId.

     Try

          Dim email As New BaseClasses.Utils.MailSender

          email.AddFrom("[email protected]")

          email.AddTo("[email protected]")

          email.AddBCC("[email protected]")

          email.SetSubject("Confirmation")

          email.SetContent("Thank you for your request. Your Customer Id is: " & myRecord.CustomerID)

          email.SendMessage()

 

     Catch ex As System.Exception

          ' Report the error message to the user.

          Utils.RegisterJScriptAlert(Me, "UNIQUE_SCRIPTKEY", "Could not send an email. Error was: " & ex.Message)

     End Try

End Sub

Redirect Method

If you want to modify the URL before redirection, we recommend you override the CommitTransaction method (described above) at the Page class level and redirect after the commit.  The SaveButton_Click method calls the SaveButton_Click_Base method on the page class to save and commit the database records.  Once the save and commit happens, the SaveButton_Click_Base redirects to the URL specified in the Property Sheet.  As such, you cannot add code after the call to the SaveButton_Click_Base method in SaveButton_Click because once the redirect happens, control will not return back to the SaveButton_Click method.

C#:

public void SaveButton_Click(object sender, EventArgs args)

{

     SaveButton_Click_Base(sender, args);

     // Code below will never get executed since SaveButton_Click_Base will

     // redirect to another page.  To customize, either replace SaveButton_Click_Base

     // functionality here, or override CommitTransaction.

}

Visual Studio .NET:

Public Sub SaveButton_Click(ByVal sender As Object, ByVal args As EventArgs)

     SaveButton_Click_Base(sender, args)

     ' Code below will never get executed since SaveButton_Click_Base will

     ' redirect to another page. To customize, either replace SaveButton_Click_Base

     ' functionality here, or override CommitTransaction.

End Sub

CreateWhereClause Method

If you want to modify the query before it is executed, the best place to do it is by overriding the CreateWhereClause method at the Table Control level for Table Report pages, and the CreateWhereClause method at the Record Control class level for the Add, Edit, and Show Record pages.  Please note that CreateWhereClause is not available at the Page level since each table and record control has its own query.  You can override the CreateWhereClause and add your own WHERE clause.

C#:

protected override BaseClasses.Data.WhereClause CreateWhereClause()

{

     WhereClause wc;

 

     wc = base.CreateWhereClause();

 

     if (IsNothing(wc))

     {

          // Get a blank WHERE clause if the base function returned null.

          wc = new WhereClause();

     }

 

     wc.iAND(CustomersTable.City, Starts_With, "Mountain");

}

Visual Basic .NET:

Protected Overrides Function CreateWhereClause() As BaseClasses.Data.WhereClause

     Dim wc As WhereClause

 

     wc = MyBase.CreateWhereClause()

 

     If IsNothing(wc) Then

          ' Get a blank WHERE clause if the base function returned null.

          wc = New WhereClause

     End If

 

     wc.iAND(CustomersTable.City, Starts_With, "Mountain")

End Function

The CreateWhereClause method creates a WhereClause object that contains clauses joined together using AND, OR and NOT operators.  The default CreateWhereClause created by Iron Speed Designer combines three different types of clauses to compose the entire WHERE clause for the query.

Static Clause: The static clause defined at design time by the developer. Note that the static clause may contain multiple clauses including clauses that support role-based security and custom function calls.

Filter Clause(s): The static clause is ANDed with one or more filter settings such as from a dropdown list or date filter textbox. For dropdown list boxes, an Equals comparison operator is used. For textbox filters, you can specify the operator such as greater-than, less-than, etc.

Search Clause: This entire clause is ANDed again with a search clause that compares the search string with the various search fields. The search clause is composed of a series of OR clauses that checks whether any of the search fields contain the text value entered in the search text box. You can configure the search fields and the operator it uses (default is Contains) via the Query Wizard in the Data Sources tab.

In pseudo-code, this clause looks like:

[Psuedo-code]

     StaticClause AND

     FilterField1 = SelectedValue1 AND

     FilterField2 = SelectedValue2 AND

     ...more filter clauses... AND

     (SearchField1 Contains SearchText OR

     SearchField2 Contains SearchText OR ...)

The CreateWhereClause must return a WhereClause object. One of the main reasons Iron Speed Designer uses a WhereClause object instead of a Where string is to insulate you from knowing the syntax based on the specific database product you are using. Oracle, Microsoft SQL Server, MySQL and Microsoft Access each have a slightly different syntax for the SQL they use. Another reason to use the WhereClause is to specify whether a clause compares the raw value or the display foreign key value. This is described below.

You can override the CreateWhereClause in the derived class such as ProductsRecordControl. You can either call the base CreateWhereClause and add your own clauses to it, or completely replace the call.

The iAND and iOR take three required parameters and up to two optional arguments. It is important to understand the two optional arguments since they provide finer control over the WHERE clause.

Column: The column object is the first parameter to iAND and iOR. The column object is used to determine the actual column name. Sometimes the column name may contain spaces and other characters not easily specified in code, so by passing the column object, the WhereClause can determine and use the correct name. The format of the first parameter is typically TableTable.Field. For example if you have a field called FirstName in a table called Customers, the column object can be specified by using CustomersTable.FirstName. If a View or a Query is used, the suffix is changed from Table to View or Query respectively. For example, the FirstName field in vwActiveCustomers view would be specified as vwActiveCustomersView.FirstName.

Operator: The operator used by the WHERE clause. Operator can be one of the following:

Value: The value is always specified as a string. Date and other objects must be converted to a string appropriately.

(Optional) ExpandForeignKey: If the Column or Field is a foreign key field, then sometimes it is convenient to pass the Display Foreign Key As (DFKA) value instead of the Id of the foreign key. For example, if there is a CustomerId field in the Orders table, you can create a clause to compare Orders.CustomerId = "3" or Orders.CustomerId = "Tom Jones".

The ExpandForeignKey parameter must be True if using the DFKA value such as “Tom Jones” in the above example. If the ExpandForeignKey is True, the SELECT statement will contain a JOIN with the foreign key table and the actual WHERE clause will compare the DFKA field with the value. For example:

SELECT * FROM Orders, Customers

WHERE Orders.CustomerID = Customers.CustomersID AND

Customers.Name = "Tom Jones"

If ExpandForeignKey is FALSE, then the SELECT statement created is a simple statement on a single table as shown below:

SELECT * FROM Orders WHERE Orders.CustomerID = "3"

By default, all static clauses specified on the Query Wizard will expand the foreign key (set to True). By default all filter clauses based on the end-user filter selection will not expand the foreign key (set to False) since in most cases we will have the Id values available in the dropdown

(Optional) IsCaseSensitive:  If the IsCaseSensitive parameter is True, the comparison is case sensitive.  For the Search filter, this setting can be specified via the Query Wizard (Data Sources tab) or the Property Sheet.  For the static search filter clause, the IsCaseSensitive is set to False, while the parameter is set to True for a dynamic filter dropdown control.

To add parenthesis in a WHERE clause, you can simply create another new WHERE clause and AND or OR it with the earlier clause. The pseudo-code below shows you how to specify the following clause:

A AND B AND (C OR D)

You can do the following:

C#:

WhereClause wc1 = new WhereClause();

wc1.iAND(A);

wc1.iAND(B);

 

// Create OR portion of the clause

WhereClause wc2 = new WhereClause();

wc2.iOR(C);

wc2.iOR(D);

 

// AND the OR portion of the clause.

wc1.iAND(wc2);

Visual Basic .NET:

Dim wc1 As WhereClause = New WhereClause

wc1.iAND(A)

wc1.iAND(B)

 

' Create OR portion of the clause

Dim wc2 As WhereClause = New WhereClause

wc2.iOR(C)

wc2.iOR(D)

 

' AND the OR portion of the clause.

wc1.iAND(wc2)

PopulateFilter Method

If you want to pre-initialize a filter with a URL, cookie, or session value, the best place to do this is to override the PopulateFilter method for the specific field filter at the Table Control class.  You can call the base method and then set the current value.  You must also override the CreateWhereClause method to add the initial setting of the filter to the WHERE clause.  You can also customize the WHERE clause used by the Populate Filter to limit the type of records retrieved, such as only Active customers.  For additional customization, you can even replace the entire PopulateFilter method with your own method.

C#:

protected override WhereClause CreateWhereClause()

{

     //  Call the MyBase.CreateWhereClause()

     WhereClause wc = base.CreateWhereClause();

     //  If MyBase.CreateWhereClause() returns nothing then create a new

     //  instance of WhereClause

     if ((wc == null))

     {

          wc = new WhereClause();

     }

     //  Add a WHERE clause based on the querystring

     string country = this.Page.Request.QueryString("Country");

     if (!this.Page.IsPostBack && country != "")

     {

          wc.iAND(CustomersTable.Country, EqualsTo, country);

     }

     return wc;

}

 

// -----------------------------------------------------------------------------------------------------------

protected override void PopulateCityFilter(string selectedValue, int maxItems)

{

     string country = this.Page.Request.QueryString("Country");

     //  The selected value only will be set when the page loads for the first time.

     if (!this.Page.IsPostBack && country != "")

     {

          base.PopulateCityFilter(country, maxItems);

     }

     else

     {

          base.PopulateCityFilter(selectedValue, maxItems);

     }

}

Visual Basic .NET:

Protected Overrides Function CreateWhereClause() As WhereClause

      ' Call the MyBase.CreateWhereClause()

     Dim wc As WhereClause = MyBase.CreateWhereClause()

 

      ' If MyBase.CreateWhereClause() returns nothing then create a new

      ' instance of WhereClause

     If (IsNothing(wc)) Then

          wc = New WhereClause

     End If

 

     ' Add a WHERE clause based on the querystring

     Dim country As String = Me.Page.Request.QueryString("Country")

     If Not (Me.Page.IsPostBack) AndAlso country <> "" Then

          wc.iAND(CustomersTable.Country, EqualsTo, country)

     End If

 

     Return wc

End Function

 

‘ -----------------------------------------------------------------------------------------------------------

Protected Overrides Sub PopulateCountryFilter(ByVal selectedValue As String, ByVal maxItems As Integer)

 

          Dim country As String = Me.Page.Request.QueryString("Country")

 

     ' The selected value only will be set when the page loads for the first time.

     If Not (Me.Page.IsPostBack) AndAlso country <> "" Then

          MyBase.PopulateCountryFilter(country, maxItems)

     Else

          MyBase.PopulateCountryFilter(selectedValue, maxItems)

     End If

End Sub

See Also

Where to add customizations?

What events to handle or methods to override?

Do Not Forget the IsPostBack Flag

 

Do Not Forget the IsPostBack Flag

By far the most common mistake made by .NET developers is to ignore the IsPostBack flag when handling an event or overriding a method.  Note that each method and event handler will be executed at least twice, once when the page is being displayed and once when the user presses a button to save or go to another page.  If you set AutoPostBack on a field to handle a TextChanged or SelectedItemChanged event, then each of the event handlers will be executed three or more times.  When you add custom logic, make sure you check for the IsPostBack flag to decide whether to execute your code the first time the page is displayed, or only during a postback such as a button click or always.

C#:

if (!this.Page.IsPostBack)

{

     //  This code executes when the page is first loaded.

}

else

{

     //  This code executes during a button click or other postback.

}

//  This code executes in all cases.

Visual Basic .NET:

If Not (Me.Page.IsPostBack) Then

     ' This code executes when the page is first loaded.

Else

     ' This code executes during a button click or other postback.

End If

' This code executes in all cases.

See Also

Where to add customizations?

What events to handle or methods to override?

Do Not Forget the IsPostBack Flag