Tuesday, March 8, 2005

How to reference a UserControl from a DataGrid

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

Sometimes you need to have a UserControl in a DataGrid, and sometime you also need to reference that control to either get/set its values. In ASP.Net, this is straight forward.

First let's create a UserControl and host Aspx page. The important sections are below, but you can download it here.

UserControl

Html:

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="UCSimple.ascx.cs" Inherits="UIDemo.UC.UCSimple" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%>
<asp:TextBox id="TextBox1" runat="server">asp:TextBox>
<
asp:Button id="Button1" runat="server" Text="Button">asp:Button>
<
asp:Label id="Label1" runat="server">Labelasp:Label>

CodeBehind:

private void Page_Load(object sender, System.EventArgs e)
{
   
// Put user code to initialize the page here
}

private void Button1_Click(object sender, System.EventArgs e)
{
    this.Label1.Text = this.TextBox1.Text;
}

public string TextValue
{
    get
    {
        return this.TextBox1.Text;
    }
    set
    {
        this.TextBox1.Text = value;
    }
}

Host Aspx Page

CodeBehind

private void Page_Load(object sender, System.EventArgs e)
{
   
// Put user code to initialize the page here
    if (!Page.IsPostBack)
    {
        string[] astr = new string[] {"aaa","bbb","ccc"};
        this.DataGrid1.DataSource = astr;
        this.DataGrid1.DataBind();
    }
}

private const int _UCColumn = 0;
private const int _UCControlPosition = 1;

private void Button1_Click(object sender, System.EventArgs e)
{
    int intRow = Convert.ToInt32(this.TxtRow.Text);
    UCSimple uc = (UCSimple)this.DataGrid1.Items[intRow].Cells[_UCColumn].Controls[_UCControlPosition];
    uc.TextValue = this.TxtSet.Text;

}

private void BtnGet_Click(object sender, System.EventArgs e)
{
    int intRow = Convert.ToInt32(this.TxtRow.Text);
    UCSimple uc = (UCSimple)this.DataGrid1.Items[intRow].Cells[_UCColumn].Controls[_UCControlPosition];
    this.LblGet.Text = uc.TextValue;
}

private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e)
{
   
//Then this fires too.
    e = e;
}

private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
   
//First this fires
    e = e;
    UCSimple uc = (UCSimple)e.Item.Cells[_UCColumn].Controls[_UCControlPosition];
    this.LblGet.Text = uc.TextValue;

}

Get the UserControl

We can reference the control absolutely:

UCSimple uc = (UCSimple)this.DataGrid1.Items[intRow].Cells[_UCColumn].Controls[_UCControlPosition];

or from the argument in an individual row.

UCSimple uc = (UCSimple)e.Item.Cells[_UCColumn].Controls[_UCControlPosition];

Note what's going on:

  1. Get the row (via .Items[intRow] or just e.Item)
  2. Then get the column (via .Cells[_UCColumn] )
  3. Then get the control within that cell - a single cell can have multiple controls.
  4. Then cast that as the UserControl type that you need.
  5. Then reference the get/set of any public property.

This will allow you to reference user controls from either outside the grid, or with respect to any row.

Monday, March 7, 2005

Passing client data to the server

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

In my last post, I discussed how to set hidden fields in UserControls to ensure that the control is reusable. This post will expand on that, showing how to use hidden fields as a bridge to pass data between the client and server.

There are several cases where you'd want to have a client-side GUI control, let the user trigger it, do some client side activity first (validation, prompt, etc...), and then send the appropriate data from that control back to the server. For example:

  • MessageBox - Get the value of the clicked button.
  • Image Map - Get the value of the selected region.
  • Dynamically generated links - Get the specific link that was selected.

The basic approach to do this is:

  1. Create a hidden field, runat=server, viewstate enabled (this is default) to store the client side value.
  2. In the CodeBehind of the UserControl, wrap that hidden field in a public property This is especially useful if there's any transformation on it, such as a default value if empty, or encoding/decoding. It also easily lets external objects, such as the hosting page or nested control, get the data because the hidden field should be protected.
  3. Have your client-side object call a JavaScript function, passing in the UserControl ClientId.
  4. In that function, (1) set the hidden field to the value you need, and (2) submit the form.

For example, I created a UserControl with a client-side image map that returns the selected region to the server.

