Thursday, 14 April 2011

Inner controls not populating when using UserControls within UserControls

I spent some time writing a new component converting our existing use of a free BDPLite component, which I wanted to replace with a jQuery UI DatePicker control.
I created a basic control, and injected some JavaScript into the page for the jQuery control
<%@ Control .... %>
<div class="datePickerDiv">        
  <input type="text" ID="txtDate" class="datePicker" runat="server"/>
</div>
Code to insert the code once per page:
protected void Page_Load(object sender, EventArgs e)
{  
  if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "jQuery"))  
  {     
     Page.ClientScript.RegisterClientScriptBlock(          
        this.GetType(),
        "jQuery",           
        @"$(document).ready( function() { $("".datePicker"").datepicker(); });",
        true);  
  }
}

public DateTime? SelectedDate
{  
  get   
  {      
     DateTime dateTime;            
    
     if(!DateTime.TryParseExact(this.txtDate.Value, "dd/MM/yyyy", out dateTime))
        return null;
     else        
        return dateTime;  
  }  
  set  
  {      
     this.txtDate.Value = value.ToString("dd/MM/yyyy");  
  }
}
Now, assuming:
  • We’ve added our jQuery references at the top of the page too and it is all working fine
  • We’re creating a new control in the same directory as the old one e.g. ~/usercontrols
  • We are referencing the DatePicker control in another control
Here is another control, referencing the first one
<%@ Control ....%>
<%@ Register TagPrefix="ct1" TagName="DatePicker" Src="~/usercontols/DatePicker.ascx" %>
<div class="outerControl">  
  <ct1:DatePicker runat="server" ID="dtDate" /><br/>  
  <ct1:DatePicker runat="server" ID="dtDate2" /><br/>
</div>

And you try and set some default values up in code:

// Set default values for controls.
protected void Page_Load(object sender, EventArgs e)
{    
  dtDate.SelectedDate = DateTime.Now;
  dtDate2.SelectedDate = DateTime.Now.AddDays(1);
}

You’ll find you’ll get a NullReferenceException at this line:

if(!DateTime.TryParseExact(this.txtDate.Value, "dd/MM/yyyy", out dateTime))

Or more specifically – at txtDate
The short answer is that .NET (4.0 anyway) doesn’t like you referencing controls within controls when they are of the same folder level. I’m not 100% sure at this exact second if it matters if they are in the same namespace.
But to fix this, I moved it to a sub-directory, changed the namespace name and changed the reference and voila! It all worked fine.