Monthly Archives: December 2011

Implementing custom sort for a Treeview

The Treeview Sort method applies a simple character sort to the node text. For sophisticated business applications this is often too simple and a custom sort is required in order to sort the nodes based upon the content and not just the character representation.

For example, consider this simple scenario with the following nodes:

  1. First Item
  2. Second Item
  3. Third Item
  4. Fourth Item

Sorting these nodes using a character sort will place them in alphabetical order; First, Fourth, Second, Third.

A custom sort is easy to implement and link to a Treeview control. In this example the Tag property of the Node control is used to store data for sorting the nodes. The NodeSort class is assigned to the TreeViewNodeSorter property before sorting the nodes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Public Class Form1
   Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        Dim nodParent As TreeNode
        Dim nodChild As TreeNode

        ' Add four nodes and assign the sequence number to the Tag property
       For intNode As Integer = 1 To 4
            nodParent = TreeView1.Nodes.Add(Choose(intNode, "First Node", "Second Node", "Third Node", "Fourth Node"))
            nodParent.Tag = intNode
        Next

        ' Add a series of dates to the 4th node
       For intAdjust As Integer = 1 To 49 Step 7
            Dim dteNode As Date = DateAdd(DateInterval.Day, intAdjust, Date.Now)
            nodChild = nodParent.Nodes.Add(String.Format("{0:dd MMM yy}", dteNode))
            nodChild.Tag = dteNode
        Next

        ' Assign the custom sort class and sort the TreeView
       With TreeView1
            .TreeViewNodeSorter = New NodeSorter
            .Sort()
        End With
    End Sub
End Class

Public Class NodeSorter
    Implements IComparer

    Public Function Compare(ByVal x As Object, ByVal y As Object) _
        As Integer Implements IComparer.Compare
        Dim tx As TreeNode = CType(x, TreeNode)
        Dim ty As TreeNode = CType(y, TreeNode)

        If TypeOf tx.Tag Is Integer And TypeOf ty.Tag Is Integer Then
            ' Sort numbers in ascending sequence
           Return String.Compare(tx.Tag.ToString, ty.Tag.ToString)
        ElseIf TypeOf tx.Tag Is Date And TypeOf ty.Tag Is Date Then
            ' Sort dates in descending sequence
           Return String.Compare(String.Format("{0:yyyymmdd}", CDate(ty.Tag)), String.Format("{0:yyyymmdd}", CDate(tx.Tag)))
        Else
            ' Sort everything else alphabetically in ascending sequence
           Return String.Compare(tx.Text, ty.Text)
        End If

    End Function
End Class

Note that the dates are sorted in descending sequence simply by swapping the X and Y nodes and comparing Y with X instead of X with Y.

Using jQuery AutoComplete with JSON Data

The Microsoft AJAX Toolkit seems have been overtaken by jQuery these days, so I thought that it was about time I investigated what it could do and how it worked. For one of the projects that I’ve got on the go the AutoComplete widget that is part of the jQuery UI looked useful, especially as it can be linked to a remote datasource. The data for this project is stored in a SQL Server database, so I needed to prove that the AutoCompete widget would work with this.

After digging around it looked to me as though I would need to implement a generic handler that jQuery could call from the client and return some JSON data. The DataContractJsonSerializer class appeared to provide the mechanism for returning the data.

Step 1 was to create the generic handler. In my VS2010 project I added a folder called Soap and within that I created a new generic handler that I called SoapTest.ashx. The code that I wrote to begin with always returns 6 items, with the text entered by the user followed by a counter. All I wanted to achieve at this point was to make sure that I could return some data to the client.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Imports System.Web
Imports System.Web.Services
Imports System.Runtime.Serialization

Public Class SoapTest
Implements System.Web.IHttpHandler

Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
context.Response.ContentType = "application/json"
Dim Term As String = String.Empty

If Not context.Request.QueryString("term") = String.Empty Then Model = context.Request.QueryString("term")

Dim DataList As List(Of AutoCompleteData) = New List(Of AutoCompleteData)
For ItemCount As Integer = 1 To 6
Dim DataItem As New AutoCompleteData
DataItem.label = String.Format("{0} - {1}", Term, ItemCount)
DataItem.value = String.Format("{0} - {1}", Term, ItemCount)
DataList.Add(DataItem)
Next

Dim Serialiser As Json.DataContractJsonSerializer = New Json.DataContractJsonSerializer(DataList.GetType())

Serialiser.WriteObject(context.Response.OutputStream, DataList)
End Sub

ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property

End Class
''' <summary>
''' AutoCompleteData class for serialising Json data
''' </summary>
''' <remarks>
''' The properties must be lowercase otherwise the jQuery code does not handle them correctly.
''' </remarks>
<DataContract()>
Public Class AutoCompleteData
<DataMember()> Public Property label As String
<DataMember()> Public Property value As String
End Class

Testing this code is simple. Run the project and change the URL to http://localhost:51727/Soap/SoapTest.ashx. Note that the port number when you run the project might not be 51727. If everything is working the function will return some JSON data that looks like this:

[{“label”:” – 1″,”value”:” – 1″},{“label”:” – 2″,”value”:” – 2″},{“label”:” – 3″,”value”:” – 3″},{“label”:” – 4″,”value”:” – 4″},{“label”:” – 5″,”value”:” – 5″},{“label”:” – 6″,”value”:” – 6″}]

Of course, the code is expecting to receive the data that the user has typed into the AutoComplete field, passed in via the QueryString ‘term’ item. Changing the URL to http://localhost:51727/Soap/SoapTest.ashx?term=abc will result in JSON data like so:
[{“label”:”abc – 1″,”value”:”abc – 1″},{“label”:”abc – 2″,”value”:”abc – 2″},{“label”:”abc – 3″,”value”:”abc – 3″},{“label”:”abc – 4″,”value”:”abc – 4″},{“label”:”abc – 5″,”value”:”abc – 5″},{“label”:”abc – 6″,”value”:”abc – 6″}]

Having established that the handler is working I then needed to hook this up to a control on the client with jQuery. A input field is required for this, and with ASP.Net 4.0 it is now very easy to link ASP controls into the Java script by making use of the new ClientIDMode property. Setting ClientIDMode=”Static” generates an HTML input field with the same ID as the ASP control which can therefore be used to hook the control into jQuery.

The server side control therefore looks like this:

Enter Text: <asp:TextBox ID=”TBAutoComplete” runat=”server” ClientIDMode=”Static”></asp:TextBox>

And the associated jQuery call like this:

1
2
3
4
5
6
7
8
9
10
&lt;script type="text/javascript"&gt;
$(function () {

$("#TBAutoComplete").autocomplete({
source: "/Soap/SoapTest.ashx",
minLength: 2,
dataType: "json"
});
});
&lt;/script&gt;

This all seemed to work quite nicely. Next I need to extend this to link the AutoComplete field to a drop-down list and pass an additional parameter to the Soap handler. Finally I’ll need to hook up the Soap handler to my database but that should be the easy bit.