I first added the hidden field "HdnRegionId" (runat=server).

In the code-behind, I added the property:

public string RegionId
{
    get
    {
        return this.HdnRegionId.Value;
    }
}

Still in the code-behind, I also created a public instance variable strClientId to store the User Control's Client Id. This is set every time in the UC page load:

public string strClientId;

private void Page_Load(object sender, System.EventArgs e)
{
    strClientId = this.ClientID;
}

Moving to the Aspx page, I created this JavaScript method in the Html:

function GetRegion(strKey, strClientId) {
    eval('document.forms[0].' + strClientId + '_HdnRegionId').value = strKey;
    document.forms[0].submit();
}

However, I still need a client-side object to call the javascript method:


   
   

For review, here's the flow:

  1. Server:
    1. Page_Load instantiates strClientId variable to hold the UserControl's ClientID.
  2. Client:
    1. Aspx page is rendered, all the <%= strClientId %>"> store the actual UserControl id.
    2. User does some client action, like clicking an image on the image map. This triggers the JavaScript method GetRegion, passing in both the value and ClientId.
    3. Given the UserControl client Id, the JavaScript method dynamically assembles the code needed to set the hidden field on that UserControl. It then submits the form, returning flow to the server.
  3. Server:
    1. Optional logic can be done in the Page_Load.
    2. The hidden field value is persisted in viewstate and is now accessible to other controls.

Sunday, March 6, 2005

Setting hidden fields in a User Control

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

Hidden fields are so powerful in UserControls because they are a bridge between the client and server. JavaScript can set a hidden field value, and then the server can get that value (given that the field is runat=server). Therefore it is very common for UserControls to need to set the values of hidden fields from the client.

While it's easy to hard-code a simple case, truly reusable controls (such as calendars or employee look-ups) are more tricky. It can especially be frustrating when you've created a control, and then a co-worker tells you it's "broken" because they're using it in a legitimate way that you didn't anticipate. There are several cases that any such control should pass:

  1. Can you change the name of an instance of the control.
  2. Can it be nested in another UserControl?
  3. Can multiple instances of the UserControl be on the page at once?
  4. Can it handle the page's form name being changed (it might not always be Form1)?

There are two other issues to at least be aware of:

  1. Does it have external dependencies, like relying on Session State or QueryString values?
  2. Can it run in both IE and NN?

Ultimately the HTML to reference the hidden field from within the same page (i.e. not a popup) is something like:

document.Form1.NestedControl1_MyUserControl_HiddenField.value

We can see that here the Form and UserControl name are both hard-coded. The hidden field is prefixed with the UserControl Id. We can handle the form by referencing it as forms[0] instead of merely the default name like "Form1". Note that an Aspx page should only have 1 form (runat=server) per page, so this is a fair assumption.

We can handle the nested UserControl by making the name as a string and choosing either function below to translate this into actual JavaScript code:

  • use getElementById(string) - given the Id as a string, get the control.
  • use eval(string) - given the script as a string, evaluate it.

For example, we could pass in the variable strUcName (which contains the UC name) into a JavaScript function to reference the following hidden field:

document.getElementById(strUcName + '_HdnOrgId').value =myValue;

The UserControl name, strUcName, can be obtained from the UserControl itself at the server with ClientID.

By dynamically assembling the JavaScript to reference the hidden field, referencing the form as forms[0] instead of a hard-coded name, and passing in the UserControl's ClientID, we fulfill the first four criteria listed above. I will cover the other two criteria in another post.

Thursday, March 3, 2005

Brief Overview of Globalization

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

It is becoming more and more common to globalize a web applications, and fortunately .Net provides the features to easily do this.

Developing Web Applications With MS VB and C# explains of three main strategies (quoted from pg. 680):

ApproachDescriptionBest for
Detect and redirectCreate a separate Web application for each supported culture, and then detect the user's culture and redirect the request to the appropriate application.Application with lots of test content that requires translation and few executable components.
Run-time adjustmentCreate a single Web application that detects the user's culture and adjusts output at run time using format specifies and other tools.Simple applications that present limited amounts of content.
Satellite assembliesCreate a single Web application that stores culture-dependent strings in resource files that are compiled into satellite assemblies. At run time, detect the user's culture and load strings from the appropriate assembly.Applications that generate content at run time or that have large executable components.

