Analytics


Google
Showing posts with label The book. Show all posts
Showing posts with label The book. Show all posts

Friday, October 16, 2009

Programmer's Journal - Chapter 10

Chapter 10 - Protocols (SMTP, FTP, HTTP)

The framework supports many kinds of Internet protocol. We have previously covered web services. Now let us look at a few more common protocols. SMTP for sending mail, FTP for file transfer and HTTP for obtaining information from other web pages.
SMTP (or Sending Mail)

SMTP stands for Simple Mail Transfer Protocol and is used to send mail. It uses port 25. When using this, you need to be aware that some antivirus software or firewalls will block this port and you may need to disable the blocking before you can send mail. This is because a lot of viruses also use this protocol to propagate themselves.

If you need to send a quick mail (which consist of text only), you can just use the simple function:

SMTP.Send(from, toMail, subject, body).

Framework 1.1 Example
The following is an example of sending attachment:

Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Web.Mail
Imports System.Reflection


' Program: TestMail_1.1
' Author: Strovek
' Date: Mar 27, 2006
' Revised:
' Simple program for sending email - using codes for Framework 1.1
'
' ===================================================================

<Assembly: AssemblyVersionAttribute("1.0.0")>
<Assembly: AssemblyFileVersion("1.0.0")>
<Assembly: AssemblyCompanyAttribute("Programmer's Journal")>
<Assembly: AssemblyProductAttribute("FTP test")>

Module Module1

