Robert Robbins

A blog about using ASP.NET for web applications and my work experiences

  • Generate Test Data

    Entering test data can be a tedious chore. Recently I found a web site that makes it easier to populate your database tables with realistic data, This is a very useful online utility. It really saves me a lot of time because I often waste an incredible amount of time just deciding on a name for a fictitious user. I also waste time looking for real addresses, phone numbers, etc. 


  • ASP.NET For Punchcard Programming

    Recently I've been studying RPG II for the Infinite 36, an old IBM programming language that was designed for 80 column punchcards. RPG II is probably the most horrible programming language that man has ever devised. Sometimes your code is a single character which has to be in an exact column. There is also a fixed logic that you have to work around. But it is the matter of getting a character in the right column that gives me a pain. The Infinite 36 is an old emulator of the IBM System 36 for the PC. It has some green screen RPG editors but I prefer to use UltraEdit.


  • ASP.NET And Legacy MS-DOS Applications

    I'm currently studying FoxPro 2.6. For MS-DOS. Yes, the company I am working for is still using applications written for FoxPro 2.6! I have to run this old MS-DOS database system in Virtual PC. So the last programming book I've read was Charles Siegel's Mastering FoxPro 2 written in 1991. The applications are completely undocumented (grrrr!) so my first step was to create a data dictionary (aka database schema). In FoxPro this can be done through the command DISPLAY STRUCTURE.


  • Firefox Extension Development

    I've been working on my job skills to remain employable. A lot of people are losing their jobs and then getting depressed because they have nothing to do except look for a job. They should be spending their time more constructively. I don't have much to do at work so yesterday I worked through a tutorial on Firefox Extension Development. I use Firefox all day at work and home because its extensions are useful for web development. It will be cool to create my own custom extensions. Today I experimented with the localization aspect of a Firefox extension. There are several things that were left out of the tutorial.


  • Time Enough To Learn French - Assez de temps pour apprendre le français

    Pennsylvania has not passed a budget for over two months. I was laid off from my full time job because of that. Now that I am collecting unemployment, I’ve had enough spare time to learn French. I have read five books on the French language. I think learning French is more worthwhile than learning Silverlight!

    I can read enough French to explore French web sites. The French version of Wikipedia is especially useful in expanding my vocabulary. I also watch a home shopping channel via streaming video because the French spoken on that channel is very clear.

    If Pennsylvania does not pass a budget soon I will have time to learn German too!

    Pennsylvanie n'a pas passé un budget (ses dépenses en fonction de) depuis plus de deux mois. J'ai été licencié de mon emploi à temps plein à cause de cela. Maintenant que je suis la collecte de chômage, j'ai eu assez de temps libre pour apprendre le français. J'ai lu cinq livres sur la langue française. Je pense que l'apprentissage du français est plus intéressant que d'apprendre Silverlight!

    Je peux lire assez de français pour explorer les sites Internet français. La version française de Wikipédia est particulièrement utile pour élargir mon vocabulaire. Je regarde aussi un chaîne de téléachat via le streaming vidéo, car le français parlé sur ce chaîne est très claire.

    Si la Pennsylvanie ne passe pas d'un budget bientôt puis j'aurai le temps d'apprendre l'allemand aussi!


  • Studying French As A Second Language

    My vacation in Paris, France was a great success and fulfilled one of my dreams. You can read about my travels on my personal blog at:

    I’ve decided to learn French as a second language even though I did not need to know any French in Paris. Learning a foreign language requires a huge commitment of time and effort, far more than is necessary to learn a programming language. You need a really good reason to learn a second language. As a programmer I am all too familiar with the temptation to learn many computer languages which you’ll never actually use. It can be a waste of time to learn a programming language. I’ve already decided that life is too short to learn Java.

    It is actually quite hard to think of a good reason to learn French. Most of the reasons given for studying French as a second language don’t make any sense, especially if you don’t plan to move to France. For example, it does not matter how influential France is as a nation. You don’t need to know French to follow the international news relating to French foreign policy. You also don’t need to know French to vacation in France. That may infuriate the French, but it doesn’t make sense to spend years learning a language just so you can use it for one week when you’ll really need it, occasionally.

    The main reason I’ve decided to learn French is to explore the uncharted territory of their geek culture. It may surprise you to learn that there is such a thing as the French geek. The stereotype of the French as artists and intellectuals conceals a far more extensive culture. The French also love comic books, science fiction, horror, video games, and pop music. Their comic book industry, known as bandes dessinées or BD for short, rivals the Japanese manga but is virtually unknown here in the United States. I was in a Borders bookstore last week and saw an entire aisle devoted to Japanese manga in paperback books called tankōbon. There was a smaller bookshelf for Marvel, DC, and other American graphic novels. However I did not find a single bandes dessinées at Borders.

    In addition to an unexplored world of graphic novels, the French also publish many science fiction novels which are never translated into English. Some of this science fiction uses historical references to the reign of Louis XIII and the periods of French Baroque and Classicism in a way that is really bizarre and amusing. There are also spy novels and other forms of pulp fiction that will never be shared with the Anglosphere (i.e. the totality of (English-speaking) nations).

    I think it would be fun to explore this world of trashy French culture because it goes against the stereotype of the French as sophisticated snobs. I’ve become pretty bored with American culture and its predictable output.

    So what does this have to do with programming? Well, programmers are often geeks so I’m staying within my culture with this new obsession. I also intend to visit many more French web sites and I’ll keep an eye out for any innovative uses of technology that have not made it to the English Internet. 


  • World Wide Web - Internationalization

    I’m going to Paris France for my vacation this year! I’m very excited about it because I’ve always been a bit of a Francophile. Unfortunately, I don’t speak French so, being a IT professional, I’ve sought a technological solution for the language barrier. I’m not sure that I’ll need to speak any French because I’m going on a guided tour which should reduce my interaction with the locals.

    I’ve already blogged about ASP.NET’s support for internationalization and browser languages on my WordPress blog at: ASP.NET 2.0 Culture – Web Site Internationalization. I also blogged about Elgg’s support for languages at Elgg Languages. I’ve done some additional research since then into how PHP supports internationalization and I looked at how all the browsers implement language settings.

    Firefox and Opera are the best browsers to use if you are working on making a web site international because they are the only browsers that allow you to indicate the browser language in the user agent string. In Firefox:

    1. To change the browser language type about:config in the address bar and press Enter.
    2. Enter general.useragent.locale in the Filter textbox.
    3. Change en-US to fr-FR or change it back to en-US.

    However, I don’t know of a single web site that attempts to detect the browser language by checking the user agent string which will almost always indicate en-US even when your browser language is not set to that. Even with my browser language set to fr-FR, only Google, Microsoft, and Facebook accommodated me with French text. There seems to be some debate over whether it is a good idea to detect browser language settings. Some developers argue that providing content in the appropriate language based on the browser language is the proper way to handle internationalization while others argue that you need to give visitors a means to explicitly change the language using flags because it has become the expected method. In my opinion, you should use both methods because a bilingual visitor will not want to be stuck with the version of the site that matches his browser language settings.

    Internet Explorer 8 seems to totally ignore the browser language settings. Even Google appears in English when I set my browser language to fr-FR in Internet Explorer 8. Only Google ignores the browser language settings for Internet Explorer 8. The Safari browser provides no means to change the browser language although you can change the application interface into French by copying the fr.lproj folder over the en.lproj folder. Opera does allow you to change the preferred language for web pages and you can change the user interface language to French by downloading the ouw960_fr.lng file which causes the user agent string to change to: Opera/9.63 (Windows NT 5.1; U; fr) Presto/2.1.1.

    The browser language setting is communicated to the web server using the Accept-Language header in each request. In ASP.NET 2.0 you really don’t need to deal with this because the framework has built-in support for localization using the page directives UICulture and Culture. However, if you want to redirect to a page based on the browser language then you can use the following code in your Global.asax file:

       1: Sub Application_Start(ByVal sender As [Object], ByVal e As EventArgs)
       2:         Dim LanguageArray As String() = System.Web.HttpContext.Current.Request.UserLanguages
       3:         If LanguageArray(0).Contains("fr") Then
       4:             System.Web.HttpContext.Current.Response.Redirect("bonjour.htm")
       5:         End If
       6: End Sub

    In PHP you can get the Accept-Language header using the server variable $_SERVER['HTTP_ACCEPT_LANGUAGE'];  and then set the locale using  setlocale(LC_ALL, 'fr-FR@euro', 'fr-FR', 'fr-fr');  which will automatically format the date using French for the month names and weekday names. You then need to set the Time Zone using putenv("TZ=Europe/Paris"); to get a 24 hour time in the Central European Time zone. On a Windows server the money_format function will not be available to you so you’ll need to define your own function for that.

    But I’ve found a more interesting technological solution for the language barrier. I bought a Pocket PC translator from ECTACO. This device looks like a Pocket PC and it is Microsoft Windows Powered but it is really a highly specialized device with a built-in microphone and large speaker. I’m not sure what operating system it is running but it does not look like Windows Mobile 2003. I think its audio phrasebook may come in handy although it could make an awkward social situation even more awkward. The original SD card did not leave enough room for any additional audio files but I managed to copy everything over to a 2 GB SD card so I could put more MP3s on it.

    Find more videos like this on VloggerHeads


  • Book Review - ASP.NET 3.5 Social Networking

    I recently finished reading the book ASP.NET 3.5 Social Networking by Andrew Siemer. I was very interested in this book because I want to build a social networking site designed for vloggers and ASP.NET is my area of expertise. Currently I'm working to customize the Elgg social networking web application using PHP because it is open source and has an active developer community.

    Unfortunately, developing a social networking application is a big project, not something the lone developer can accomplish. It really requires an integrated suite of web applications with modules for blogging, forums, groups, media galleries, etc. Andrew Siemer has done an impressive job of creating the framework of a social networking web application as documented in his book but it is by no means feature complete. There was no mention in the book of any plans to make this an open source project.

    The author has clearly been keeping up with changes in technology and methodology. As far as ASP.NET 3.5 goes, he made extensive use of LINQ to SQL but was unable to use MVC which wasn't available at the time, although he managed to implement a MVP (Model View Presenter) design pattern. He also made use of open source tools like Lucene.NET, MemCached, StructureMap, and NUnit. I don't have any experience with those tools and I'm not up to speed with ASP.NET 3.5 but I considered this a bonus because I learned something new.

    Even though I'm not using ASP.NET on my social networking project I still wanted to read this book to pick up on some tips on how to design such an application. Andrew Siemer provides plenty of explanation for his design decisions including how his database schema is structured according to the project requirements. This can eliminate one of the biggest stumbling blocks to a large project because a lot of time is wasted just figuring out how to design the database. Elgg is using the Entity Attribute Value model for its database and there is some debate over how well this will scale.

    Although Andrew Siemer added considerable complexity to his project by using StructureMap, I guess this is wise because a social networking web application needs to be flexible. This design decision would definitely make sense for an open source project. StructureMap is a Dependency Injection tool written in C# for .NET development. StructureMap is also a generic "Plugin" mechanism for flexible and extensible .NET applications. In other words, it allows other developers to replace an object with some other implementation.

    Social networking sites tend to get a lot of feature requests and suggestions from their large community of users. You really have to participate on several sites to get a sense of what the users are likely to need. Although the author anticipated many potential issues and requirements he did neglect to include some standard features. For example, he does not provide a means for members to customize their profile though CSS. Elgg also does not allow users to design their profile page and I know vloggers would miss this as they currently have a lot of fun creating new banners on Vloggerheads. I believe that vlogging is essential to create a really strong sense of online community so I found the media gallery component of the design particularly lacking. But you would need an entire video sharing module to really address that need. That is a huge project in its own right. I've learned that comment threading is really important for social networking sites. Without it you get users typing the @ sign in their comments to indicate who they are replying to and that is just an awkward user hack to work around a serious design flaw in the application.

    Although this is a thick book, 556  pages, most of the content is code listings so you won't need to invest too much time in reading it. You may feel a little lost if you haven’t been following the rapid changes in the ASP.NET framework and the additional open source tools require additional work to familiarize yourself with them because they are not part of the ASP.NET framework.


  • Page Lifecycle Sample Code

    Yesterday I attempted to create an ASP.NET page which used every single page event in order to better understand how to use each event. You cannot find any sample code for many page events. I managed to write code for every event except Page_AbortTransaction, Page_CommitTransaction, Page_DataBinding, and Page_Disposed which aren't being fired for this page. I tried to use meaningful examples but some of the code may be pointless. Let me know if you see any mistakes or have suggestions for improvements because this has gone into my notes.

    The ASPX page includes a placeholder for adding a dynamic control and a GridView for experimenting with control rendering. I used the Northwind database for sample data. I also used a master page.

       1: <%@ Page Language="VB" MasterPageFile="~/Default.master" AutoEventWireup="false" CodeFile="LifeCycle.aspx.vb" Inherits="LifeCycle" title="ASP.NET 2.0 Life Cycle" %>
       2: <%@ MasterType VirtualPath="~/Default.master" %>
       3: <asp:Content ID="Content1" ContentPlaceHolderID="Main" Runat="Server">
       4:     <asp:PlaceHolder runat="server" id="LabelPlaceHolder" />
       5:     <br />
       6:     <asp:GridView ID="GridView1" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
       7:         <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
       8:         <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
       9:         <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
      10:         <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
      11:         <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
      12:         <EditRowStyle BackColor="#999999" />
      13:         <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
      14:     </asp:GridView>
      15: </asp:Content>

    The VB code behind file uses a text file trace listener to log the page events as they are fired. That actually has to be set up in the web.config file. The Page_PreRenderComplete has some code commented out. This code adds the HTML rendered by the entire page to the log file but it causes an error about an extra form tag so I only left it in for debugging purposes. The only interesting things I'm doing is adjusting the column header text for the GridView and adding a top row. Some of the other page event code could use better examples.

       1: Imports System.Diagnostics
       2: Imports System.Data
       3: Imports System.Data.SqlClient
       4: Imports System.IO
       6: Partial Class LifeCycle
       7:     Inherits System.Web.UI.Page
       9:     Protected Sub Page_AbortTransaction(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.AbortTransaction
      10:         System.Diagnostics.Trace.WriteLine("Page_AbortTransaction " & Now())
      11:     End Sub
      13:     Protected Sub Page_CommitTransaction(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.CommitTransaction
      14:         System.Diagnostics.Trace.WriteLine("Page_CommitTransaction " & Now())
      15:     End Sub
      17:     Protected Sub Page_DataBinding(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DataBinding
      18:         System.Diagnostics.Trace.WriteLine("Page_DataBinding " & Now())
      19:     End Sub
      21:     Protected Sub Page_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
      22:         System.Diagnostics.Trace.WriteLine("Page_Disposed " & Now())
      23:     End Sub
      25:     Protected Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Error
      26:         ' Use this event to log a page error
      27:         Dim objException As Exception = Server.GetLastError().GetBaseException()
      28:         System.Diagnostics.Trace.WriteLine(objException.ToString())
      29:         System.Diagnostics.Trace.WriteLine("Page_Error " & Now())
      30:     End Sub
      32:     Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
      33:         ' Use this event to read or initialize control properties.
      34:         Dim ContentPlaceHolder1 As ContentPlaceHolder = CType(Master.FindControl("Main"), ContentPlaceHolder)
      35:         Dim LabelPlaceHolder As PlaceHolder = CType(ContentPlaceHolder1.FindControl("LabelPlaceHolder"), PlaceHolder)
      36:         Dim lblMessage As Label = CType(LabelPlaceHolder.FindControl("lblMessage"), Label)
      37:         lblMessage.ForeColor = Drawing.Color.Black
      38:         System.Diagnostics.Trace.WriteLine("Page_Init " & Now())
      39:     End Sub
      41:     Protected Sub Page_InitComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.InitComplete
      42:         ' Use this event for processing tasks that require all initialization be complete.
      43:         Dim ContentPlaceHolder1 As ContentPlaceHolder = CType(Master.FindControl("Main"), ContentPlaceHolder)
      44:         Dim LabelPlaceHolder As PlaceHolder = CType(ContentPlaceHolder1.FindControl("LabelPlaceHolder"), PlaceHolder)
      45:         Dim lblMessage As Label = CType(LabelPlaceHolder.FindControl("lblMessage"), Label)
      46:         lblMessage.ToolTip = "Tooltip added during InitComplete event."
      47:         ' Set a value in the viewstate
      48:         Me.ViewState("Author") = "Robert S. Robbins"
      49:         System.Diagnostics.Trace.WriteLine("Page_InitComplete " & Now())
      50:     End Sub
      52:     Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
      53:         ' Use the OnLoad event method to set properties in controls and establish database connections.
      54:         Dim cn As New SqlConnection
      55:         Dim objSqlCommand As New SqlCommand
      56:         cn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings("Northwind").ToString()
      57:         cn.Open()
      58:         objSqlCommand.Connection = cn
      59:         objSqlCommand.CommandText = "SELECT ProductID, ProductName, UnitPrice FROM Products ORDER BY ProductName"
      60:         Dim objDataSet As DataSet = New DataSet()
      61:         Dim objSqlDataAdapter As SqlDataAdapter = New SqlDataAdapter()
      62:         objSqlDataAdapter.SelectCommand = objSqlCommand
      63:         objSqlDataAdapter.Fill(objDataSet)
      64:         GridView1.DataSource = objDataSet
      65:         GridView1.DataBind()
      66:         cn.Close()
      67:         System.Diagnostics.Trace.WriteLine("Page_Load " & Now())
      68:     End Sub
      70:     Protected Sub Page_LoadComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LoadComplete
      71:         ' Use this event for tasks that require that all other controls on the page be loaded.
      72:         ' Example: Get data from a GridView row after databinding has occurred.
      73:         System.Diagnostics.Trace.WriteLine(GridView1.Rows.Item(5).Cells(1).Text, "Row 6, Cell 2")
      74:         System.Diagnostics.Trace.WriteLine("Page_LoadComplete " & Now())
      75:     End Sub
      77:     Protected Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
      78:         ' Check the IsPostBack property to determine whether this is the first time the page is being processed.
      79:         If Page.IsPostBack = False Then
      80:             ' Create or re-create dynamic controls.
      81:             Dim lblMessage As New Label
      82:             lblMessage.ID = "lblMessage"
      83:             lblMessage.Width = 500
      84:             lblMessage.Text = "Dynamically created control."
      85:             Dim ContentPlaceHolder1 As ContentPlaceHolder = CType(Master.FindControl("Main"), ContentPlaceHolder)
      86:             Dim LabelPlaceHolder As PlaceHolder = CType(ContentPlaceHolder1.FindControl("LabelPlaceHolder"), PlaceHolder)
      87:             LabelPlaceHolder.Controls.Add(lblMessage)
      88:         End If
      89:         System.Diagnostics.Trace.WriteLine("Page_PreInit " & Now())
      90:     End Sub
      92:     Protected Sub Page_PreLoad(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreLoad
      93:         ' Use this event if you need to perform processing on your page or control before the Load event.
      94:         ' Viewstate should be available now because it has been loaded.
      95:         System.Diagnostics.Trace.WriteLine(Me.ViewState.Values.Count, "ViewState Count")
      96:         ' The Request object should be available now. 
      97:         System.Diagnostics.Trace.WriteLine(Request.ServerVariables("SCRIPT_NAME"), "Script Name")
      98:         System.Diagnostics.Trace.WriteLine("Page_PreLoad " & Now())
      99:     End Sub
     101:     Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
     102:         ' Use the event to make final changes to the contents of the page or its controls.
     103:         Dim ContentPlaceHolder1 As ContentPlaceHolder = CType(Master.FindControl("Main"), ContentPlaceHolder)
     104:         Dim GridView1 As GridView = CType(ContentPlaceHolder1.FindControl("GridView1"), GridView)
     105:         Dim objGridViewRow As GridViewRow
     106:         objGridViewRow = New GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)
     107:         Dim objTableCell As TableCell = New TableCell()
     108:         Dim literal As LiteralControl = New LiteralControl()
     109:         literal.Text = "Northwind Products"
     110:         objTableCell.Controls.Add(literal)
     111:         objTableCell.ColumnSpan = 3
     112:         objTableCell.HorizontalAlign = HorizontalAlign.Center
     113:         objTableCell.ForeColor = Drawing.Color.White
     114:         objTableCell.BackColor = Drawing.Color.Black
     115:         objGridViewRow.Cells.Add(objTableCell)
     116:         GridView1.Controls(0).Controls.AddAt(0, objGridViewRow)
     117:         System.Diagnostics.Trace.WriteLine("Page_PreRender " & Now())
     118:     End Sub
     120:     Protected Sub Page_PreRenderComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRenderComplete
     121:         ' At this stage of the page life cycle, all controls are created, any pagination required is completed, and the page is ready to render to the output.
     122:         ' Example: The column headings of the gridview should be accessible now.
     123:         Dim ContentPlaceHolder1 As ContentPlaceHolder = CType(Master.FindControl("Main"), ContentPlaceHolder)
     124:         Dim GridView1 As GridView = CType(ContentPlaceHolder1.FindControl("GridView1"), GridView)
     125:         GridView1.HeaderRow.Cells(0).Text = "Product ID"
     126:         GridView1.HeaderRow.Cells(1).Text = "Product Name"
     127:         GridView1.HeaderRow.Cells(2).Text = "Unit Price"
     128:         ' Get the rendered HTML
     129:         'Dim objStringBuilder As New StringBuilder()
     130:         'Dim objStringWriter As New StringWriter(objStringBuilder)
     131:         'Dim objHtmlTextWriter As New HtmlTextWriter(objStringWriter)
     132:         'Me.RenderControl(objHtmlTextWriter)
     133:         'Dim strGridViewHTML As String = objStringBuilder.ToString()
     134:         'System.Diagnostics.Trace.WriteLine(strGridViewHTML)
     135:         System.Diagnostics.Trace.WriteLine("Page_PreRenderComplete " & Now())
     136:     End Sub
     138:     Protected Sub Page_SaveStateComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SaveStateComplete
     139:         ' Use this event perform tasks that require view state to be saved, but that do not make any changes to controls.
     140:         ' Example: Lets store some extraneous information in the view state
     141:         Me.ViewState("Date") = Now()
     142:         System.Diagnostics.Trace.WriteLine("Page_SaveStateComplete " & Now())
     143:     End Sub
     145:     Protected Sub Page_Unload(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Unload
     146:         ' Use this event to do final cleanup work, such as closing open files and database connections, or finishing up logging or other request-specific tasks.
     147:         System.Diagnostics.Trace.WriteLine(Me.ViewState.Values.Count, "ViewState Count")
     148:         System.Diagnostics.Trace.WriteLine("Page_Unload " & Now())
     149:     End Sub
     150: End Class