天天看點

In Search Of ASP.Net Controls

In Search Of ASP.Net Controls

Posted by scott on 2004年4月24日

Tips for locating controls with the FindControl method, including finding controls in DataGrid rows, Repeaters, DataGrid headers, and user controls.

The FindControl method of the System.Web.UI.Control class appears simple enough to use. In fact, the MSDN description of the method is one simple sentence:

Searches the current naming container for the specified server control.

The key to using FindControl is to invoke the method on the correct container. As a warm up, let’s say we have the following form inside of an aspx page.

<form id="Form1" method="post" runat="server">
  <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
  <asp:Button id="Button1" runat="server" Text="Button"></asp:Button>
</form>
      
Although we can declare a member variable in our code behind class to reference TextBox1, let’s use the FindControl method in an event handler.
private void Button1_Click(object sender, System.EventArgs e)
{
   TextBox b = Page.FindControl("TextBox1") as TextBox;
   if(b != null)
   {
      Response.Write("Found TextBox1 on Button1_Click<br>");
   }         
}
      
The above code will find TextBox1 and write a message into the response stream. But what happens when the TextBox is not a child of the control we query with FindControl? Consider the following form.
<form id="Form1" method="post" runat="server">
  <asp:Panel id="Panel1" runat="server" Height="152px">
    Panel
    <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
    <asp:Button id="Button1" runat="server" Text="Button"></asp:Button>
  </asp:Panel>
</form>
      
In the above form, the parent of TextBox1 is the Panel control Panel1. Let’s use the same event handler we used in the first example and see what happens.
In Search Of ASP.Net Controls
Once again we have easily located the control. For later comparison, let’s view an excerpt of the HTML source the ASP.NET form has given us.
<div id="Panel1" style="height:152px;">
  Panel
  <input name="TextBox1" type="text" id="TextBox1" />
  <input type="submit" name="Button1" value="Button" id="Button1" />
</div>
      
In the source above we can see ASP.NET has given the TextBox a client side ID of TextBox1, the same ID we use on the server. This behavior changes when we place the textbox inside of a control implementing the INamingContainer interface, which will change the way we use the FindControl method. Any control implementing INamingContainer will create a new control namespace so that all child controls will have a unique ID on the page. In short, an INamingContainer control will guarantee there are no naming conflicts on a page. This behavior is best demonstrated with an example.

FindControl in a DataGrid

There are several ASP.NET controls which implement INamingContainer, including the DataGrid, the Repeater, and the DataList. Let’s build a form with a DataGrid and view the HTML output.
<form id="Form1" method="post" runat="server">
   <asp:DataGrid id=DataGrid1 runat="server" DataSource="<%# employees1 %>" 
        AutoGenerateColumns="False"
        OnSelectedIndexChanged="DataGrid1_SelectedIndexChanged"
        OnEditCommand="DataGrid1_EditCommand">
     <Columns>
       <asp:BoundColumn DataField="emp_id" SortExpression="emp_id" HeaderText="emp_id"/>
       <asp:BoundColumn DataField="fname" SortExpression="fname" HeaderText="fname"/>
       <asp:BoundColumn DataField="lname" SortExpression="lname" HeaderText="lname"/>
       <asp:TemplateColumn>
         <ItemTemplate> 
           <asp:TextBox Runat="server" ID="TextBox1" />
         </ItemTemplate>
       </asp:TemplateColumn>
       <asp:ButtonColumn Text="Select" CommandName="Select"></asp:ButtonColumn>
       <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Update" 
               CancelText="Cancel" EditText="Edit">
       </asp:EditCommandColumn>
     </Columns>
   </asp:DataGrid>
</form>
      