Public Function prgVersion() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(_
System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Main()
Dim SMTPSrv As String
Dim addr As String
Console.WriteLine("Test Send Mail - Version " & prgVersion())

Try
SMTPSrv = System.Configuration.ConfigurationSettings.AppSettings("SMTPSrv")
If Len(SMTPSrv) <= 0 Then
Throw New Exception("No SMTP Server provided")
End If
Console.WriteLine("smtp server is " & SMTPSrv)

addr = System.Configuration.ConfigurationSettings.AppSettings("Rcpt")
If Len(addr) <= 0 Then
Throw New Exception("No address provided")
End If

Dim Message As System.Web.Mail.MailMessage = New System.Web.Mail.MailMessage
Message.To = addr
Message.From = addr
Message.Subject = "Test Mail - for testing SMTP"
Message.Body = "Just a test message"
Message.Attachments.Add(New MailAttachment("c:\test2.txt"))
SmtpMail.SmtpServer = SMTPSrv
SmtpMail.Send(Message)
Console.WriteLine("Message sent")

Catch ex As Exception
Console.WriteLine("Error " & ex.Message)
End Try

Console.WriteLine("End Program")
End Sub

End Module


In the above example, the attachment is provided via the filename. This is the only option available in Framework 1.1.

The sample above uses the config file to provide the SMTP server name and also the receipients name. One of the services provided by IIS is SMTP; this is what we are using to send the mail. The config file is as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="SMTPSrv" value="localhost" />
<add key="Rcpt" value="bteoh1@hotmail.com" />
</appSettings>
</configuration>


Framework 2.0 Example
The new namespace System.Net.Mail is introduced in Framework 2.0 to replace System.Web.Mail namespace in Framework 1.1.

The following is a sample of the code using the new namespace.

Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Net.Mail
Imports System.Reflection


' Program: TestMail_2.0
' Author: Strovek
' Date: Mar 27, 2006
' Revised:
' Simple program for sending email - using codes for Framework 1.1
'
' ===================================================================

<Assembly: AssemblyVersionAttribute("1.0.0")>
<Assembly: AssemblyFileVersion("1.0.0")>
<Assembly: AssemblyCompanyAttribute("Programmer's Journal")>
<Assembly: AssemblyProductAttribute("FTP test")>

Module Module1

Public Function prgVersion() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Main()
Dim SMTPSrv As String
Dim addr As String
Dim attch As Attachment
Console.WriteLine("Test Send Mail - Version " & prgVersion())

Try
SMTPSrv = System.Configuration.ConfigurationManager.AppSettings("SMTPSrv")
If Len(SMTPSrv) <= 0 Then
Throw New Exception("No SMTP Server provided")
End If
Console.WriteLine("smtp server is " & SMTPSrv)

addr = System.Configuration.ConfigurationManager.AppSettings("Rcpt")
If Len(addr) <= 0 Then
Throw New Exception("No address provided")
End If

Dim fromAddr As New MailAddress(addr)
Dim toAddr As New MailAddress(addr)
Dim Message As New System.Net.Mail.MailMessage(fromAddr, toAddr)

Message.Subject = "Test Mail - for testing SMTP"
Message.Body = "Just a test message"
Message.Attachments.Add(New Attachment("c:\test2.txt"))

Dim strm As New MemoryStream
Dim sw As New StreamWriter(strm)
sw.WriteLine("Line 1")
attch = New Attachment(strm, "test3.txt")
Message.Attachments.Add(attch)

Dim SMTP As New SmtpClient(SMTPSrv)
SMTP.Send(Message)
sw.Close()
Console.WriteLine("Message sent")

Catch ex As Exception
Console.WriteLine("Error " & ex.Message)
End Try

Console.WriteLine("End Program")
End Sub

End Module


Note the change in the constructor. This constructor is used because the .to method is read-only so has to be set in the constructor. The Attachment introduced in Framework 2.0 to replace mail-attachment allows passing a stream in the constructor. This is allows you to create an attachment in memory and send it (as shown in the example above).

The config file is identical to the one used in the previous example.

FTP
FTP is short for File Transfer Protocol and is used primarily for transferring files to and from a remote ftp server. The support was provided in Framework 1.1. However, it was difficult because you have to implement your own code to handle the FTP protocol.

The ftp protocol code has been implemented in Framework 2.0. The following is a sample of transferring a file to and from the local ftp server:

' doFTP
' Author Strovek
' Date Apr 03, 2006
' Revised
' For performing simple FTP
' ===============================================================================


Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Text
Imports System.Net
Imports System.Reflection

<Assembly: AssemblyVersionAttribute("1.0.0")>
<Assembly: AssemblyFileVersion("1.0.0")>
<Assembly: AssemblyCompanyAttribute("Programmer's Journal")>
<Assembly: AssemblyProductAttribute("FTP test")>


Public Class doFTP

Dim ftp As FtpWebRequest

Public Shared Function prgVersion() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(_
System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Public Shared Sub main()
Dim ftpSrv As String = Nothing
Dim ftpUsr As String = Nothing
Dim ftpPwd As String = Nothing
Dim ftpDir As String = Nothing
Dim fSrcName As String = "C:\icons\icons.bmp"
Dim fTgtName As String = "icons.bmp"
Dim fTgtName2 As String = "c:\tx\icons.bmp"

Console.WriteLine("doFTP - Version " & prgVersion())

Try

ftpSrv = System.Configuration.ConfigurationManager.AppSettings("ftpSrv")
ftpUsr = System.Configuration.ConfigurationManager.AppSettings("ftpUsr")
ftpPwd = System.Configuration.ConfigurationManager.AppSettings("ftpPwd")
ftpDir = System.Configuration.ConfigurationManager.AppSettings("ftpDir")

If ftpSrv.Contains("ftp://") = False Then
ftpSrv = "ftp://" & ftpSrv
End If

Dim ftpObj As New doFTP
ftpObj.uploadFile(ftpSrv, ftpUsr, ftpPwd, "blank", fSrcName, fTgtName)
ftpObj.downloadFile(ftpSrv, ftpUsr, ftpPwd, "blank", fTgtName, fTgtName2)

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try

Console.WriteLine("End Program")

End Sub

' for Uploading file
Sub uploadFile(ByVal ftpSrv As String, ByVal usrName As String, _
ByVal usrPwd As String, ByVal ftpDir As String, _
ByVal src As String, ByVal tgt As String)

Dim ftpUrl As New StringBuilder

If ftpSrv Is Nothing Then
Throw New Exception("[upLoadFile01]ftp server not provided")
End If
If usrName Is Nothing Then
Throw New Exception("[upLoadFile02]User Name not provided")
End If
If usrPwd Is Nothing Then
Throw New Exception("[upLoadFile03]User Password not provided")
End If
If src Is Nothing Then
Throw New Exception("[upLoadFile04]Source FileName not provided")
End If
If tgt Is Nothing Then
Throw New Exception("[upLoadFile05]Target filename not provided")
End If

Dim ftpStream As Stream = Nothing
Dim fStream As FileStream = Nothing
Dim ftpResponse As FtpWebResponse = Nothing

ftpUrl.Append(Trim(ftpSrv))
If Trim(ftpSrv).EndsWith("/") = False Then
ftpUrl.Append("/")
End If
ftpUrl.Append(Trim(ftpDir))
If Trim(ftpDir).EndsWith("/") = False Then
ftpUrl.Append("/")
End If
ftpUrl.Append(tgt)

Try
Console.WriteLine(ftpUrl.ToString)
ftp = WebRequest.Create(ftpUrl.ToString)
ftp.Credentials = New NetworkCredential(usrName, usrPwd)
ftp.Method = WebRequestMethods.Ftp.UploadFile
ftpStream = ftp.GetRequestStream

fStream = File.Open(src, FileMode.Open)

Dim buffer(1024) As Byte
Dim bytesRead As Integer
While True
bytesRead = fStream.Read(buffer, 0, buffer.Length)
If bytesRead = 0 Then
Exit While
End If
ftpStream.Write(buffer, 0, bytesRead)
End While

' The request stream must be closed before getting the response.
ftpStream.Close()
fStream.Close()

ftpResponse = ftp.GetResponse()
Console.WriteLine(ftpResponse.StatusDescription)
Console.WriteLine("Upload successful")
Catch ex As Exception

' Need to check here since finally clause will
' never be reached because of the throw exception
If fStream IsNot Nothing Then
fStream.Close()
End If
If ftpStream IsNot Nothing Then
ftpStream.Close()
End If

Console.WriteLine(ex.StackTrace)
Throw New Exception("[upLoadFile06]" & ex.Message)
Finally
If fStream IsNot Nothing Then
fStream.Close()
End If
If ftpStream IsNot Nothing Then
ftpStream.Close()
End If
End Try

End Sub

Sub downloadFile(ByVal ftpSrv As String, ByVal usrName As String, _
ByVal usrPwd As String, ByVal ftpDir As String, _
ByVal src As String, ByVal tgt As String)

Dim ftpUrl As New StringBuilder

If ftpSrv Is Nothing Then
Throw New Exception("[downloadFile01]ftp server not provided")
End If
If usrName Is Nothing Then
Throw New Exception("[downloadFile02]User Name not provided")
End If
If usrPwd Is Nothing Then
Throw New Exception("[downloadFile03]User Password not provided")
End If
If src Is Nothing Then
Throw New Exception("[downloadFile04]Source FileName not provided")
End If
If tgt Is Nothing Then
Throw New Exception("[downloadFile05]Target filename not provided")
End If

Dim ftpStream As Stream = Nothing
Dim fStream As FileStream = Nothing
Dim ftpResponse As FtpWebResponse = Nothing

ftpUrl.Append(Trim(ftpSrv))
If Trim(ftpSrv).EndsWith("/") = False Then
ftpUrl.Append("/")
End If
ftpUrl.Append(Trim(ftpDir))
If Trim(ftpDir).EndsWith("/") = False Then
ftpUrl.Append("/")
End If
ftpUrl.Append(src)

Try
Console.WriteLine(ftpUrl.ToString)
ftp = WebRequest.Create(ftpUrl.ToString)
ftp.Credentials = New NetworkCredential(usrName, usrPwd)
ftp.Method = WebRequestMethods.Ftp.DownloadFile
ftpResponse = ftp.GetResponse
ftpStream = ftpResponse.GetResponseStream

Dim buffer(1024) As Byte
Dim bytesRead As Integer
fStream = File.Create(tgt)
While True
bytesRead = ftpStream.Read(buffer, 0, buffer.Length)
If bytesRead = 0 Then
Exit While
End If
fStream.Write(buffer, 0, bytesRead)
End While

' The request stream must be closed before getting the response.
ftpStream.Close()
fStream.Close()

Console.WriteLine("download successful")
Catch ex As Exception

' Need to check here since finally clause will
'never be reached because of the throw exception
If fStream IsNot Nothing Then
fStream.Close()
End If
If ftpStream IsNot Nothing Then
ftpStream.Close()
End If

Console.WriteLine(ex.StackTrace)
Throw New Exception("[downloadFile06]" & ex.Message)
Finally
If fStream IsNot Nothing Then
fStream.Close()
End If
If ftpStream IsNot Nothing Then
ftpStream.Close()
End If
End Try

End Sub

End Class


The following is the config file used with the sample above:

<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="ftpSrv" value="ftp://localhost"/>
<add key="ftpUsr" value="anonymous"/>
<add key="ftpPwd" value="t@t.com"/>
<add key="ftpDir" value="/blank/"/>
</appSettings>
</configuration>



In the above sample, the uploadFile subroutine shows how to upload a file to the ftp server and downloadFile downloads the file from the ftp server. As shown in the example above, you need to provide the target server filename together with the directory and the server name. The same with when downloading the file, you need to provide the fully qualified path as in ftp://localhost/blank/test.txt. The sample uses only ASCII transfer. In order to use binary file transfer, set the UseBinary property of the FtpWebRequest object to True. Note that all transfer used here are sent via clear text (without encryption). If your server supports SSL, you can set the EnableSSL property to true. In the case of SSL enabled, then the protocol used with ftps. There is another protocol called sftp - i.e. ftp over SSH (Secure Shell). The implementation is provided with the Framework (so you either have to provide your own implementation or obtain a third party implementation).

HTTP (also known as screen scrapping)

HTTP protocol is typically used either for screen scrapping, where you obtain information from other web pages or to automated submission of information to other web sites. However, many web developers have come out with mechanism to prevent programs from using their web sites with JavaScript, image - requiring the user to interprete and key in the values.

Fiddler
Before you can perform screen scrapping you need to know what needs to be transmitted to the web page. Microsoft provides a free tool (to be used with Internet Explorer) to do this - it is called Fiddler.

For the purpose of illustration, a simple page was created for the purpose of illustration:

Before submit:



After Submit:



This is what you see in Fiddler:



Notice that what is sent to the server.

Code Sample
The following is a code sample of how we can use this information:

' ScrapScrn
' Author Strovek
' Date Apr 20, 2006
' Revised
' For performing simple FTP
' ===============================================================================

Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Net
Imports System.Text
'Imports System.Web

Module ScrapScrn
Dim rqst As HttpWebRequest
Dim rsp As HttpWebResponse
Private header As WebHeaderCollection
Private cookies As CookieContainer = New CookieContainer()
Const url As String = "http://localhost/pj/chapter10/simplesubmit.aspx"

Sub main(ByVal CmdArgs() As String)

Console.WriteLine("Program start")
Try
postPage()
Catch ex As Exception
Console.WriteLine("Error " & vbNewLine & ex.Message)
End Try
Console.WriteLine("End Program")

End Sub

Sub postPage()

Dim cookies As CookieContainer = New CookieContainer()
rqst = WebRequest.Create(url)
rsp = rqst.GetResponse
Dim rspStr As String
Dim sReader As New StreamReader(rsp.GetResponseStream)
header = rqst.Headers ' Obtain the header values from the request
cookies = rqst.CookieContainer ' Obtain Cookie from the request
rspStr = sReader.ReadToEnd
sReader.Close()
Console.WriteLine("Response received" & vbNewLine & rspStr)

Dim key As String

Console.WriteLine("header content is as follows")
For Each key In header.AllKeys
Console.WriteLine("Key is {0} and value is {1}", key, header.Item(key))
Next

Dim viewState As String = ExtractViewState(rspStr)
Console.WriteLine("viewState is " & viewState)

Dim postData As String = String.Format("__VIEWSTATE={0}&txtEntry={1}&btnSubmit={2}", _
viewState, "test", "Submit")
Console.WriteLine("postData is " & postData)

rqst = WebRequest.Create(url)
'rqst.Headers = header ' Repopulate the header values from the previous request
rqst.CookieContainer = cookies ' Repopulate the cookies from the previous request
rqst.ContentType = "application/x-www-form-urlencoded"
rqst.Method = "POST"

Dim sw As New StreamWriter(rqst.GetRequestStream)
sw.Write(postData)
sw.Close()

rsp = rqst.GetResponse
sReader = New StreamReader(rsp.GetResponseStream)
rspStr = sReader.ReadToEnd
sReader.Close()
Console.WriteLine("Data after post" & vbNewLine & rspStr)

End Sub

Function ExtractViewState(ByVal strParam As String) As String
Dim ViewStateDelimiter As String = "__VIEWSTATE"
Dim valueDelimiter As String = "value="""

Dim viewStateNamePosition As Integer = strParam.IndexOf(ViewStateDelimiter)
Dim viewStateValuePosition As Integer = strParam.IndexOf( _
valueDelimiter, viewStateNamePosition)

Dim viewStateStartPosition As Integer = viewStateValuePosition + valueDelimiter.Length
Dim viewStateEndPosition As Integer = strParam.IndexOf("""", viewStateStartPosition)

Dim returnVal As String = _
System.Web.HttpUtility.UrlEncodeUnicode(strParam.Substring(viewStateStartPosition, _
viewStateEndPosition - viewStateStartPosition))

Return returnVal
End Function

End Module


The first portion of the code performs a get to the web page to obtain the headers, cookies etc. See below:

Dim cookies As CookieContainer = New CookieContainer()
rqst = WebRequest.Create(url)
rsp = rqst.GetResponse
Dim rspStr As String
Dim sReader As New StreamReader(rsp.GetResponseStream)
header = rqst.Headers ' Obtain the header values from the request
cookies = rqst.CookieContainer ' Obtain Cookie from the request
rspStr = sReader.ReadToEnd
sReader.Close()
Console.WriteLine("Response received" & vbNewLine & rspStr)


Since the web page was written in ASP.Net, it contains a hidden field called ViewState, we need to send back this value when we post the information. In order to that, we need to first get the value from the original response. The view state is as follows:

<input type="hidden" name="__VIEWSTATE" value="dDwxMzMzMTE3MDk3Ozs+ouZtBeP5/4BlYpWcfFxvgG8hQ8c=" />

To do that, first find the location of _VIEWSTATE and then find the location of the first value=" after that. Then get the value until the end of the quote. The function to do that is as follows:

Function ExtractViewState(ByVal strParam As String) As String
Dim ViewStateDelimiter As String = "__VIEWSTATE"
Dim valueDelimiter As String = "value="""

Dim viewStateNamePosition As Integer = strParam.IndexOf(ViewStateDelimiter)
Dim viewStateValuePosition As Integer = strParam.IndexOf( _
valueDelimiter, viewStateNamePosition)

Dim viewStateStartPosition As Integer = viewStateValuePosition + valueDelimiter.Length
Dim viewStateEndPosition As Integer = strParam.IndexOf("""", viewStateStartPosition)

Dim returnVal As String = _
System.Web.HttpUtility.UrlEncodeUnicode(strParam.Substring(viewStateStartPosition, _
viewStateEndPosition - viewStateStartPosition))

Return returnVal
End Function


Finally, construct the string you want to post, in this case is:

__VIEWSTATE=dDwxMzMzMTE3MDk3Ozs%2BouZtBeP5%2F4BlYpWcfFxvgG8hQ8c%3D&txtEntry=testing&btnSubmit=Submit

Another thing to note between the original get and the final post, the contenttype has changed.

The following is the original content type:



The following is the content-type when posting:



The following is the rest of the code:


Dim postData As String = String.Format("__VIEWSTATE={0}&txtEntry={1}&btnSubmit={2}", _
viewState, "test", "Submit")
Console.WriteLine("postData is " & postData)

rqst = WebRequest.Create(url)
'rqst.Headers = header ' Repopulate the header values from the previous request
rqst.CookieContainer = cookies ' Repopulate the cookies from the previous request
rqst.ContentType = "application/x-www-form-urlencoded"
rqst.Method = "POST"

Dim sw As New StreamWriter(rqst.GetRequestStream)
sw.Write(postData)
sw.Close()

rsp = rqst.GetResponse
sReader = New StreamReader(rsp.GetResponseStream)
rspStr = sReader.ReadToEnd
sReader.Close()
Console.WriteLine("Data after post" & vbNewLine & rspStr)


In the above sample, we are sending back all the cookies that were obtained from the original request. The header was not returned because the contenttype is part of the header and should be changed.

As with FTP, we can also enableSSL to perform https.

Saturday, June 13, 2009

Programmer's Journal - Chapter 4

Chapter 4 – Introducing ASP.Net

ASP.Net is a platform consisting of Web Forms and Web Services. It can be written in any language that is supported in the dotNet framework.

Let us start with Web forms.

Introduction

HTML
Some knowledge of HTML is essential when writing a web application. HTML is a set tag that can be understood by browser like Internet Explorer and Firefox. For example:



Not all html tags are visible. Some of them are invisible. Example:



Web Controls

Web controls are enhancements to the HTML tags. Examples are:



However, it is important to note that not all html tags have web control equivalent. Some like <input type=file> has no equivalent in Webcontrol but can be used as HTMLControl in the webforms. Others like <asp:datagrid> has no equivalent in html.

Example of the comparison is as follows:

Source code:

<html>
<head>
</head>
<body>
<form runat="server">
Basic HTML <b>Hello There</b>
<br />
<input type="button" value="Submit" />
<br />
<textarea></textarea>
<br />
<input type="text" />
<br />
<input type="checkbox" />
<br />
<input type="radio" />
<br />
<input type="file" />
<br />
<pre> This is a test now
What do you think </pre>
<br />
<hr />
Web Control
<br />
<asp:Button id="Button1" runat="server" Text="Submit"></asp:Button>
<br />
<asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Rows="2" Columns="20"></asp:TextBox>
<br />
<asp:CheckBox id="CheckBox1" runat="server"></asp:CheckBox>
<br />
<asp:RadioButton id="RadioButton1" runat="server"></asp:RadioButton>
<br />
<asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
<br />
</form>
</body>
</html>


View:



Web Forms can be written in two ways:

  1. Having all the code and html tags in one file. This is similar to the classical ASP approach.
  2. Separating the code in one file (known as codebehind) and the html/webcontrol in the aspx page. In which case, you have to compile the codebehind file before you can use it. This is approached used in Visual Studio and SharpDeveloper.

Some benefits ASP.NET has over ASP are:

  1. By default, ASP.Net will keep track of values in controls (known as ViewState). In ASP, this has to be done manually in the code.
  2. There is a lot more control over the properties of the control – enabling/disabling, appearance etc
Simple Page Sample

Single page sample

<%@ Page Language="VB" %>
<script runat="server">

Sub btnSubmit_Click(sender As Object, e As EventArgs)
lblMsg.Text += "<br>" & txtInput.Text
End Sub

Sub Page_Load(sender As Object, e As EventArgs)
Response.Write("<title>Simple Page</title>")
If cbHideInput.Checked Then
txtInput.Visible = False
Else
txtInput.Visible = True
End If
End Sub

</script>
<html>
<head>
</head>
<body>
<form runat="server">
Text Input:
<asp:TextBox id="txtInput" runat="server"></asp:TextBox>
<br />
<asp:Button id="btnSubmit" onclick="btnSubmit_Click" runat="server" Text="Submit"></asp:Button>
<br />
<br />
<asp:CheckBox id="cbHideInput" runat="server" Text="Hide Text Box"></asp:CheckBox>
<br />
<asp:Label id="lblMsg" runat="server"></asp:Label>
<br />
</form>
</body>
</html>


Note that all codes for the above page are placed between the <script runat="server"> and </script> and this is before the <html> tag. The codes can be transferred to a separate file known as code behind page.

However, before we do that, let us look at what the above page does.

Sub Page_Load(sender As Object, e As EventArgs)
Response.Write("<title>Simple Page</title>")
If cbHideInput.Checked Then
txtInput.Visible = False
Else
txtInput.Visible = True
End If
End Sub


The page_load subroutine is executed every time the page loads. This can be shown by adding a counter and have it increment then refresh the page several times.

In the above case, performing response.write("<title>Simple Page</title>"), which will cause the word Simple Page appear on the header bar of the browser:



The pageload also checks the checkbox for hiding the textbox; if it is checked, it changes the visible property of the textbox to false, which will hide the textbox (i.e. the server will not send the codes for rendering the control to the browser - you can verify this by looking at the source code of the page generated).

Sub btnSubmit_Click(sender As Object, e As EventArgs)
lblMsg.Text += "<br>" & txtInput.Text
End Sub


This code, will handle the Click event from btnSubmit button control. This is specified in the webcontrol declaration (in the onclick property of the declaration) in the display portion of the page - as shown below:

<asp:Button id="btnSubmit" onclick="btnSubmit_Click" runat="server" Text="Submit"></asp:Button>

When the button is clicked, we just append the value of the txtInput in the lblMessage content.

Page output:



Page source for second click:

<title>Simple Page</title>
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="FirstForm.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNzc0MzEyOTQ2O3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDc+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFw8YnJcPnRlc3RcPGJyXD50ZXN0MTs+Pjs+Ozs+Oz4+Oz4+O2w8Y2JIaWRlSW5wdXQ7Pj6M942YKbECvEx4rYgtGz/L1qpWTQ==" />

Text Input:
<input name="txtInput" type="text" value="test1" id="txtInput" />
<br />
<input type="submit" name="btnSubmit" value="Submit" id="btnSubmit" />
<br />
<br />
<input id="cbHideInput" type="checkbox" name="cbHideInput" /><label for="cbHideInput">Hide Text Box</label>
<br />
<span id="lblMsg"><br>test<br>test1</span>
<br />
</form>
</body>
</html>


Notice there is a hidden input field with the name "__VIEWSTATE". This is how, asp.net is able to keep track of the state all the controls within its page. The state tracked includes the values and attributes like css classes used, visibility etc.

Page source for third click:

<title>Simple Page</title>
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="FirstForm.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNzc0MzEyOTQ2O3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDE+O2k8Nz47PjtsPHQ8cDxwPGw8VGV4dDtWaXNpYmxlOz47bDx0ZXN0MTtvPGY+Oz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxcPGJyXD50ZXN0XDxiclw+dGVzdDFcPGJyXD50ZXN0MTs+Pjs+Ozs+Oz4+Oz4+O2w8Y2JIaWRlSW5wdXQ7Pj4tiPKYc8LB75h/ooW4ReGZipS3rQ==" />

Text Input:

<br />
<input type="submit" name="btnSubmit" value="Submit" id="btnSubmit" />
<br />
<br />
<input id="cbHideInput" type="checkbox" name="cbHideInput" checked="checked" /><label for="cbHideInput">Hide Text Box</label>
<br />
<span id="lblMsg"><br>test<br>test1<br>test1</span>
<br />
</form>
</body>
</html>


When you compare the sourcecode from the second click and third click, notice that the text input is no longer there:

From 2nd click:

Text Input:
<input name="txtInput" type="text" value="test1" id="txtInput" />
<br />


From 3rd click:

Text Input:

<br />

As mentioned, when the property of the text box is set to visible = false then the server will not send the code to render the text input to the web browser.
With codebehind sample

To use the code behind, we need to:


1. Add the following directive to the top of the aspx page:
<%@ Page Language="vb" autoeventwireup="false" Inherits="FirstFormCB.WebForm1" %>
2. Move all code between the <script runat="server"> and </script> into a new file.
3. Since the code behind file can be any class and has no knowledge of the aspx page. We need to know a bit more work.
a. In the above directive, we indicated that the class is WebForm1 and it resides in a namespace "FirstFormCB" - specified in the "inherits" property. We need to declare the webform class and place it in a namespace called FirstFormCB.
b. We create the class by inheriting from System.Web.UI.Page. This will allow inherit all the information like the state, cookies etc.
c. We also need to declare all the webcontrols from the aspx page so that we can access the values and attributes of those webcontrols. (This is done automatically for you if you are using Visual Studio.Net).
4. The codebehind file needs to be compiled into a library assembly (.dll extension).
5. The dll file needs to be placed in the bin directory directly below a virtual application directory in IIS. (Unless you install the assembly as a Global Assembly Cache - more about this later).

Code Behind File
The code behind pages look like this:

'
' CodeBehind for FirstFormCB
' User: Strovek
' Date: 9/11/2005
'
' ========================================

Imports System
Imports System.Web
Imports System.Web.UI.WebControls

Namespace FirstFormCB

Public Class WebForm1
Inherits System.Web.UI.Page

Protected WithEvents lblMsg As Label
Protected WithEvents txtInput As TextBox
Protected WithEvents cbHideInput As CheckBox
Protected WithEvents btnSubmit As Button


Sub btnSubmit_Click(sender As Object, e As EventArgs)
lblMsg.Text += "<br>" & txtInput.Text
End Sub

Sub Page_Load(sender As Object, e As EventArgs)
Response.Write("<title>Simple Page</title>")
If cbHideInput.Checked Then
txtInput.Visible = False
Else
txtInput.Visible = True
End If
End Sub

End Class
End Namespace


ASPX file
The aspx page looks like this:

<%@ Page Language="vb" autoeventwireup="false"
codebehind="FirstFormCB.aspx.vb" Inherits="FirstFormCB.WebForm1" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
Text Input:
<asp:TextBox id="txtInput" runat="server"></asp:TextBox>
<br />
<asp:Button id="btnSubmit" onclick="btnSubmit_Click" runat="server" Text="Submit"></asp:Button>
<br />
<br />
<asp:CheckBox id="cbHideInput" runat="server" Text="Hide Text Box"></asp:CheckBox>
<br />
<asp:Label id="lblMsg" runat="server"></asp:Label>
<br />
</form>
</body>
</html>


Note in Framework 1.1, you specify the page directive is as follows:

<%@ Page Language="vb" autoeventwireup="false"
codebehind="FirstFormCB.aspx.vb" Inherits="FirstFormCB.WebForm1" %>


In Framework 2.0, the codebehind property has been changed to CodeFile as shown below:

<%@ Page Language="vb" autoeventwireup="false"
CodeFile="FirstFormCB.aspx.vb" Inherits="FirstFormCB.WebForm1" %>

Compiling
Next you need to compile the code behind file into an assembly. This step is not necessary in the single file because IIS will compile the aspx file the first time you access it. You can use the command like to compile it using the following command (do not press return):

\WINDOWS\Microsoft.NET\Framework\v1.1.4322\vbc /t:library /out:bin\FirstFormCB.dll FirstFormCB.aspx.vb /r:system.web.dll,system.dll

In our example, we place the namespace in the codebehind file, alternatively, we can specify the namespace within when we type the command using the /rootnamespace: FirstFormCB switch when compiling.

Alternatively, you can create a nant build file and then use nant to perform the compilation. A sample of the nant build file is as follows:

<?xml version="1.0"?>
<!--
NANT build File for FirstFormCB
Author Strovek
Date Sep 11, 2005
Revised

-->
<project name="FirstFormCB" default="build" basedir=".">
<property name="project.name" value="First Form with Code Behind"/>
<property name="project.version" value="1.1.0" />
<description>The RHO IT Slider Library of build files.</description>
<property name="debug" value="true" unless="${property::exists('debug')}" />
<property name="verbose" value="true" />
<target name="clean" description="remove all generated files">
<delete file="FirstFormCB.exe" failonerror="false" />
<delete file="FirstFormCB.pdb" failonerror="false" />
</target>
<target name="build" description="compiles the source code">
<vbc target="library" output="bin\FirstFormCB.dll" debug="${debug}">
<sources>
<include name="FirstFormCB.aspx.vb" />
</sources>
<references>
<include name="System.dll" />
<include name="System.Web.dll" />
</references>
</vbc>
</target>
</project>


Changes in Framework 2.0

When you use Visual Studio 2005, the codes (Visual Studio will always use the codebehind option for all ASPX pages) will no longer be automatically compiled into a single .dll file and place into the bin directory of the project. Instead, it will compile and place it into memory for execution when you first execute the application and subsequently when any of the files changes (It will still recognize the assemblies placed into the bin directory).

Microsoft is working on a option (called Visual 2005 Web Applications Project) which allows Visual Studio 2005 to use the same project compilation model (as used by Visual Studio 2003). This add-on is still in beta.

A new option is introduced - to compile the whole application directory. When you do this, everything will be compiled - including the .aspx (leaving only stubs for entry points). Only selected files like web.config will be left uncompiled.

In Visual Studio 2003, when you create a database connection by dragging it to the page the connection string is included in the page. However in Visual 2005, when you create a database connection, you are given an option to save the connection string to the configuration file. When you do that, the connection string will be stored in the ConnectionString section in the web.config. This is a new section introduced in Framework 2.0.

Web.config
Sample web.config is as follows (note the new section "connectionStrings"):

<?xml version="1.0"?>
<!--
Note: As an alternative to hand editing this file you can use the
web admin tool to configure settings for your application. Use
the Website->Asp.Net Configuration option in Visual Studio.
A full list of settings and comments can be found in
machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
-->
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<appSettings/>
<connectionStrings>
<add name="connstr" connectionString="Data Source=orcl;User ID=scott;Password=tiger" />
</connectionStrings>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.

Visual Basic options:
Set strict="true" to disallow all data type conversions
where data loss can occur.
Set explicit="true" to force declaration of all variables.
-->
<compilation debug="true" strict="false" explicit="true">
<assemblies>
<add assembly="System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/></assemblies></compilation>
<pages>
<namespaces>
<clear/>
<add namespace="System"/>
<add namespace="System.Collections"/>
<add namespace="System.Collections.Specialized"/>
<add namespace="System.Configuration"/>
<add namespace="System.Text"/>
<add namespace="System.Text.RegularExpressions"/>
<add namespace="System.Web"/>
<add namespace="System.Web.Caching"/>
<add namespace="System.Web.SessionState"/>
<add namespace="System.Web.Security"/>
<add namespace="System.Web.Profile"/>
<add namespace="System.Web.UI"/>
<add namespace="System.Web.UI.WebControls"/>
<add namespace="System.Web.UI.WebControls.WebParts"/>
<add namespace="System.Web.UI.HtmlControls"/>
</namespaces>
</pages>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows"/>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.

<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
</system.web>
</configuration>


Application in IIS
To create a application in IIS, when you declare a virtual directory, set it to run script only. In IIS, right click, select new then click virtual directory:



Provide the name of the virtual directory:



Provide the actual directory:



Select the options (Read and run scripts should be sufficient):



Then click Finish:



The icon created in IIS should appears as follows:

For non-application directory it will appear as follows:

If the virtual directory is already created, then right click on the directory and select properties, then click the create button to convert the directory into an application directory:



After you click the create button, the property will change to the following:



For this book, I created a virtual directory PJ and the codes of each chapter is placed in the different chapter directory, the directories are as follows:



For the code behind example, the generated assembly (.dll file) should be placed into /PJ/Chapter4/bin/ directory. The aspx and web.config is placed in the /PJ/Chapter4/ directory

ASP.Net will always look into the application directory for the web.config and the bin directory just below the application directory for the compiled assemblies. So if you have the FirstFormCB.aspx in Chapter4, it will look in Chapter4 directory for web.config and Chapter4/bin directory for the FirstFormCB.dll.

If you decide to move the file to Chapter3 (which is not an application) the framework will look for the web.config in PJ (the closest application it can find. It will also look for the FirstFormCB.dll in PJ/bin directory when you invoke the page from the browser.

The assembly filename is defined during compilation. If you rename the file after that, the framework will reject it and indicate that the name does not match the reference.

There are two web controls that make tabular output very easy to do. They are DataGrid and DataRepeater.
  • Controls
  • Web Control
  • Simple DataGrid

If you just need to output the information queried directly from a database and do not need special format, it is very easy to produce tabular output using a datagrid. Datagrid can take input from many data sources- it can use a datareader, arraylist, DataTable, etc.

In the following example, I will use a datareader to illustrate:

The aspx is as follows:

<%@ Page Language="vb" autoeventwireup="false" codebehind="SimpleDataGrid.aspx.vb" Inherits="simpleDataGrid.WebForm1" %>
<html>
<head>
</head>
<body>
<form runat="server">
<asp:DataGrid id="dgResults" runat="server" BackColor="#C0FFC0">
<AlternatingItemStyle backcolor="#80FF80"></AlternatingItemStyle>
<HeaderStyle backcolor="#FFC080"></HeaderStyle>
</asp:DataGrid>
<br />
<asp:Label id="lblErrMsg" runat="server" forecolor="Red"></asp:Label>
</form>
</body>
</html>


When specifying a datagrid, we can provide instructions like the background color for the header and alternate colors for the items. The above code will look like the following in the designer of Web Matrix:



I have set the color for alternating items and also for the header. The lblErrMsg is a label for me to display any errors caught.

The code behind is as follows:

'
' SimpleDataGrid
' User: Strovek
' Date: 9/22/2005
'
' ========================================

Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Reflection
Imports System.Web.UI.WebControls

<Assembly: AssemblyTitle("Simple Data Grid")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Programmer's Journal")>
<Assembly: AssemblyProduct("Simple Data Grid")>
<Assembly: AssemblyCopyright("Programmer's Journal")>
<Assembly: AssemblyTrademark("Programmer's Journal")>
<Assembly: CLSCompliant(True)>
<Assembly: AssemblyVersion("1.00.0")>
<Assembly: AssemblyFileVersion("1.0.0")>

Namespace simpleDataGrid

Public Class WebForm1
Inherits System.Web.UI.Page

Protected WithEvents dgResults As DataGrid
Protected WithEvents lblErrMsg As Label
Dim Conn As New
OleDbConnection(System.Configuration.ConfigurationSettings.AppSettings("connStr"))

Function Ver() As String
Return _ System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Response.Write("<title>Data Grid Sample - Version " & Ver() & "</title>")

fillGrid

End Sub

Private Sub fillGrid
Try
Conn.Open
Dim sql As String = "SELECT partno Lot, id Product, " _
& "currentstep Step, currentqty Qty " _
& "FROM partmaster p, system_lookup i " _
& "WHERE p.status = 'Active' AND " _
& "i.systemid = 'Prod' AND p.prod = i.id_type"
Dim dbCmd As OleDBCommand = Conn.CreateCommand
dbCmd.CommandText = sql
Dim dr As oleDBDataReader
dr = dbCmd.ExecuteReader
dgResults.DataSource = dr
dgResults.DataBind
dr.Close
dbCmd.Dispose
Conn.Close


Catch ex As Exception
lblErrMsg.Text = ex.Message()
Finally
If Conn.State = ConnectionState.Open Then
Conn.Close
End If
End Try

End Sub


End Class
End Namespace



The output is as follows:



In the above example, we assign the OleDBDataReader to the datasource property. When the databind method is invoked, the datagrid will automatically create the columns (PARTNO, ID, CURRENTSTEP, CURRENTQTY) using the information from the datareader and populate it. After this is done, we no longer need the datareader and can close it. We can also dispose the datacommand and also the database connection.

Repeater

Repeater does not automatically generate any items; you have specify the templates of the items you want to display.

The following is a sample of getting the same output (as the SimpleDataGrid) as above:

<%@ Page Language="vb" autoeventwireup="false" codebehind="SimpleRepeater.aspx.vb" Inherits="simpleRepeater.WebForm1" %>
<html>
<head>
</head>
<body>
<form runat="server">
<asp:Repeater id="drResults" runat="server">
<HeaderTemplate>
<table border="1" cellspacing="0">
<tr bgcolor="#FFC080">
<td>
LOT
</td>
<td>
PRODUCT
</td>
<td>
STEP
</td>
<td>
QTY
</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr bgcolor="#C0FFC0">
<td>
<%# DataBinder.Eval(Container, "DataItem.Lot") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Product") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Step") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Qty") %>
</td>
</tr>
</ItemTemplate>
<AlternatingItemTemplate>
<tr bgcolor="#80FF80">
<td>
<%# DataBinder.Eval(Container, "DataItem.Lot") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Product") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Step") %>
</td>
<td>
<%# DataBinder.Eval(Container, "DataItem.Qty") %>
</td>
</tr>
</AlternatingItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</form>
</body>
</html>


Using the repeater control, we now have to specify the html tags to specify how the data will display on the page. As shown in the above sample, we need to have the start table tag <table> and the end table </table>. To ensure that the tag is at the beginning, we place it at the HeaderTemplate and to ensure the end table tag is at the end, we place it at the FooterTemplate.

Subsequently, the code behind is identical for both with the only exception is the declaration of the variable (which I changed from dgResults to drResults. If you look at the code, everything else is identical (with the simpleDataGrid.aspx.vb):

'
' SimpleRepeater
' User: Strovek
' Date: 9/22/2005
'
' ========================================

Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Reflection
Imports System.Web.UI.WebControls

<Assembly: AssemblyTitle("Simple Data Repeater")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Programmer's Journal")>
<Assembly: AssemblyProduct("Simple Data Repeater")>
<Assembly: AssemblyCopyright("Programmer's Journal")>
<Assembly: AssemblyTrademark("Programmer's Journal")>
<Assembly: CLSCompliant(True)>
<Assembly: AssemblyVersion("1.00.0")>
<Assembly: AssemblyFileVersion("1.0.0")>

Namespace simpleRepeater

Public Class WebForm1
Inherits System.Web.UI.Page

Protected WithEvents drResults As Repeater
Protected WithEvents lblErrMsg As Label
Dim Conn As New _
OleDbConnection( _
System.Configuration.ConfigurationSettings.AppSettings("connStr"))

Function Ver() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Page_Load(sender As Object, e As EventArgs) _
Handles MyBase.Load
Response.Write("<title>Data Grid Sample - Version " _
& Ver() & "</title>")

fillGrid

End Sub

Private Sub fillGrid
Try
Conn.Open
Dim sql As String = _
"SELECT partno Lot, id Product, " _
& "currentstep Step, currentqty Qty " _
& "FROM partmaster p, system_lookup i " _
& "WHERE p.status = 'Active' AND " _
& "i.systemid = 'Prod' AND p.prod = i.id_type"
Dim dbCmd As OleDBCommand = Conn.CreateCommand
dbCmd.CommandText = sql
Dim dr As oleDBDataReader
dr = dbCmd.ExecuteReader
drResults.DataSource = dr
drResults.DataBind
dr.Close
dbCmd.Dispose
Conn.Close


Catch ex As Exception
lblErrMsg.Text = ex.Message()
Finally
If Conn.State = ConnectionState.Open Then
Conn.Close
End If
End Try

End Sub


End Class
End Namespace


DataList

A third control that can be used is to produce a formatted output is DataList. As it in the Repeater, we need to define the templates (Header, Item and AlternatingTemplate). However, the start table (<table>), end table (</table>) and the row tags are not necessary (<tr> and </tr>) This is how it can be done to produce similar output:

<%@ Page Language="vb" autoeventwireup="false" codebehind="SimpleDataList.aspx.vb" Inherits="simpleDataList.WebForm1" %>
<html>
<head>
</head>
<body>
<form runat="server">
<asp:DataList id="dlResults" runat="server" Border="1">
<HeaderTemplate>
<td bgcolor="#FFC080">
Lot</td>
<td bgcolor="#FFC080">
Product</td>
<td bgcolor="#FFC080">
STEP</td>
<td bgcolor="#FFC080">
QTY</td>
</HeaderTemplate>
<FooterTemplate></FooterTemplate>
<ItemTemplate>
<td bgcolor="#C0FFC0">
<%# DataBinder.Eval(Container, "DataItem.Lot") %>
</td>
<td bgcolor="#C0FFC0">
<%# DataBinder.Eval(Container, "DataItem.Product") %>
</td>
<td bgcolor="#C0FFC0">
<%# DataBinder.Eval(Container, "DataItem.Step") %>
</td>
<td bgcolor="#C0FFC0">
<%# DataBinder.Eval(Container, "DataItem.Qty") %>
</td>
</ItemTemplate>
<AlternatingItemTemplate>
<td bgcolor="#80FF80">
<%# DataBinder.Eval(Container, "DataItem.Lot") %>
</td>
<td bgcolor="#80FF80">
<%# DataBinder.Eval(Container, "DataItem.Product") %>
</td>
<td bgcolor="#80FF80">
<%# DataBinder.Eval(Container, "DataItem.Step") %>
</td>
<td bgcolor="#80FF80">
<%# DataBinder.Eval(Container, "DataItem.Qty") %>
</td>
</AlternatingItemTemplate>
</asp:DataList>
<br />
<asp:Label id="lblErrMsg" runat="server" forecolor="Red"></asp:Label>
</form>
</body>
</html>


Again the necessary codebehind is identical:

'
' SimpleDataList
' User: Strovek
' Date: 9/22/2005
'
' ========================================

Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Reflection
Imports System.Web.UI.WebControls

<Assembly: AssemblyTitle("Simple Data List")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Programmer's Journal")>
<Assembly: AssemblyProduct("Simple Data List")>
<Assembly: AssemblyCopyright("Programmer's Journal")>
<Assembly: AssemblyTrademark("Programmer's Journal")>
<Assembly: CLSCompliant(True)>
<Assembly: AssemblyVersion("1.00.0")>
<Assembly: AssemblyFileVersion("1.0.0")>

Namespace simpleDataList

Public Class WebForm1
Inherits System.Web.UI.Page

Protected WithEvents dlResults As DataList
Protected WithEvents lblErrMsg As Label
Dim Conn As New _ OleDbConnection(System.Configuration.ConfigurationSettings.AppSettings("connStr"))

Function Ver() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Page_Load(sender As Object, e As EventArgs) _
Handles MyBase.Load
Response.Write("<title>Data Grid Sample - Version " _
& Ver() & "</title>")
fillGrid
End Sub

Private Sub fillGrid
Try
Conn.Open
Dim sql As String = _
"SELECT partno Lot, id Product, " _
& "currentstep Step, currentqty Qty " _
& "FROM partmaster p, system_lookup i " _
& "WHERE p.status = 'Active' AND " _
& "i.systemid = 'Prod' AND p.prod = i.id_type"
Dim dbCmd As OleDBCommand = Conn.CreateCommand
dbCmd.CommandText = sql
Dim dr As oleDBDataReader
dr = dbCmd.ExecuteReader
dlResults.DataSource = dr
dlResults.DataBind
dr.Close
dbCmd.Dispose
Conn.Close


Catch ex As Exception
lblErrMsg.Text = ex.Message()
Finally
If Conn.State = ConnectionState.Open Then
Conn.Close
End If
End Try

End Sub


End Class
End Namespace


SimpleTable

When using Table Web control, we will need to use the similar approach as how it would be done using classic ASP. The HTML is relative simple but a lot more coding is needed.

The aspx page is as follows:

<%@ Page Language="vb" autoeventwireup="false" codebehind="SimpleTable.aspx.vb" Inherits="simpleTable.WebForm1" %>
<html>
<head>
<style type="text/css">.hColor {
BACKGROUND: #ffc080
}
.pColor {
BACKGROUND: #c0ffc0
}
.aColor {
BACKGROUND: #80ff80
}
</style>
</head>
<body>
<form runat="server">
<asp:Table id="tblResults" runat="server" CellSpacing="0" border="1"></asp:Table>
<br />
<asp:Label id="lblErrMsg" runat="server" forecolor="Red"></asp:Label>
</form>
</body>
</html>


This is the code behind:

'
' SimpleTable
' User: Strovek
' Date: 9/22/2005
'
' ========================================

Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Reflection
Imports System.Web.UI.WebControls

<Assembly: AssemblyTitle("Simple Data List")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Programmer's Journal")>
<Assembly: AssemblyProduct("Simple Data List")>
<Assembly: AssemblyCopyright("Programmer's Journal")>
<Assembly: AssemblyTrademark("Programmer's Journal")>
<Assembly: CLSCompliant(True)>
<Assembly: AssemblyVersion("1.00.0")>
<Assembly: AssemblyFileVersion("1.0.0")>

Namespace simpleTable

Public Class WebForm1
Inherits System.Web.UI.Page

Protected WithEvents tblResults As Table
Protected WithEvents lblErrMsg As Label
Dim Conn As New _ OleDbConnection(System.Configuration.ConfigurationSettings.AppSettings("connStr"))

Function Ver() As String
Return _
System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly.Location).FileVersion
End Function

Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Response.Write("<title>Data Grid Sample - Version " & Ver() & "</title>")

fillGrid

End Sub

Private Sub fillGrid
Dim tRow As TableRow
Dim tCell As TableCell
Dim isPrimary As Boolean = True
Dim bkColor As String

Try
Conn.Open
Dim sql As String = "SELECT partno Lot, id Product, " _
& "currentstep Step, currentqty Qty " _
& "FROM partmaster p, system_lookup i " _
& "WHERE p.status = 'Active' AND " _
& "i.systemid = 'Prod' AND p.prod = i.id_type"
Dim dbCmd As OleDBCommand = Conn.CreateCommand
dbCmd.CommandText = sql
Dim dr As oleDBDataReader
dr = dbCmd.ExecuteReader

tRow = New TableRow
tRow.CssClass = "hColor"
tCell = New TableCell
tCell.Text = "LOT"
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = "PRODUCT"
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = "STEP"
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = "QTY"
tRow.Cells.Add(tCell)
tblResults.Rows.Add(tRow)

While dr.Read
If isPrimary = True Then
bkColor = "pColor"
isPrimary = False
Else
bkColor = "aColor"
isPrimary = True
End If
tRow = New TableRow
tRow.CssClass = bkColor
tCell = New TableCell
tCell.Text = dr.Item("LOT")
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = dr.Item("PRODUCT")
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = dr.Item("STEP")
tRow.Cells.Add(tCell)
tCell = New TableCell
tCell.Text = dr.Item("QTY")
tRow.Cells.Add(tCell)
tblResults.Rows.Add(tRow)

End While
dr.Close
dbCmd.Dispose
Conn.Close


Catch ex As Exception
lblErrMsg.Text = ex.Message()
Finally
If Conn.State = ConnectionState.Open Then
Conn.Close
End If
End Try

End Sub

End Class
End Namespace


Html Control

A "HTML" tag can be converted into a html control by:

1. Adding a runat="server" in the properties.
2. Giving the tag an id.

You may ask why would you want to use a html control when we already have such a rich set of webcontrols available. The reason is:

1. There are just some controls that are not available as a html tag. One such control is the control for uploading file (<input type=file>)
2. To easily convert an existing page to a ASP.Net page.
3. Your familiarity with the tags.

However, note that a webcontrol equivalent of a html control will have more capabilities.

Caution
At this point, it is important to remember that we should not make everything to "runat=server", otherwise, you are increasing the overhead on the page and will cause the page to load very slowly. You will notice the slowdown as you increase the number of controls. In order to avoid this problem, you should keep in mind the following guideline:

1. Create a control (html or webcontrol) only if you want to control it - change attribute (appearance, visibility etc), change the value, obtain a value.
2. Disable tracking the state, you don't need the server to track the state changes, (ie. For cases where you do not need to maintain the values that were submitted to the server).

WebServices
Web services allows another interface for users to access your application (in addition to Webforms, Console program and ASP.Net). The .Net Framework makes creation of very simple. To do that, you just need to:

1. Create a asmx file which points to the
2. import System.Web.Services.
3. Add <WebMethod()> in front of the functions you want to expose. This can be a function or a subroutine. However, placing it in a subroutine will not have any return value.

Single Page sample
The following is a sample (singlepagewebsvc.asmx):

<%@ WebService language="VB" class="Svc1" %>

Imports System
Imports System.Web.Services
Imports System.Xml.Serialization

Public Class Svc1
Inherits System.Web.Services.WebService

<WebMethod> Public Function HelloWorld() As String
Return "Hello world"
End Function

End Class


You can then test the control by pointing to it using the web browser. (However, this can only be done if you are on the webserver (accessing via http://localhost/) - this is the security default for .Net Framework 1.1).

When you access the page, you will encounter the following page:



When you click the HelloWorld link, you will then have the following screen:



When invoked, it will produce the following output:

<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">Hello world</string>


You can also get the wsdl by placing a ?wsdl at the end of the asmx page. The following is the output:

<?xml version="1.0" encoding="utf-8" ?>
<wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
<s:element name="HelloWorld">
<s:complexType />
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld" />
</wsdl:message>
<wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse" />
</wsdl:message>
<wsdl:portType name="Svc1Soap">
<wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn" />
<wsdl:output message="tns:HelloWorldSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Svc1Soap" type="tns:Svc1Soap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
<wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://tempuri.org/HelloWorld" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Svc1">
<documentation xmlns="http://schemas.xmlsoap.org/wsdl/" />
<wsdl:port name="Svc1Soap" binding="tns:Svc1Soap">
<soap:address location="http://localhost/pj/chapter4/singlepagewebsvc.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>


First code behind sample
As in aspx, you can also use the code behind approach. For the code behind approach, the asmx just need to contain one line:

<%@ WebService Language="vb" Codebehind="FirstWebSvc.asmx.vb" Class="FirstWebSvc.Service1" %>

The following code behind will produce the exact same result as the previous example.

Imports System
Imports System.Web.Services

Namespace FirstWebSvc
Class Service1
Inherits System.Web.Services.WebService

<WebMethod> Public Function HelloWorld() As String
Return "Hello world"
End Function

End Class

End Namespace


Note that in both cases, the class inherits System.Web.Services.WebService. This is optional, we need to do that only if we want to access common asp.net objects such as application state and session state. So if you do not need to access such object, you don't need to inherit from the class as shown in the next sample (again will produce the exact same result):

ASMX page:

<%@ WebService Language="vb" Codebehind="SecondWebSvc.asmx.vb" Class="SecondWebSvc.Service1" %>


The code behind file:

Imports System
Imports System.Web.Services

Namespace SecondWebSvc
Class Service1

<WebMethod> Public Function HelloWorld() As String
Return "Hello world"
End Function

End Class

End Namespace


Consuming the Webservice
Once a web service is available (irrespective if it was written in any platform - could be java, cgi, .net etc), we can use it. If you are using Visual Studio for your programming, just add the webservice url as a web reference.

Using Visual Studio
In the Visual studio project, go to the solution explorer, highlight the reference, right click and then indicate add "Web Reference". You will then get the following screen (enter the url to the webservice as shown below then click Go):



When you click go, Visual studio will go to the web service url and search for all the exposed methods. You will then get the following screen:



You can then provide the name you will use to reference this webservice and click "Add Reference". The default name is the url in reverse (in our case is called localhost). If the webservice is http://fullasagoog.com/packages/googService.cfc?wsdl, then the default name will be com.fullasagoog. This is the namespace that will be used when you want to access the webservice.

In this example, we shall call it FirstWebSvc. Visual studio will create a Directory called FirstWebSvc and place it in a subdirectory called WebReferences. It will generate several files there but the main one that we are interested in is the Reference.vb. This is the proxy class that will use to communicate with the webservice.

The following is the content of the file:

'---------------------------------------------------------------------------
' <autogenerated>
' This code was generated by a tool.
' Runtime Version: 1.1.4322.2032
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </autogenerated>
'---------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports System
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Xml.Serialization

'
'This source code was auto-generated by Microsoft.VSDesigner,
'Version 1.1.4322.2032.
'
Namespace FirstWebSvc

'<remarks/>
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Web.Services.WebServiceBindingAttribute(Name:="Service1Soap", [Namespace]:="http://tempuri.org/")> _
Public Class Service1
Inherits System.Web.Services.Protocols.SoapHttpClientProtocol

'<remarks/>
Public Sub New()
MyBase.New
Me.Url = "http://localhost/pj/Chapter4/firstwebsvc.asmx"
End Sub

'<remarks/>
<System.Web.Services.Protocols.SoapDocumentMethodAttribute(_
"http://tempuri.org/HelloWorld", RequestNamespace:="http://tempuri.org/", _ ResponseNamespace:="http://tempuri.org/", _
Use:=System.Web.Services.Description.SoapBindingUse.Literal, _
ParameterStyle:=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)> _
Public Function HelloWorld() As String
Dim results() As Object = Me.Invoke("HelloWorld", New Object(-1) {})

Return CType(results(0),String)
End Function

'<remarks/>
Public Function BeginHelloWorld(ByVal callback As _
System.AsyncCallback, _
ByVal asyncState As Object) As System.IAsyncResult
Return Me.BeginInvoke("HelloWorld", New Object(-1) {}, callback, _
asyncState)
End Function

'<remarks/>
Public Function EndHelloWorld(ByVal asyncResult As _
System.IAsyncResult)
As String
Dim results() As Object = Me.EndInvoke(asyncResult)
Return CType(results(0),String)
End Function
End Class
End Namespace


As you can see below, the class has the namespace of the name we gave it and also a class called Service1. This are all derived from the wsdl descriptor, as shown below:

<wsdl:definitions targetNamespace="http://tempuri.org/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
<s:element name="HelloWorld">
<s:complexType/>
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld"/>
</wsdl:message>
<wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse"/>
</wsdl:message>
<wsdl:portType name="Service1Soap">
<wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn"/>
<wsdl:output message="tns:HelloWorldSoapOut"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Service1Soap" type="tns:Service1Soap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://tempuri.org/HelloWorld" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Service1">
<documentation/>
<wsdl:port name="Service1Soap" binding="tns:Service1Soap">
<soap:address location="http://localhost/pj/Chapter4/firstwebsvc.asmx"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

With the proxy class, we can then use the class as though it is another class from any assembly as shown in the code below:

Module Module1

Sub Main()
Dim wSvc As New FirstWebSvc.Service1

Console.WriteLine(wSvc.HelloWorld)

End Sub

End Module



When we run the program, it will return "Hello World".

Without Visual Studio
There is a utility provided with the .net sdk called wsdl.exe. If you install SDK at the default location, it will be in the following diretory:

C:\Program Files\Microsoft.NET\SDK\v1.1\bin

Click wsdl /? to get additional help. To generate the above proxy class, you can key in the following command:

"\Program Files\Microsoft.NET\SDK\v1.1\Bin\wsdl" /language:vb /o:firstwebsvc.vb http://localhost/pj/Chapter4/firstwebsvc.asmx

It will generate the following file:

'------------------------------------------------------------------------------
' <autogenerated>
' This code was generated by a tool.
' Runtime Version: 1.1.4322.2032
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </autogenerated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On

Imports System
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Xml.Serialization

'
'This source code was auto-generated by wsdl, Version=1.1.4322.2032.
'

'<remarks/>
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Web.Services.WebServiceBindingAttribute(Name:="Service1Soap", [Namespace]:="http://tempuri.org/")> _
Public Class Service1
Inherits System.Web.Services.Protocols.SoapHttpClientProtocol

'<remarks/>
Public Sub New()
MyBase.New
Me.Url = "http://localhost/pj/Chapter4/firstwebsvc.asmx"
End Sub

'<remarks/>
<System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/HelloWorld", RequestNamespace:="http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)> _
Public Function HelloWorld() As String
Dim results() As Object = Me.Invoke("HelloWorld", New Object(-1) {})
Return CType(results(0),String)
End Function

'<remarks/>
Public Function BeginHelloWorld(ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
Return Me.BeginInvoke("HelloWorld", New Object(-1) {}, callback, asyncState)
End Function

'<remarks/>
Public Function EndHelloWorld(ByVal asyncResult As System.IAsyncResult) As String
Dim results() As Object = Me.EndInvoke(asyncResult)
Return CType(results(0),String)
End Function
End Class


The proxy class generated code is the identical. You now have an option to either compile this as a separate .dll file (if you are going to use it with multiple programs and compile it with your main program. In order to compile the program, you need to reference three libraries:
  • system.web.services.dll
  • system.xml.dll
  • system.dll

The following is the command:

\WINDOWS\Microsoft.NET\Framework\v1.1.4322\vbc /t:library firstwebsvc.vb /r:system.web.services.dll,system.xml.dll,system.dll

This will generate firstwebsvc.dll referring to the firstwebsvc.dll, use the following command to compile:

\WINDOWS\Microsoft.NET\Framework\v1.1.4322\vbc consumeservice.vb /r:firstwebsvc.dll,System.Web.Services.dll,system.dll

Alternative, you can compile both files into one exe with the following command:

\WINDOWS\Microsoft.NET\Framework\v1.1.4322\vbc consumeservice.vb firstwebsvc.vb /out:consumeservice2.exe /r:System.xml.dll,System.Web.Services.dll,system.dll

Using Multiple Version of Framework at the same time
When you install the first .Net framework and it detects IIS, it will intercept all requests for ASP.Net application. When you install the second version of the Framework, the second version will detect that there is already one version that is responding to ASP.Net applications and will not replace (so existing applications will not break).

Assuming that you installed Framework 1.1 first. All ASP.Net application will be served by that version of 1.1. However, now you want to start using Framework 2.0 for some new applications. To do this, create a new virtual directory (application) - assuming the directory is called /SampleApp1. After creating the directory, use the aspnet_regiis.exe utility from Framework 2.0 and type the following command:

aspnet_regiis.exe -s W3SVC/1/ROOT/SampleApp1

After typing the above command, /SampleApp1 will use Framework 2.0 while the rest of the website will still continue to use Framework 1.1.

Each time you create a website within the server, IIS will automatically increment the number. The first website will be W3SVC/1/, subsequent ones will be W3SVC/2/, W3SVC/3/ etc.

Alternatively, the version of framework used for a specific application can also be set through the application properties as shown in the screen below: