Saturday, April 23, 2005

Using JavaScript to read a client-side file

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_javascript_to_read_a_clientside_file.htm]

One of the benefits of blogs is that you get to write about oddball topics just for fun. One such topic is using JavaScript to read a client-side file. Initially this wasn't possible - JavaScript was not designed to allow this due to security concerns. Imagine a malicious app reading/writing all your important system files! It is certainly not advisable due to security concerns. A good security overview of JavaScript is in the "Security" section at: http://www.quirksmode.org/js/intro.html

Most of the time if you do need a client file, you could use ASP.Net's file uploaded control to upload files from the client to the server.

However, it is still possible for JavaScript to read client files using ActiveX:

function ReadFromFile() {
    var strContent = ReadFileToString(document.Form1.TxtFileName.value);
    document.Form1.HdnContent.value = strContent;
    document.Form1.submit();
}

function ReadFileToString(strFileName) {
    var strContents;
    strContents = "";

    objFSO = new ActiveXObject("Scripting.FileSystemObject");
    if (objFSO.FileExists(strFileName)) {
        strContents = objFSO.OpenTextFile(strFileName, 1).ReadAll();
    }
   
    return strContents;
} //end of function

This script first uses the ActiveX Scripting.FileSystemObject to read the file, then stores the contents in a hidden field, and lastly submits to the server so it can do whatever it needs with that client data.

Note that you'll need to enable ActiveX objects in the browser, else you'll get an error like: "Error: Number:-2146827859 Description:Automation server can't create object". You can do this by:

  1. In Internet Explorer > Tools > Internet Options > Security > Custom Level
  2. Enabling or prompting "Initializing and Script Activex controls not marked as safe"

While this is a cute trick to know, again I emphasize be cautious of using it in any enterprise app due to security reasons.

Tuesday, April 19, 2005

Remove ListItems, by value, from the DropDownList

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/remove_listitems_by_value_from_the_dropdownlist.htm]

There are times when you'll want to add a set of items to a DropDownList, and then remove certain items. For example, you may databind a code-value table from a shared database, but then need to exclude potential values based on individual configuration settings.

The DropDownList.Items property is a ListItemCollection that supports the Remove and RemoveAt methods. The RemoveAt simply takes an index (of type integer) and removes the item at that specific index. However, referencing items by index can be delicate. For example, if the DropDownList added a blank space at the top to indicate that the field was optional, that would bump all the indexes off by one. For lookup dropdowns, where the text is a description and the value is a unique primary key, we'd like to be able to remove items by value.

The problem is that the Remove(string) method is based on a "string representation" of the list item, NOT just the value. So if we add items to a dropdown like so:

this.DropDownList1.Items.Add(new ListItem("Apple","101"));
this.DropDownList1.Items.Add(new ListItem("Banana","201"));
this.DropDownList1.Items.Add(new ListItem("Orange","301"));
this.DropDownList1.Items.Add(new ListItem("Mango"));

 The following will remove nothing:

this.DropDownList1.Items.Remove("Banana");    //Text
this.DropDownList1.Items.Remove("201");    //Value

Remove(string) works by first constructing a new ListItem from that string, and then removing that ListItem. In other words, Remove works acts like so:

this.DropDownList1.Items.Remove(new ListItem("Banana"));


This will create a new ListItem with both the text and description being the same ("201"). Therefore if the list items have different text and values, which they almost always do, then the Remove(string) method by itself won't work.

However, we can make our own method that cycles through the dropdown and removes if the value matches. Note that we could make this more reusable by sending in the dropdown as a ref parameter.

public static void RemoveByValue(ref DropDownList d, string strKey)
{
    for (int i = 0; i < d.Items.Count; i++)
    {       
        if (d.Items[i].Value == strKey)
        {
            d.Items.RemoveAt(i);
            return;
        }
    }
}

Given that dropdowns are only intended for small sets of data, cycling through all the items isn't a significant performance hit.

This provides a reusable method to remove items (by value) from a dropdown list.

Sunday, April 17, 2005

Maintaining Scroll Position on PostBacks

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/maintaining_scroll_position_on_postbacks.htm]

A classic problem is ASP.Net is then when you postback on a long page, you lose your position. For example, if you were at the bottom of the page and hit a server control, the page may refresh and put you at the top. This can be very frustrating for pages that are more than one screen long.

One solution is to use the Page property, SmartNavigation. This supposedly will persist position upon postback, but it is very delicate and can induce many rendering bugs. The page may work fine on your local machine, but may have all its styles messed up in other environments. I've tried using SmartNavigation before, only to have more hassle than its worth.

A more durable solution is described in an article by Steve Stchur: http://aspnet.4guysfromrolla.com/articles/111704-1.aspx. It walks through a solution and provides a custom control.

Monday, April 11, 2005

Adding and Finding UserControls

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/adding_and_finding_usercontrols.htm]

Dynamically adding a UserControl to a page provides great flexibility and code reuse. It's also relatively easy. You can add a control to a page by putting it in a PlaceHolder, and then retrieve it later using the Page.FindControl method. While there are other articles that address this, this blog post will highlight 3 tips:

  1. Make sure you give your added-control an ID so that you can reference it later
  2. Call the method that adds the controls from the OnInit method. This will then handle viewstate for you.
  3. Use Request.ApplicationPath to get the application path of the UserControl.

The code below provides a method, InitializeUserControl, which is called from the OnInit of the page. The method first gets the path of the UserControl, then loads it into the page, sets the ID, and then adds it to a placeholder. Note that if you don't set the ID, then you can't (easily) reference it later.

One convenient convention is having all directory paths end with a "/". By creating a utility method GetApplicationPath, we can wrap the Request.ApplicationPath method and add the "/" to the end. In real applications this Utility method would be abstracted to its own class.

public void InitializeUserControl()
{
   
// Put user code to initialize the page here
    string strUrl = GetApplicationPath() + "AddControls/WebUserControl1.ascx";
    UserControl uc = null;
    uc = (UserControl)Page.LoadControl(strUrl);
    uc.ID = "UC1";
    this.PlaceHolder1.Controls.Add(uc);
}

public static string GetApplicationPath()
{
    return System.Web.HttpContext.Current.Request.ApplicationPath + @"/";
} //end of method

We can find the control using the Page.FindControl method and the ID we assigned above. In the snippet below we cast the UserControl to the appropriate type (WebUserControl1), and then call its custom property MyNewProperty.

UserControl uc = null;
uc = (UserControl)Page.FindControl("UC1");
this.Label1.Text = ((WebUserControl1)uc).MyNewProperty;

Thursday, April 7, 2005

Extending WebControls to make your own Server Controls

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/extending_webcontrols_to_make_your_own_server_controls.htm]

There are several different groups of controls in ASP.Net, ranging from the foundational HtmlControls to the advanced Custom Server Controls:

  1. HtmlControls - standard HTML controls. (In the System.Web.UI.HtmlControls namespace).
  2. WebControls - ASP.Net controls. These include controls with similar Html counterparts like Button and Dropdown, but also have advanced controls like Validators and DataGrid (In the System.Web.UI.WebControls namespace).
  3. UserControls - Combines multiple controls into a single, reusable control (Inherits from System.Web.UI.UserControl class).
  4. Custom Server Controls
    1. Composite Custom Controls - create new controls from existing server controls.
    2. Rendered Custom Controls - create entirely new controls from raw HTML.
    3. Extended Controls - creates a new server control by inheriting from a previous control.

While everything ultimate reduces to Html, grouping controls together for reusable packages lets us save a lot of time. One such technique is to create an Extended Control by inheriting from an already existing control. This is great for when you want to build off a single control and extend its functionality. For example you could inherit from the TextBox class and add a new method ToLower which makes all the text lowercase.

There are several benefits

  1. Very easy - just create a new class that inherits from the control you need
  2. Has all the properties of the base control
  3. Has Design-time support (unlike User Controls)

This is easy to do, here's how:

  1. Create a new WebControlLibrary (compiles to a DLL). This is used for Composite and Rendered controls as well.
  2. Add a new class called TextBoxA, and make it inherit from System.Web.UI.WebControls.TextBox
  3. Add the methods and properties you need.