I personally have used the resource files. While there are a lot of online tutorials already out there and reference, I have found a couple useful techniques not popularly discussed:

One of the problems with globalization is the Left-To-Right or Right-To-Left direction of text. This can complicate string concatenation. For example the expression: "Hello" + strYourName + " There" assumes a LTR direction that's fine for English, but not Arabic. Therefore I found it useful to create a GlobalUtilities.Concat method that takes an array of variables and the neutral culture (like "en" or "de"), and concatenates those variables in the correct direction.

Another problem is performing complex business logic, such as concatenating variables to create an output message to the user, or formatting DateTimes. This requires knowing the culture so that you can carry out the logic, or even get resources from the resource file. I have found it convenient to pass in the culture as a parameter into the object's constructor. I can then easily test those error-prone globalization methods.

Another tip I'm finding useful is applying FxCop's Globalization rules:

  • Avoid duplicate accelerators
  • Do not hardcode locale specific strings
  • Do not pass literals as localized parameters
  • Set locale for data types
  • Specify CultureInfo
  • Specify IFormatProvider
  • Specify MessageBoxOptions

Monday, February 28, 2005

How to Test Private and Protected methods

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

I published an article on CodeProject How to Test Private and Protected methods.

This article explains some theory behind testing/not testing private methods, and then provides and walks through a downloadable code sample to demonstrate these testing techniques.

Sunday, February 27, 2005

Tips to Handle Config files for Testing and Deployment

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

.Net provides Config files to store configuration values for applications. Each application (an executable or web application) can have its own config file. Two common config files are:

  • Web.Config - used by an ASP.Net or Web Service project.
  • App.Config - used by executables like console apps or Windows Forms.

For example, a standard solution might include a web application (with a web.config) and a Console application to run unit tests (with a App.Config). There are challenges with config files for both testing and deployment:

  • Testing - you may want to localize the config file to the test environment. For example, the app.config may store the database connection string, and you may want to set this connection string to the test database.
  • Deployment - Each environment should have its own config. When you deploy an app to a new environment, you need to also have the correct config deployed too.

This prompts three questions:

  • Should you include the config in the project file?
  • How do you get the latest version of the config?
  • How do you localize the config for a specific environment?

Let's look at each one of these:

Should you include the config in the project file?

Having a file in the physical directory, and having the file listed in the project are two different things. If in Visual Studio, Solution Explorer, you click "Show all files", you'll see all the files in the physical directory that aren't listed in the proj file.

 ProCon
Include
  • Easy to get the latest (just do a Get-Latest from VSS)
  • Can be used by NUnit if you step through your tests in Visual Studio.

 

  • Hard for each developer to have their own individual copy.
Exclude
  • Easier for MSI Installer. If the config file isn't included, then deploying the MSI won't overwrite any existing config. This makes it very easy to redeploy the application without breaking the config.
  • Each developer keeps local copy, doing a get-latest won't overwrite your localized copy because VS.Net's source control features only update files that are part of the project.
  • Config won't be available to NUnit or TestDriven.Net Plug-in (a free plug-in to let you run NUnit in Visual Studio).

How do you get the latest version of the config?

Two ways to get the latest version of a file:

  • Pull from Visual Studio (such as doing a "Get-Latest")
  • Push from VSS.

How do you localize the config for a specific environment?

Perhaps the best way to localize configs for your environment is to have a localized version in another folder, and then have a DOS script that can copy that localized version into the project folder.

Suggestion

Based on these questions, I've found the following parameters to work well.

Config FileInclude/ExcludeWhyHow to get latestLocalize
Web.Config - used for web application.Exclude
  • Best for deployment. Re-running the MSI installer doesn't override the config (because it's not included in the MSI installer).
  • Never run NUnit on Web app directly, so this isn't a problem.
Manually push from VSSNeed to localize it the first time, but then do nothing afterwards because a get-latest won't override it.
App.Config - used for Unit Tests.IncludeNeeded for NUnitAutomatic when getting project within VS.NetRun DOS script to copy in your localized files.

In terms of keeping configs updated, I find the following process helpful:

  1. If a change is made to the config (which should be rare), then all devs will be notified
  2. Regardless of technique (whether include or exclude), a localized copy should still be stored on each dev machine
  3. Each developer is ultimately responsible for updating their local copy.