The DataGrid in the form will display data from a well known SQL Server table. Using a TemplateColumn, we will add a TextBox control with the ID of TextBox1 to each row of the grid. Let’s take a look at an excerpt of the HTML ASP.NET generates.
<table cellspacing="0" rules="all"  id="DataGrid1">
    <tr>
    <td>emp_id</td><td>fname</td><td>lname</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>
  </tr>
  <tr>
    <td>A-C71970F</td><td>Aria</td><td>Cruz</td><td>
      <input name="DataGrid1:_ctl2:TextBox1" type="text" id="DataGrid1__ctl2_TextBox1" />
    </td><td>
  </tr>
  <tr>
    <td>A-R89858F</td><td>Annette</td><td>Roulet</td><td>
      <input name="DataGrid1:_ctl3:TextBox1" type="text" id="DataGrid1__ctl3_TextBox1" />
    </td><td>
  </tr>
      

Here we can see there are many instances of TextBox1, but each ID is prefixed with some additional identifier information. This behavior shows the INamingContainer objects at work. The DataGrid implements INamingContainer and will preface each child control ID with ‘DataGrid1’. As we will see shortly, a DataGrid uses a collection of DataGridItem controls to represent each row of data. A DataGridItem control also implements INamingContainer, and will preface the name of child controls with a it’s own generated identifier (‘_ctrl2’, ‘_ctrl3’, etc.).

Now, if we used the following code, FindControl will return a null value

Control c = Page.FindControl(“TextBox1”)
      
The Page control cannot find a TextBox1 because the TextBox controls hide themselves inside of INamingContainer controls. Besides, which control would we really expect FindControl to return? The first TextBox control of the page? The last TextBox control? Typically, when you want to find a TextBox inside of a DataGrid, you’ll be looking for a TextBox on a specific row the user has chosen. For example, we added a Select column to allow the user to click on a hyperlink to chose the selected row. Let’s try to grab the TextBox control in the SelectedIndexChanged event.
private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e)
{
   TextBox b;
   b = DataGrid1.Items[DataGrid1.SelectedIndex].FindControl("TextBox1") as TextBox;
   if(b != null)
   {
      Response.Write("Sender = " + sender.GetType().ToString() + "<br>");
      Response.Write("Found Textbox1 in SelectedIndexChanged event<br>");
      Response.Write(FindUtil.DumpParent(b));
   }
}
      
The code invokes FindControl on the selected DataGridItem object. The code above will work, as demonstrated in the following figure.
In Search Of ASP.Net Controls
We have also added some additional output to clearly see the chain of ownership leading to our TextBox. The TextBox1 control has a TableCell object as a parent, which in turn is a child control of a DataGridItem object, and so on up the line until we reach a Page control. The code to produce this dump is next.
public class FindUtil
{
   public static string DumpParents(Control c)
   {
      StringBuilder sb = new StringBuilder();
      sb.Append(c.ID + " (" + c.GetType().ToString() + ")");

      while(c.Parent != null)
      {
         c = c.Parent;
         sb.Append(" -><br>");
         sb.Append(c.ID + " (" + c.GetType().ToString() + ")");
      }

      return sb.ToString();
   }
}
      

The code will walk up the Parent control references building a string of control IDs (when a control ID exists – not all controls in a form will have a server side ID), and the control type.

We also added an EditCommandColumn to our DataGrid for the user to select a row to edit. Working with the EditCommand event handler is slightly easier, because the second parameter to the handler is of type DataGridCommandEventArgs, which contains a reference to the DataGridItem the user has selected for interaction.

protected void DataGrid1_EditCommand(object source, 
                                     DataGridCommandEventArgs e)
{
   TextBox b;
   b = e.Item.FindControl("TextBox1") as TextBox;
   if(b != null)
   {
      Response.Write("Found Textbox1 in EditCommand event<br>");
   }        
}
      

Finding Controls In Headers and Footers

