Using jQuery UI Along with ASP.NET Web Forms

I like the jQuery UI objects, particularly its Button and Datepicker widgets. But what if you want to use them on a normal ASP.NET web form? Back in the early 2000’s, I wrote the VBTrain Graphical Button control to generate all the JavaScript needed to swap out graphics for the up, down, over, and disabled states. But now jQueryUI does all of that for us. Making the two technologies work together is pretty simple. Here is what the input control looks like:

<input id="SubmitBtn" runat="server" value="Submit" type="submit" />

The only change we made was the runat=”server” part. This is what allows us to recognize it in server-side code.

The next step is to add jQuery UI. We do this with the appropriate .js and css files loaded in the head of the file. We are using the Start theme, which is why the reference is “css/start/.”

<link href="css/start/jquery-ui-1.8.17.custom.css" rel="stylesheet" type="text/css" />
<script src="scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.8.17.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        $("input:submit").button();
    });
</script>

You might recognize the “load” function from the previous article. Once the page is fully loaded, it executes. We then use a new type of selector: “input:submit”. This means that we find all input controls that have a type of submit. We then call the button() method on each one. This is what loads all the appropriate graphics and associated scripts. If there had been 10 input buttons (of type submit) on the page, that one line would have configured all of them.

The last step is handling the click in our “code behind” file. That handler is shown below.

Private Sub SubmitBtn_ServerClick(sender As Object, e As System.EventArgs) Handles SubmitBtn.ServerClick
    ' take action here
End Sub

Those of you who are used to normal ASP.NET buttons will see that the event is a bit different: ServerClick rather than the normal Click. But the functionality is exactly the same.

.NET Structures

When I am programming, I often into situations where I need to pass around related data of different types. In some languages, the best you can do is create some sort of array and then stuff the data into elements of the array and hopefully be able to convert the data in and other of strings. But languages like Visual Basic.Net have a more powerful option, a Structure. Like a full-fledged Class, a structure has its own properties and can even have its own methods.

For a custom project I was working on, I needed to read a bunch of related files and keep track of them until I wrote their data to the database. At that time, I needed to move the “successful” files to a “Success” directory, the problem files to an “Error” directory, and those that needed to be skipped to a “Skip” directory. To do this efficiently, I needed to keep track of a reference to the original file and the path to move it to. For that, I used the structure below:

    Friend Structure MoveFileStructure
        Friend FileInfoId As FileInfo
        Friend TargetPath As String
    End Structure

The structure has two properties. The FileInfoId is of type FileInfo and is a reference to the file itself. The TargetPath is the complete path to where I want to move the file later. I then carry that data around the program until I needed to work with the files. For example, the method below actually moved the files to their new location.

    Friend Sub MoveFiles(ByRef moveFileList As List(Of MoveFileStructure))
        Dim newFilePath As String
        Dim structureId As MoveFileStructure

        For Each structureId In moveFileList
            newFilePath = structureId.TargetPath
            If File.Exists(newFilePath) Then
                File.Delete(newFilePath)
            End If
            Try
                structureId.FileInfoId.MoveTo(newFilePath)
            Catch ex As Exception
                ' don't do anything
            End Try
        Next
        moveFileList.Clear()
    End Sub

We pass in a Generic List (another great concept in .NET) of MoveFileStructure objects. So each of these are instances of the structure, which means they have FileInfoId and TargetPath properties. We then loop through the list. We read the TargetPath and delete any existing file with that path. We then use the MoveTo method of our FileInfo object to move the file to the new location. We put the code into a Try-Catch block just in case the file got listed twice and has already been moved. Finally, we clear our list since the files have now been moved.

SCORM and Closing the Browser Window: Silverlight

As discussed in the previous post on ActionScript, we needed to change our logic somewhat to support showing our Exam Engine and Training Studio content in a Tracker.Net 5 (or other LMS) frame. Since Exam Engine has both a Flash/Flex implementation and a Silverlight implementation (note: version 4 is now HTML/JavaScript), I thought I would show the Silverlight implementation in this article. The logic is the same as with the previous article. We want to initiate the SCORM messages when the user clicks the Exit button and then be sure not to resend them when the window closes. In the popup window situation, however, the SCORM messages need to be sent when the user closes the browser window without clicking the Exit button.

The Exit button code is shown below.

Private Sub ExitBtn_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    Dim examRefId As ExamEngineSettings = ExamReferenceId
    Dim exitMessage As String = examRefId.ReadExamSetting("ExitMessage", "")
    Dim okToExit As Boolean = True

    If exitMessage <> "" Then
        okToExit = Browser.HtmlPage.Window.Confirm(exitMessage)
    End If
    If okToExit = True Then
        ' Changed the following since this did not work if the content is in an iFrame.
        ' Instead, call ExitExam directly and set a variable to skip the exit on the
        ' subsequent closing of the browser window
        Dim exitSuccess As Boolean = examRefId.ExitExam()

        If exitSuccess = True Then
            examRefId.AlreadyExited = True
            Browser.HtmlPage.Window.Eval("window.close()")
        End If
    End If
End Sub

We again read our exit message and display that to the user via JavaScript. Notice how we can call the JavaScript confirm method directly from the Visual Basic code. If the user confirms exiting, we call the ExitExam method. This is where all the SCORM messages are sent. If that returns True, we set the AlreadyExited global variable to True and try to close the window.

As with the Flash/Flex implementation we registered the “onunload” browser event. In this case, we call ExitExamHandler as shown below. Notice that we check the same AlreadyExited variable before calling ExitExam. This avoids it being called twice if the user clicked the Exit button to close the window.

Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
    Me.RootVisual = New Page()
    HtmlPage.Window.AttachEvent("onunload", AddressOf ExitExamHandler)
