Archive for the 'Programming' Category

Build a better submit button in ASP.NET

Posted in Programming, ASP.NET on June 8th, 2005

Two routine problems you have with a web application. Either the user clicks a button and they didn’t mean too or they click a button more than once, and meant to.

You can solve the first problem by asking for a confirmation.

Say you have a Delete button a web form. And you can the user to be prompted with a “Are you sure you want to do this?” confirmation box before proceeding. Simply put this code in the page_load.

btnSubmit.Attributes.Add(”onclick”, _
“return confirm(’Are you sure you want to delete this page? ” _
& “This cannot be undone.’);”)

The second problem I was having with some users, who shall remain unnamed, clicking the submit button a little too aggressively. Their machine being a little slow, they thought pressing the button again (and again) would make it go faster. This caused havok with one of my web applications by processing an order too many times. So I sought out to write a button that could be disabled on the client-side before the postback began.

I initially tried just calling this.disabled = true when the button was pressed. This caused a problem since a disabled button wouldn’t allow a postback. So like any good programmer, I Googled this and didn’t not have to look to far for like-minded people with a good solution.

In order for a disabled button to work you would have to force postback by adding

__doPostBack('btnProcess','');

to the onClick event. Of course this is better done by calling Page.GetPostBackEventReference(Me.btnProcess) on your Page_Load.

And to get validation to work properly the standard validation script

if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();

has to be replaced with

if (typeof(Page_ClientValidate) == 'function')
{ if (Page_ClientValidate() == false) { return false; }}

The final result for my solution was the following:

'This is the code for a Single-Click button
'Prevents a user from submitting a page more than once.
Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder
'This forces the page validation, if any, to execute.
sb.Append("if (typeof(Page_ClientValidate) == 'function') { ")
sb.Append("if (Page_ClientValidate() == false) { return false; }} ")
'Changes the text of the button. Gives the user a processing message.
sb.Append("this.value = 'Processing...';")
'Prevent the button from being pressed a second time.
sb.Append("this.disabled = true;")
'Forces the page to postback.
sb.Append(Me.Page.GetPostBackEventReference(Me.btnProcess))
sb.Append(";")
Me.btnProcess.Attributes.Add("onclick", sb.ToString())

And of course if you want to combine these two functions you can simply use this code. This will ask the user for confirmation and only disable the button if the user clicks ‘yes’.

'This is the code for a Single-Click button
'Prevents a user from submitting a page more than once.
Dim sb As System.Text.StringBuilder = New System.Text.StringBuilder
'Confirm user action before processing.
sb.Append("if (confirm('Are you sure you want to process this shipment? " _
& "This cannot be undone.') == false) { return false; } ")
'This forces the page validation, if any, to execute.
sb.Append("if (typeof(Page_ClientValidate) == 'function') { ")
sb.Append("if (Page_ClientValidate() == false) { return false; }} ")
'Changes the text of the button. Gives the user a processing message.
sb.Append("this.value = 'Processing...';")
'Prevent the button from being pressed a second time.
sb.Append("this.disabled = true;")
'Forces the page to postback.
sb.Append(Me.Page.GetPostBackEventReference(Me.btnProcess))
sb.Append(";")
Me.btnProcess.Attributes.Add("onclick", sb.ToString())

And that’s it. This should be on every web application where multiple submits could cause a problem.

OpenDoubleListBox, I knew I was forgetting something

Posted in Programming, .NET, Software, ASP.NET, Open Source on May 31st, 2005

I apologize, I setup a new site and didn’t move over the OpenDoubleListBox files. I didn’t even realize it until I received an email today. Thanks for the reminder Noah.

Current files for the OpenDoubleListBox:
DoubleListBox Example Project
OpenDoubleListBox binary
OpenDoubleListBox Source Code

If you don’t know, the OpenDoubleListBox is a open source implementation of a .NET server control for a double list box . Written in C#. See an image here.

I have also received several requests for additional functionality. I hope to be releasing a new version when I get the time. You can read more about the control at ASP.NET.

Multi-threaded Applications: Making Asynchronous calls to a .NET web service

Posted in Programming, .NET, Web Service on May 26th, 2005

The Business Case
I am currently designing a Customer Inquiry screen for our customer support personnel. I built a Windows application in VB.NET to retrieve and view large amounts customer data from different areas of the enterprise. Business requirements stated to return customer information, such as address and tax information, purchase history, and open orders.

I decided to create a Web Service to handle the functionality of retrieving customer details from the SQL Server. This way I could create a Windows application or a web application and use the same components. Even if a remote user wants to use a Windows application, the web service can retrieve customer data from the home office.

I will assume you are already familiar with the creation of a web service, if not this article maybe difficult to understand. I completed the functionality of my web service, what I call SEER services. The SEER services will forever be my one stop shop for customer information. Currently it performs five functions, including, searching by customer name or number, retrieve past invoice history, calculate customer spending by fiscal year, and retrieving open orders.

Creating and Calling a Multi-threaded Web Service
You don’t have to do anything special to create a multi-threaded web service. The .NET framework already handles this. For each of your WebMethods, .NET creates several more methods; giving your web service that asynchronous touch. Ok, let’s say I have a function called getInvoiceHistory, .NET has also created a method called BegingetInvoiceHistory and EndgetInvoiceHistory. Only calling this method is slightly different from calling the method normally. Let’s look at the differences.