For some DataGrid objects you may want to display an interactive control inside of the header or footer section. These cases require a slightly different method of attack with FindControl. Consider the following form
<asp:DataGrid id=DataGrid1 runat="server" DataSource="<%# employees1 %>"
       AutoGenerateColumns="False" ShowFooter="True">
  <Columns> 
    <asp:BoundColumn DataField="emp_id" SortExpression="emp_id" HeaderText="emp_id"/>
    <asp:BoundColumn DataField="fname" SortExpression="fname" HeaderText="fname"/>
    <asp:BoundColumn DataField="lname" SortExpression="lname" HeaderText="lname"/>
    <asp:TemplateColumn>
      <HeaderTemplate>
        <asp:DropDownList Runat="server" ID="DropDownList1">
          <asp:ListItem Value="1">One</asp:ListItem>
          <asp:ListItem Value="2">Two</asp:ListItem>
          <asp:ListItem Value="3">Three</asp:ListItem>
        </asp:DropDownList>
      </HeaderTemplate>
      <ItemTemplate>
        <asp:TextBox Runat="server" ID="TextBox1" />
      </ItemTemplate>
      <FooterTemplate>
        <asp:DropDownList Runat="server" ID="Dropdownlist1">
          <asp:ListItem Value="1">One</asp:ListItem>
          <asp:ListItem Value="2">Two</asp:ListItem>
          <asp:ListItem Value="3">Three</asp:ListItem>
        </asp:DropDownList>
      </FooterTemplate>
    </asp:TemplateColumn>
    <asp:ButtonColumn Text="Select" CommandName="Select"></asp:ButtonColumn>
  </Columns>
</asp:DataGrid>
      

Here we have added a DropDownList control into the HeaderTemplate and FooterTemplate of the DataGrid. We should know at this point we cannot call FindControl on the Page, or on the DataGrid to retrieve these controls, because they will exist inside of another INamingContainer control, specifically a DataGridItem. What might be suprising however, is how we cannot use the DataGrid object’s Items collection as we did earlier. MSDN documentation has the following to say about the Items collection.

Note: Only items bound to the data source are contained in the Items collection. The header, footer, and separator are not included in the collection.

So we will not find the header and footer DataGridItem controls inside the Items collection, but all child controls must exist inside the Controls collection. In this case, our earlier dump of the DataGrid control hierarchy will prove useful. We know the parent of each DataGridItem is a DataGridTable, and the parent of the DataGridTable is the DataGrid control itself. We will have to assume the DataGridTable is the first control in the DataGrid, and the header row will be the first control of the DataGridTable (consequently the footer is the last control of the DataGridTable). Thus the following code:

private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e)
{
   DropDownList d;                 
   d = DataGrid1.Controls[0].Controls[0].FindControl("DropDownList1") as DropDownList;
   if(d != null)
   {
      Response.Write("Found header DropDownList1 in SelectedIndexChanged event<br>");
   }

   int footerIndex = DataGrid1.Controls[0].Controls.Count-1;
   d = DataGrid1.Controls[0].Controls[footerIndex].FindControl("DropDownList1") as DropDownList;
   if(d != null)
   {
      Response.Write("Found footer DropDownList1 in SelectedIndexChanged event<br>");
      Response.Write(FindUtil.DumpParents(d));
   }
}
      
will give us both DropDownList controls in the header and footer, and produce the following output.
In Search Of ASP.Net Controls
Seeing hard coded index values into the Controls collection is a bit worrisome. If your control changes, or the DataGrid control changes in a newer version of the framework, this code could break. An alternate solution could involve looping through the DataGridTable controls looking for DataGridItem objects with a ItemType property equal to ListItem.Header or ListItem.Footer. Whichever you decide, neither feels extremely robust in the face of future changes.

FindControl in Repeater controls

Like the DataGrid, the Repeater is an INamingContainer. Let’s take a look at the following form which displays authors from SQL Server.
<asp:Repeater id="Repeater1" runat="server">
  <ItemTemplate>
     <tr>
        <td><%#DataBinder.Eval(Container.DataItem, "au_id")%></td>
        <td><%#DataBinder.Eval(Container.DataItem, "au_lname")%></td>
        <td><%#DataBinder.Eval(Container.DataItem, "au_fname")%></td>
        <td><asp:TextBox Runat="server" ID="TextBox1" /></td>
        <td><asp:Button Runat="server" ID="Button1" OnClick="Button1_Click" Text="Click" /></td>
     </tr>
  </ItemTemplate>