Wednesday, February 23, 2005

Tips with OOP - New, Virtual, and Abstract

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

As I prepare to teach a C# class, I've being looking at ways to explain and clarify certain Object Oriented Programming (OOP) concepts. I find that while a lot of people are familiar with the terms, the specifics always seem confusing. Two questions I often hear are:

  1. What is the difference between using virtual/override and new?
  2. What is the difference between virtual and abstract?

While there are certainly smarter people then myself who have thoroughly explained every inch of OOP, I'll give it my two cents anyway. (For those interested in other resources, I personally like Deitel and Microsoft's C# Spec).

What is the difference between using virtual and new?

The Virtual keyword (used in the base class) lets a method be overridden (by using override in the derived class) such that if you declare an object of a base type, and instantiate it of a derived type, then calling the virtual method will call the derived type's method, not the base type. This is polymorphism.

The new keyword (used in the derived class) lets you hide the implementation of a base method such that when you declare and instantiate an object of the same type, the method of that type is called. There is no polymorphism here.

I think the best way to explain this is via a code sample that shows the different effects of using virtual vs. new. Say you have the following two classes:

    public class MyBase
    {
        public virtual string MyVirtualMethod()
        {
            return "Base";
        }

        public string MyNonVirtualMethod()
        {
            return "Base";
        }
    } //end of sub class

    public class MyDerived : MyBase
    {
        public override string MyVirtualMethod()
        {
            return "Derived";
        }

        public new string MyNonVirtualMethod()
        {
            return "Derived";
        }
    } //end of sub class

This has two classes, MyBase and MyDerived. For illustration, the classes each only have two methods. For comparison, MyBase has one method virtual, and one not. Likewise, MyDerived overrides the virtual method and hides the non-virtual. The following code snippet shows three separate cases. Note that the virtual-new keywords affect the value of MyNonVirtualMethod based on whether the object is declared as type MyBase or MyDerived.

MyBase m1 = new MyDerived();
Console.WriteLine("MyVirtualMethod='" + m1.MyVirtualMethod() + "', MyNonVirtualMethod='" + m1.MyNonVirtualMethod() + "'");
//    Returns: MyVirtualMethod='Derived', MyNonVirtualMethod='Base'

MyDerived md = new MyDerived();
Console.WriteLine("MyVirtualMethod='" + md.MyVirtualMethod() + "', MyNonVirtualMethod='" + md.MyNonVirtualMethod() + "'");
//    Returns: MyVirtualMethod='Derived', MyNonVirtualMethod='Derived'

Note that in the first case, where we declare the object of type MyBase yet instantiate it to type MyDerived, calling MyNonVirtual method is called on the declared type. In the second case, it is called on the Instantiated type.

These concepts are very common when declaring an array of base objects, and then cycling through the array and calling virtual members. A classic example is making an array of Shape objects, calling the GetArea() method on each one, and it correctly returning the area for that shape.

What is the difference between virtual and abstract?

These two keywords are sometimes confused because both can be over-ridden. However, unlike Virtual members, Abstract members have no implementation. As Deitel puts it, "Abstract base classes are too generic to define real world objects" (C# How to Program, pg 392). The Abstract keyword can be applied to either members or the class as a whole. Note that if a class has an abstract member, then that class must itself be abstract (There is no such thing as a "virtual" class).

In the following code snippet below (from Deitel, pg. 394), the abstract class shape has two virtual methods: Area and Double, and one abstract property Name. Note that both virtual members have a concrete implementation. If you create a derived class "Point", and call its Area method, it will return 0.

public abstract class Shape
{
    public virtual double Area()
    {
        return 0;
    }

    public virtual double Volume()
    {
        return 0;
    }

    public abstract string Name
    {
        get;
    }
}

The following table highlights these differences:

 VirtualAbstract
Has implementationYesNo
Scopemembers onlymembers and classes
Can create an instance ofNot Applicable (you can only create instances of classes, not members)No - but you can create an instance of a class that derives from an abstract class. And you can declare a type Abstract class and instantiate that as a derived class.

Common Abstract classes in the .Net framework include System.Xml.XmlNode and System.IO.Stream. Perhaps the most common virtual method is System.Object.ToString().