While trying to create a custom User Control for a project I've been working on at work I discovered a few things that need to be done to add a Typed DataSource to a User Control and still have your data controls work

 1: Unless you know the unique ClientID that will be given to your datasource at runtime you can remap the DataSourceID at runtime.

In the Page_Init of your user control set the datasource of your data control to the ClientID of your datasource control

Example:

protected void Page_Init(object sender, EventArgs e) {

// Set datasource name for repeater to new ClientID name of ds
myRepeater1.DataSourceID = myDS.ClientID;

}

This essentially gets the ClientID of the datasource before the page actually starts to run and does the databinding/etc.  Otherwise in a user control your ClientID gets a unique key prependend to the front of it consisting of it and your usercontrol ID.

2: Now if you run your page with the typed repeater control you will also need to change some code in the private ConnectToDataSourceView() method.

The control as-is will only work from the calling page itself and not in a lower hierarchy control like a user control.  What we need to do is search for the control recursively.  There is already a FormUtil.FindControls() method that is close, but it searches for Controls by ID instead of ClientID

We'll add a new method to the partial class FormUtil named FindControlByClientID() as follows:

      public static Control FindControlByClientID(Control root, string clientID){            // if root Control is clientID return root Control
            if (root.ClientID == clientID)
return root;             // search all child Controls of root Control
            foreach (Control ctl in root.Controls)
{                // Search children Controls recursively
                Control rCtl = FindControlByClientID(ctl, clientID);
                if (rCtl != null) return rCtl;
            }

            return null;
        }

Now you can change the code for the Typed Repeater Control:

private System.Collections.IEnumerable ConnectToDataSourceView() {

                  if (m_currentView == null)
                  {
                        MyNetTiers.Web.Data.MyDataSource datasource = null;
                        // CHANGE HERE
                        //Control ctl = this.Page.FindControl(DataSourceID);
                       
Control ctl = FormUtil.FindControlByClientID(this.Page, DataSourceID);

You of course should change this in the templates and it will always work recursively.

 

3: The final tip is dealing with ControlID parameters in your Typed Datasource.

So far I've only dealt with two controls <asp:ControlParameter /> and Typed SQL Filters. Lets start with an example datasource.

<data:MyTableDataSource ID="mtDS" runat="server"
                   EnablePaging="True"
                  
EnableTransaction="false"
                  
EnableSorting="True"
                   
EnableDeepLoad="true"
                  
SelectMethod="GetByID"
                  
EnableViewState="false"
                  
>

<Parameters>
  
<asp:ControlParameter Name="ID" ControlID="__Page" PropertyName="MyID" Type="Int32"/>
   <data:SqlParameter>
     
<Filters>
        
<data:MyTableFilter Column="Active" ControlID="__Page" PropertyName="Active"
                             
ComparisionType="Equals" />

      </Filters>

   </data:SqlParameter>

</Parameters>

<DeepLoadProperties Method="IncludeChildren" Recursive="False">
  
<Types>
     
<data:NewHomesProperty Name="MyDeepCollection" />
      
<data:NewHomesProperty Name="MyOtherDeepCollection" />
  
</Types>
</DeepLoadProperties>

</data:MyTableDataSource>

As you can see I have a MyTableDataSource that is calling the GetByID method of the Provider and is calling the values from the __Page control (the calling page).  The problem is that this is a User Control and we want to store our properties in the code behind for the user control.  This is going to be done in two steps.  We'll start with the <asp:ControlParameter /> :

in the code behind for the Page_Init on your user control do as follows:

protected void Page_Init(object sender, EventArgs e) {

// Set parameter for ID to find name of UC
ControlParameter p = (ControlParameter)mtDS.Parameters["ID"];
p.ControlID =
this.ClientID;

This gets the control parameter of Name="ID" from the datasource parameters and overrides the value of the ControlID with the scoped version of the custom User Control's ClientID.  Now you can add a parameter in the code behind as follows:

public int CommID {

get {
 
string reqId = Request["id"];
 
int id = 0;

  try {

    if (reqId != null) id = int.Parse(reqId);

  }catch{}  return id;

  }

}

That will get you the id from the request SomeRequestPageWithMyCustomUserControl.aspx?id=1

Now we need to deal with the filter.  For this I turn to Generics to help me out. The biggest issue with the SqlParameters is they are nested such you cant find them by a ControlID. So first lets add another neat method to our FormUtil partial class.

public static void ChangeControlID<FilterType, FilterColumn>(ParameterCollection collection, FilterColumn column, string controlId) where FilterType : SqlFilter<FilterColumn> {

  foreach (Parameter p in collection) {

    if (p is SqlParameter) { 
      
SqlParameter s = p as SqlParameter;
     
foreach (object filt in s.Filters) {
       
if (filt is FilterType) {
         
SqlFilter<FilterColumn> f = filt as SqlFilter<FilterColumn>;
         
if (f.Column.Equals(column)) f.ControlID = controlId;
        }
      }
    }
  }
}

This neat little creation of mine pulls apart the typed filters from the parameter list of the datasource and then changes the ControlID once it finds the correct column.  In the Page_Init of your User Control do as follows:

protected void Page_Init(object sender, EventArgs e) {

// Set parameter for ID to find name of UC
ControlParameter p = (ControlParameter)mtDS.Parameters["ID"];
p.ControlID =
this.ClientID;

// Change MyTableColumn to point to this User Control
FormUtil.ChangeControlID<MyTableFilter, MyTableColumn>(mtDS.Parameters, MyTableColumn.Active , ClientID);

Now you can add another Getter for this code behind:

public bool Active {
  get {
    return true;
  }
}

Now you can put your new User Control on anything you want without errors =o)~

Enjoy!

 

Chris Berthold
Softrim Corporation
Estero, FL