</asp:Repeater>
      
Each row of the repeater will contain a TextBox and Button control. Every Button control will use the same event handler where we can grab the corresponding TextBox control in the row with the following code.
protected void Button1_Click(object sender, System.EventArgs e)
{
   Button btn = sender as Button;
   TextBox tb = btn.Parent.FindControl("TextBox1") as TextBox;
   if(tb != null)
   {
      Response.Write("Found TextBox1 in Button1_Click event<br>");
      Response.Write(FindUtil.DumpParents(tb));
   }
}
      
Here you can see we have taken a different tack by asking for the parent of the Button control which has fired the event handler. In the following screen shot, you can see the parent of the TextBox (and the Button) is a RepeaterItem control (which also implements INamingContainer). Thus the first TextBox appearing on the screen will have an unique ID of Repeater1:_ctl0:TextBox1.
In Search Of ASP.Net Controls
One more tip before we move to the last section. Suppose we added a second button at the bottom of the form with the repeater. When the user clicks this button we want to loop the repeater collecting the contents of each TextBox control from every row. You can accomplish this task with the following code.
private void Button2_Click(object sender, System.EventArgs e)
{
   foreach(RepeaterItem item in Repeater1.Items)
   {
      TextBox b = item.FindControl("TextBox1") as TextBox;
      Response.Write(b.Text + "<br>");                       
   }
}
      

User Controls

For the sake of completeness, let’s take a look at how FindControl works when a User Control (ascx) appears. Let’s through the following user control into a form.
<%@ Control Language="c#" 
  AutoEventWireup="false" 
  Codebehind="MyUserControl.ascx.cs" 
  Inherits="aspnet.FindControls.MyUserControl" 
  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>
      
If we view the HTML output, we will see the following.
<input name="MyUserControl1:TextBox1" type="text" id="MyUserControl1_TextBox1" />
   <input type="submit" name="MyUserControl1:Button1" value="Button" id="MyUserControl1_Button1" />
      

Although user controls do not implement INamingContainer, they do ensure their child controls will not have a naming conflict. For instance, if we placed two of these user controls on the same form, without munging the names we would have two TextBox1 controls on the same form.

Although you generally will not need to do this, you can find a control from the code behind of a user control as easily as the first example in this article

private void Button1_Click(object sender, System.EventArgs e)
{
   TextBox b = this.FindControl("TextBox1") as TextBox;
   if(b != null)
   {
      Response.Write("Found TextBox1 on User Control Button1_Click<br>");
   }               
}
      
If you wanted to find the TextBox control from inside of the parent page (not recommended, but here for completeness), you would have to invoke FindControl on the user control. In the following example, we will also use FindControl to find our user control.
private void Page_Load(object sender, System.EventArgs e)
{
   MyUserControl u = FindControl("MyUserControl1") as MyUserControl;
   TextBox b = u.FindControl("TextBox1") as TextBox;
   if(b != null)
   {
      Response.Write("Found TextBox1 on Page_Load<br>");
   }         			
}
      

Conclusion

Hopefully this article has shed some light and will offer some assistance to those readers who have lost wayward controls. The behavior of FindControl makes perfect sense once you understand the purpose of INamingContainer and begin using FindControl on the correct objects.

-- by K. Scott Allen

Comments:

Flat

Nested

Threaded

Embedded

Oldest First

Newest First

A well written article. Thanks.

By SomeNewKid on 2004-6-7

This article answers a very common question on the ASP.NET Forums. It means I can be lazy and point posters to this article.

Thanks!

Printing

By db7728 on 2004-6-8

Anyone know how to print this? Thanks!

Printing

By scott on 2004-6-11

I'm sorry but we do not have a print friendly format at this time. If you send your email address to [email protected] I will email the word document to you.

Issues with Repeaters or DataList with Seperator Items

By santanaf on 2004-6-14

Keep in mind that with respect to DataList and Repeaters (and perhaps other items) if you include an Item Separator template your controls will not be found as expected. This may be a bug with the .NET framework 1.0 and may be fixed in 1.1, however this is what I found.