using System;using System.Web.UI.WebControls;namespace WebControlLibrary1{    ///     /// Summary description for TextBoxA.    ///     public class TextBoxA : System.Web.UI.WebControls.TextBox    {        public TextBoxA()        {        }        public void ToLower()         {            this.Text = this.Text.ToLower();        }        public string NewProp         {            get             {                if (ViewState["NewProp"] == null)                    return "";                else                    return ViewState["NewProp"].ToString();            }            set             {                ViewState["NewProp"] = value;            }        }    } //end of class}

 In the code snippet, we've added a method ToLower which references the textbox property "Text" and makes it lowercase. We've also created an arbitrary property NewProp and made it save data using ViewState.

We can load this easily into Aspx pages by first adding the DLL to the project like any other reference, and then adding the control itself to the toolbox. This lets us perform drag & drop operations like any other WebControl.

  1. In the ToolsBox, right click for the context menu and select "Add/Remove Items"
  2. A new window appears with two tabs: (1) .Net Framework Components (selected by default) and (2) COM Components. Within the tab there is a list of controls with checkboxes.
  3. Click the browse button to find the WebControlLibrary  we just created, and add it.
  4. Select the checkboxes for the controls you want to appear in the ToolBox.
  5. The controls are now in the toolbox, and you can drag them onto a page just like any other.

If you don't want to add it to your toolbox, then you can also directly type the Html. You need to first Register the assembly by putting something like this at the top:

<%@ Register TagPrefix="cc1" Namespace="WebControlLibrary1" Assembly="WebControlLibrary1" %>

And then reference the control in the page by:

<cc1:TextBoxA id="TextBoxA1" runat="server">cc1:TextBoxA>

In summary, this is a quick technique with high return, and therefore a good skill to have.

Monday, April 4, 2005

Unit Testing the Data Access Layer

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/unit_testing_the_data_access_layer.htm]

I had the opportunity to write an article for 4guysfromrolla - "Unit Testing the Data Access Layer".

"Unit testing is becoming very popular among .NET projects, but unit testing the Data Access Layer (DAL) still remains a common obstacle. The problem stems from the very nature of databases: they are slow, common, persistent, external, and relational. This conflicts the nature of unit tests, which should be quick, independent, and repeatable..."

You can read the entire article on the 4GFR website at:

http://aspnet.4guysfromrolla.com/articles/040605-1.aspx

Sunday, April 3, 2005

Resolving Http 500 Server Errors in ASP.Net and IIS 5.1

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/resolving_http_500_server_errors_in_aspnet_and_iis_51.htm]

Every developer likes to take certain things for granted - like ASP.Net being configured correctly with IIS. However sometimes there will just be something non-obvious that is preventing ASP.Net pages from being served, returning an Http 500 error instead. The frustrating thing is that these are often caused by system configuration issues that most developers should never need to worry about. Below I offer some advice intended for IIS 5.1 on a local dev machine that resolves some of these errors. The problem is most likely:

  1. IIS or ASP.Net simply not installed correctly
  2. Security configuration

0. The Basics

First, try the classic: reboot your machine.

If that doesn't work, then turn off friendly errors in IE (tools > options > turn of friendly errors). This will give you a more helpful message like "Logon failure: user not allowed to log on to this computer." instead of just "500 Server error".

1. Check that IIS works

Be aware of the difference between ASP.Net and IIS. ASP may be installed fine, but IIS may be messed up. Some general tips:

  • See if you can view http://localhost/iishelp
  • Try viewing an HTML page through IIS. This totally bypasses any ASP.Net issues.

If you can't view IIS, then perhaps IIS is disabled or turned off.

2. Other Web Servers interfering with IIS

If this is the first time you installed IIS, perhaps there is another webserver running on your machine that is interfering with it (or if you disabled IIS and installed another Web Server). For example sometimes Java developers have another server using port 80 that blocks IIS. Try turning this other server off.

3. Uninstall and reinstall IIS and ASP.Net

Perhaps uninstalling and reinstalling will fix it.

  • Restart IIS by typing iisreset at the command line.
  • Reinstall ASP.Net by typing "aspnet_regiis.exe -i" at the command line.

If that doesn't work, you can uninstall IIS and ASP.Net, rename the inetpub directory to effectively delete it (while still saving your files), and then reinstall IIS and ASP.Net. This may reset user account systems.

4. Set ASP.Net security permissions

Make sure that the ASP.NET user has rights on Inetpub. Add the ASPNET user to INETPUB folder's users (Security tab in Properties) and granted it full control.

5. Check the Local security policy

In Admin Tools > Local Security Settings > Local Policies > Security Options > Audit Shut down system immediantly if unable to log secuiryt aufits --> should be disabled.

6. Check the Registry

In the registry editor (type regedit at the command line), check HKEY_LOCAL_MACHINE\SYSTEM\CurrentControLSet\CONTROL\LSA\CrashOnAuditFail

Set default = "0" (should not be "value not set")

7. Check the Machine.config for security settings

As a last resort, change machine.config,probably located in a directory like: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\CONFIG\machine.config

Look for  the xml tag "

Note that this is highly un-secure, but may be useful for letting you continue development while your IT staff investigates issues further.

 

Conclusion

I've had the misfortune of needing to fix this on many laptops now, and these hard-learned-tips have helped me out.