End Sub

Public Sub ExitExamHandler(ByVal sender As Object, ByVal e As EventArgs)
    Dim examRefId As ExamEngineSettings = ExamReferenceId

    If examRefId.AlreadyExited = False Then
        ExamReferenceId.ExitExam()
        examRefId.AlreadyExited = True
    End If
End Sub

Visual Basic in Reports

I’ve been spending a great deal of time building reports in Visual Studio for our new Tracker Reports product. While SQL Server Reporting Services has a number of great features, one of my favorites is the fact that you can embed Visual Basic code in the report itself. This can be extremely helpful with complex logic. For example, the completion status of a lesson or course can be expired, critical, or alert when it has an expiration date. Here is what the function looks like:

Public Function DetermineCompletionStatus(ByVal completionStatusObj As Object, _
    ByVal expirationDateObj As Object, ByVal currentDays_CriticalObj As Object, _
    ByVal currentDays_AlertObj As Object, Optional ByVal isCourse as Boolean = True) As String

    Dim completionStatus As String
    Dim expirationDateId, currencyCriticalDate, currencyAlertDate As DateTime
    Dim currencyCriticalDays, currencyAlertDays As Integer
    Dim checkExpiration As Boolean = isCourse
    Dim defaultStatus as String = "Not Started"

    If isCourse = False Then
    	defaultStatus = "not attempted"
    End If

    If IsDBNull(completionStatusObj) = True Then
        completionStatus = defaultStatus
    Else
        completionStatus = CStr(completionStatusObj)
        If completionStatus = ""
        	completionStatus = defaultStatus
        End If
    End If

    Dim returnString As String = completionStatus

    If IsDBNull(expirationDateObj) = True OrElse expirationDateObj Is String.Empty OrElse _
        IsNothing(expirationDateObj) = True OrElse expirationDateObj.ToString = "" Then

        expirationDateId = DateTime.MinValue
    Else
        Try
            expirationDateId = Convert.ToDateTime(expirationDateObj)
        Catch ex As Exception
            expirationDateId = DateTime.MinValue
        End Try

        If expirationDateId > DateTime.MinValue Then
            If IsDBNull(currentDays_CriticalObj) = True OrElse IsNothing(currentDays_CriticalObj) = True OrElse _
                currentDays_CriticalObj.ToString = "" OrElse currentDays_CriticalObj.ToString = "-10" Then

                currencyCriticalDays = 0
                currencyCriticalDate = expirationDateId
            Else
                currencyCriticalDays = CInt(currentDays_CriticalObj)
                currencyCriticalDate = expirationDateId.AddDays(-(currencyCriticalDays))
            End If

            If IsDBNull(currentDays_AlertObj) = True OrElse IsNothing(currentDays_AlertObj) = True OrElse _
                currentDays_AlertObj.ToString = "" OrElse currentDays_AlertObj.ToString = "0" OrElse _
                currentDays_AlertObj.ToString = "-10" Then

                currencyAlertDays = 0
                currencyAlertDate = expirationDateId
            Else
                currencyAlertDays = CInt(currentDays_AlertObj)
                currencyAlertDate = expirationDateId.AddDays(-(currencyAlertDays))
            End If
        End If
    End If

    Select Case completionStatus.ToLower
    Case "completed", "passed"
	' expiration Date

	checkExpiration = True
    End Select

    If checkExpiration = True Then
        If expirationDateId <> DateTime.MinValue Then
            If (expirationDateId <= Now) Then
                Dim expiredString As String = "Expired"

                returnString = expiredString
            ElseIf (currencyCriticalDate <= Now) Then
                returnString = "Critical"
            ElseIf (currencyAlertDate <= Now) Then
                returnString = "Alert"
            End If
        End If
    End If

	returnString = StrConv(returnString, VbStrConv.ProperCase)
    Return returnString
End Function

Within the report itself, we can use an expression for the value for the “status” rather than linking to just a single column from our query. For example, here is an expression for the course status:

=Code.DetermineCompletionStatus(Fields!CompletionStatus.Value, Fields!ExpirationDate.Value, _
    Fields!CurrencyDaysFlag_Critical.Value, Fields!CurrencyDaysFlag_Alert.Value)

Notice how we begin the expression with Code to denote that we are using our own code rather than built-in operators.

Dictionaries in .NET

In the previous post, we looked at a question related to the Dictionary object in ActionScript. Here is some continued information on the equivalent object in .NET.

We are looking at a Silverlight version of Training Studio 2 right now (note: this was true when written. But we have since decided to have the next version be HTML and JavaScript). One advantage in .NET is that we can use Generics to specify the type of objects within the Dictionaries or ArrayCollections. For example, here is how the same objects are defined in ActionScript and Silverlight:

ActionScript

public static var masterContentArray:ArrayCollection = new ArrayCollection();
public static var pageArray:Dictionary;

Silverlight

Friend masterContentDictionary As New Dictionary(Of Integer, Dictionary(Of String, String))
Friend pageArray As Dictionary(Of String, String)

Note that we renamed masterContentArray to masterContentDictionaryand made the “page number” an explicit key. The direct equivalent to the ArrayCollection would be:

Friend masterContentArray As New List(Dictionary(Of String, String))

The advantage of Generics is that we can specify exactly what kind of objects are stored in the Dictionary or List objects. If we try to put an object of the wrong type in it, Visual Studio will tell us. When we pull objects out of the Dictionary or List, we can use them as that type right away, rather than having to use the Dictionary() syntax.