Create a DataList with your ItemTemplate, also create a SeparatorTemplate item as well. Place a control in your ItemTemplate and then bind a DataTable to your DataList. Once bound loop over your DataList items using a for loop with an upper bound of DataList.Items.Count. Within the for loop use the null check to determine if DataList.Items[i].FindControl("ControlName") actually finds all of the controls that you may expect it to. It seems to me that the Seperator items are included in the Items Collection of the DataList, however they are not included in the DataList.Items.Count property. There is also no good way that I saw in my brief look to determine if the item you are looking at is a ItemTemplate or a Seperator template. Maybe with the NamingContainer? Perhaps someone can shed some light on this strange behavior for me (or perhaps I am just completely off the rocker with it).

Alex

Re: SeparatorTemplate

By scott on 2004-6-14

Hi Alex:

I added a line to the Repeater example:

<SeparatorTemplate><tr><td>blah</td></tr></SeparatorTemplate>

I can still loop through Repeater1.Items without hitting any of the Separator controls in ASP.NET 1.1. Perhaps you could post or email some code that reproduces the problem you found and I could take a look?

please reply me soon

By mahsa on 2004-7-9

Hi I try to have just one check box in the footer of datalist and access to that to see if it checked or not I use this sample for datagrid its useful in datalist too

but my problem is I dont want to repeat the check box in each row so I dont want to use the footertemplate in ItemTemplate so now how can I find the check box and hire CheckedChanged event?

<asp:DataList id="DataList1" runat="server">

<ItemTemplate>

1

<asp:CheckBox Visible="False" ID="cbCalculate" Runat="server" AutoPostBack="True"></asp:CheckBox>

<asp:LinkButton Visible="False" ID="lb" Runat="server">uu</asp:LinkButton>

</ItemTemplate>

<FooterTemplate>

<asp:CheckBox ID="Ch_LiftGround" Runat="server" Text="Lift Ground Service" class='caption' AutoPostBack=true></asp:CheckBox>

</FooterTemplate>

</asp:DataList>

Regards...Mahsa

How can I find a datagrid embebed into a two datalists?

By carolina_cgu on 2004-10-6

(My english is not very good)

Hi I have a datalist that has another datalist. The second datalist i can find it and i can call it. And I have another datagrid into this datalist. But this datagrid i cant find it.

I use :

CType(sender, DataList).SelectedIndex = E.Item.ItemIndex

Dim dltItem As DataList = Me.dltSucursales.Items(E.Item.ItemIndex).FindControl("dtlAreas")

Anybody can help me?

Problem with FindControl in a Repeater

By senyl on 2004-10-8

I found that when you have a repeater with either a HeaderTemplate and/or Footer Template, findcontrol doesn't work too well. I have a repeater bound to a dataview. On OnItemBound, I loop through all the items just as listed in the article. However, for the life of me, findcontrol comes up empty if you utilize a header or footer template. Your choice then is either to do the findcontrol yourself with multiple loops, or don't use a header or footer template, and pull those out of the repeater. If you do the latter, you have to make sure all of the tables are formatted properly. Really a big pain in the rear! If you can do this properly, please tell me how.

looping through controls in a repeater

By wilkins on 2004-12-2

I have 2 label controls in the headertemplate section of my repeater. Now at run-time I'm trying to set the text property depending on the users language. How do you do that? I've pasted in my repeater (headertemplate only) code below. I'm able to do this with a label outside the repeater just fine.

thanks.

asp:repeater id="rpPlantTransit" runat="server"> <HeaderTemplate> <table class="DataGrid"> <tr>

<td class="DataGridHeader" width="200px">

<asp:Label ID="lblPlantHeader" Runat="server"></asp:Label>

</td>

<td class="DataGridHeader" width="200px">

<asp:Label ID="lblTransit" Runat="server"></asp:Label>

</td>

</tr> </HeaderTemplate>

... </asp:repeater>

Finding Controls In Headers and Footers

By soli on 2004-12-24