I have previously declared a Dataset as dstDataSet, my web service as seerservices, and selectedCustomer is a string containing the customer ID who I am retrieving the data for. In my single threaded days, I would have simply retrieved customer data in this fashion.

dstDataSet = seerservices.getInvoiceHistory(selectedCustomer)

But in the asynchronous world that same method call would look something like this.

seerservices.BegingetInvoiceHistory(selectedCustomer, acCustomerRecords1, 0)

As you can see more parameters needs to be passed to our asynchronous method than the synchronous method. The first value is a string with the customer ID, same as before. But the next value is another object called AsyncCallback. I used the following declaration to create that object.

Private acCustomerRecords1 As AsyncCallback

        acCustomerRecords1 = New AsyncCallback(AddressOf Me.onCompletedCustomerRecords1)

This will allow a local function onCompletedCustomerRecords1 to handle the returning data. In order to complete the threaded call you will need to call EndgetInvoiceHistory, or Endxxxxxx where xxxxxx is your function name.

Private Sub onCompletedCustomerRecords1(ByVal asyncResult As IAsyncResult)
             'Declare the web service
            Dim seerservices As New SeerServices.seer
            Try
                'Returns the dataset to a class variable (why is explained later)
                _dstDataSet1 = seerservices.EndgetInvoiceHistory(asyncResult)
                'Bind the dataset to my datagrid
                Me.BeginInvoke(CallDataBindToDataGrid)
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.Critical, "Error!")
            End Try
        End Sub

Notice the function declares the web service and then calls the EndgetInvoiceHistory function. This will retrieve the return value from the function.

Summary
That’s it; to call an asynchronous web service you just follow a few steps. And before I tie up some loose ends, let’s go through these steps again.

1. Declare a AsyncCallback object

Private acCustomerRecords1 As AsyncCallback
acCustomerRecords1 = New AsyncCallback(AddressOf Me.onCompletedCustomerRecords1)

2. Call the thread friendly version of your web service function. Again you don’t have to do anything to your web service. This function is created automatically by .NET.

seerservices.BegingetInvoiceHistory(selectedCustomer, acCustomerRecords1, 0)

3. Create a function to handle the thread after completion.

Private Sub onCompletedCustomerRecords1(ByVal asyncResult As IAsyncResult)
        Dim seerservices As New SeerServices.seer
            Try
                _dstDataSet1 = seerservices.EndgetInvoiceHistory(asyncResult)
                Me.BeginInvoke(CallDataBindToDataGrid)
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.Critical, "Error!")
            End Try
      End Sub

Calling out of thread objects
Ok, now your probably wondering what Me.BeginInvoke means. This is a little off topic but I will through it briefly. DataBindToDataGrid is a method that will bind the class property _dstDataSet1 to a datagrid on my form. Now CallDataBindToDataGrid is a previously declared MethodInvoker object that will call the aforementioned function.

Dim CallDataBindToDataGrid As New MethodInvoker(AddressOf Me.DataBindToDataGrid)

The reason for this is, a datagrid is a single instance object and it’s methods cannot be accesses while in a thread. The BeginInvoke method will call the datagrid databinding properties and assign _dstDataSet1 as its data source. Try binding the dataset directly to the datagrid in this function, or even calling DataBindToDataGrid function directly and an exception will be thrown. The gist of the error is, “Controls created on one thread cannot be parented to controls created on another thread” or you can’t call methods of a single-threaded object outside your thread.

Public Sub DataBindToDataGrid()
        dgHistoryDetail.DataSource = _dstDataSet1
        dgHistoryDetail.DataMember = "invoiceheaders"
        _dstDataSet1 = Nothing
    End Sub

Conclusion
The main reason I chose this approach to data retrieval is it speeds up database access. Instead of each function retrieving and binding the data in order, each function is done simultaneously. And the fact that the UI is still responsive while the data is loading will make it easier on the user. You will be amazed by the fact you can still manuever around the user interface while your application is working in the background.

References
Griffiths, Ian. Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads. MSDN. Feb 2003. Aug 9, 2004. http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/

Meier, J.D., Srinath Vasireddy, Ashish Babbar, Alex Mackman. Improving Web Services Performance. Improving .NET Application Performance and Scalability. May 2004. Aug 9, 2004. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt10.asp

Must have .NET Developer Tools

Posted in Programming, .NET, Software, Open Source on July 27th, 2004

MSDN Magazine had a good article on a number of .NET developer tools; all free, some open source. Check out the article here. I personally use half of this tools already.

Tasktime 2.0

Posted in Programming, Software, Open Source on April 20th, 2004

Listed on sourceforge.net
What is Tasktime?  It is software that will allow you to track
time spent on a given task
.  It sits idlely in the taskbar. 
The data is stored in XML format so you can import it into any data
source or use the existing XSLT file to get a basic report on it.

When first run the Tasktime app will show up as a small  clock.

Tasktime in taskbar

Once you double click on the icon it will start the timing of a
specified task.
tasktime start task screen

When you wish to stop tracking the task double click on the stopwatch
icon.
Tasktime tracking a task

Enter any comments you wish recorded and click OK.

end task and enter comments

When tracking a task you can also check the running time by right
clicking on the stopwatch icon and clicking Check time.
tasktime check time screen