How to find controls in Language="VB"

How to call Nested Controls

By jmhmaine on 2005-1-20

I have a CheckBoxList control nested in a DataList <selectedItemTemplate>. The data source is from SQL Server tables, so the boxes are built dynamically.

As you can see I have tried using me.FindControl("chblEmailSignup") which works fine when it by itself, but will not work when it is nested in the DataList. Also, I am not able to keep the state of the boxes, so if the user clicks a checkbox and submits the form, the check goes back to unchecked. This might have something to do with it.

Here is the ASPX page:

<%@ Page Language="vb" AutoEventWireup="false"

Codebehind="simpleData.aspx.vb" Inherits="simpleData" Trace="True" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>

<head>

<title>simpleData</title>

</head>

<body>

<form id="Form1" method="post" runat="server">

<asp:DataList ID="dlEmail_Parent"

Runat="server"

RepeatLayout="Flow"

DataKeyField="FamilyID"

OnItemCommand="DoItemSelect"

OnCancelCommand="DoItemCancel"

EnableViewState="True">

<ItemTemplate>

<asp:ImageButton CommandName="Select"

ImageUrl='/images/page_elements/uparrow.gif' Height="10" Width="11"

runat="server" ID="Imagebutton1" EnableViewState=False />

<%# DataBinder.Eval(Container.DataItem, "FamilyName") %><br>

</ItemTemplate>

<SelectedItemTemplate>

<asp:ImageButton CommandName="Cancel"

ImageUrl='/images/page_elements/downarrow.gif' Height="10" Width="11"

runat="server" ID="Imagebutton2" EnableViewState=False />

<%# DataBinder.Eval(Container.DataItem, "FamilyName") %><br>

<asp:CheckBoxList id="chblEmailSignup"

DataSource='<%# GetChildRelation(Container.DataItem,

"PROJECT_FAMILY_PROJECT") %>'

DataMember='PROJECT'

DataTextField='ProjectName'

DataValueField='ProjectID'

EnableViewState="True"

OnSelectedIndexChanged="Check_Clicked"

Runat="server">

</asp:CheckBoxList>

<asp:Button ID="submitSelections" Text="Submit Selections" Runat="server"

EnableViewState=False />

</SelectedItemTemplate>

</asp:DataList>

<br />

<asp:Literal ID="ltrEmailSignup" Runat="server"

EnableViewState=False />

</form>

</body>

</html>

Here is the VB Code Behind:

Imports System.Data.SqlClient

Public Class simpleData

Inherits System.Web.UI.Page

Protected WithEvents dlEmail_Parent As DataList

Protected WithEvents ltrEmailSignup As Literal

#Region " Web Form Designer Generated Code "

'This call is required by the Web Form Designer.

<System.Diagnostics.DebuggerStepThrough()> Private Sub

InitializeComponent()

End Sub

'NOTE: The following placeholder declaration is required by the Web

Form Designer.

'Do not delete or move it.

Private designerPlaceholderDeclaration As System.Object

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles MyBase.Init

'CODEGEN: This method call is required by the Web Form Designer

'Do not modify it using the code editor.

InitializeComponent()

End Sub

#End Region

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles MyBase.Load

If Not (IsPostBack) Then

bindData()

End If

End Sub

Sub Check_Clicked(ByVal sender As Object, ByVal e As System.EventArgs)

'ltrEmailSignup.Text = "Selected Item(s):<br><br>" &

chblEmailSignup.ClientID

' Iterate through the Items collection of the CheckBoxList

' control and display the selected items.

Dim selectedCBL As CheckBoxList =

CType(Me.FindControl("chblEmailSignup"), CheckBoxList)

Dim i As Integer

If (Not selectedCBL Is Nothing) Then

For i = 0 To selectedCBL.Items.Count - 1

If selectedCBL.Items(i).Selected Then

ltrEmailSignup.Text &= selectedCBL.Items(i).Text &

"<br>"

End If

Next

Else

ltrEmailSignup.Text = "control is equal to nothing" ' I AM

STUCK HERE!!!!

End If

End Sub

Sub bindData()

Dim sqlParent As String = "SELECT FamilyID,FamilyName FROM

PROJECT_FAMILY;"

Dim sqlChild As String = "SELECT ProjectID, ProjectName,

FamilyID FROM PROJECT;"

Dim ds As New DataSet

Dim conn As New SqlConnection

conn.ConnectionString = Global.ConnectionString

'Two commands for two tables

Dim cmdParent As SqlCommand

Dim cmdChild As SqlCommand

'Two datapaters to fill the dataset from two tables

Dim adpParent As SqlDataAdapter

Dim adpChild As SqlDataAdapter

'This handles the relationship between the two columns

Dim datarelation As datarelation

Dim dcParent As DataColumn

Dim dcChild As DataColumn

'First command for first table

cmdParent = New SqlCommand

cmdParent.Connection = conn

cmdParent.CommandText = sqlParent

'Second command for Second table

cmdChild = New SqlCommand

cmdChild.Connection = conn

cmdChild.CommandText = sqlChild

'Now , we will fill the first table and add it to the dataset

adpParent = New SqlDataAdapter

adpParent.SelectCommand = cmdParent

adpParent.TableMappings.Add("Table", "PROJECT_FAMILY")

adpParent.Fill(ds)

'As we did in the previous step , here for the Second table

adpChild = New SqlDataAdapter

adpChild.SelectCommand = cmdChild

adpChild.TableMappings.Add("Table", "PROJECT")

adpChild.Fill(ds)

dcParent = ds.Tables("PROJECT_FAMILY").Columns("FamilyID")

dcChild = ds.Tables("PROJECT").Columns("FamilyID")

'Here we combined two datacolumns to the relations obj

datarelation = New datarelation("PROJECT_FAMILY_PROJECT",

dcParent, dcChild)

ds.Relations.Add(datarelation)

dlEmail_Parent.DataSource = ds.Tables("PROJECT_FAMILY")

dlEmail_Parent.DataBind()

adpParent.Dispose()

adpParent = Nothing

adpChild.Dispose()

adpChild = Nothing

ds.Dispose()

ds = Nothing

conn.Close()

conn = Nothing

End Sub

Function GetChildRelation(ByVal dataItem As Object, ByVal relation

As String) As DataView

'TODO: Test for Null DataItem

Dim drv As DataRowView = CType(dataItem, DataRowView)

Return drv.CreateChildView(relation)

End Function

'====================================================================================

'Sub DoItemSelect(ByVal objSource As Object, ByVal objArgs As

DataListCommandEventArgs)

'

'Handles the event of the Item Select Command from web page

'====================================================================================

Sub DoItemSelect(ByVal objSource As Object, ByVal objArgs As

DataListCommandEventArgs)

dlEmail_Parent.SelectedIndex = objArgs.Item.ItemIndex 'Set the

Seleted Item

bindData()

End Sub

'====================================================================================

'Sub DoItemCancel(ByVal objSource As Object, ByVal objArgs As

DataListCommandEventArgs)

'

'Handles the event of the Item Cancel Command from web page

'====================================================================================

Sub DoItemCancel(ByVal objSource As Object, ByVal objArgs As

DataListCommandEventArgs)

dlEmail_Parent.SelectedIndex = -1 'Leave Select Mode

bindData()

End Sub

End Class

Update on Nested CheckBox issue

By jmhmaine on 2005-1-20

I have had partial success, I found a great article on FindControl:

http://www.odetocode.com/Articles/116.aspx

So I changed my original code from

Dim selectedCBL As CheckBoxList = CType(Me.FindControl("chblEmailSignup"), CheckBoxList)

To:

Dim selectedCBL As CheckBoxList = CType(dlEmail_Parent.Items(dlEmail_Parent.SelectedIndex).FindControl("chblEmailSignup"), CheckBoxList)

I'm still having a problem keeping the checkboxes checked between submits. I believe this is also affecting my ASP:Literal control because the value when another item is selected.

繼續閱讀