Bind Your ASP Controls Together Without Keeping Them Together

Have you ever had the situation where you need to show a tabulated list of items. Sure, everyone has. If we are using ASP.Net Controls, we would use a GridView.

What if you want to select one of the items in the grid, and display an edit panel for that user? This is still relatively easy. Take a look at my post called Creating a Gridview and Formview Master/Child page for more information.

Let's say we have a GridView which lists all users. When the user selects a user in that grid, we want the user data to be displayed in a DetailsView on a separate tab for editing. Then, when the user updates that user, we want the new information to be "pushed" back into the GridView. The difficulty with this comes because we have separated the GridView and the related DetailsView into different ASP.Net Controls (ie Tabs). Take this code for example.

<ajaxToolkit:TabContainer ID="TabContainer" runat="server" Width="100%" CssClass="AjaxTab">

	<ajaxToolkit:TabPanel ID="GridTab" runat="server" HeaderText="Results" Width="100%">
		<ContentTemplate>

			<asp:UpdatePanel ID="GridUpdatePanel" runat="server" UpdateMode="Conditional">
			<ContentTemplate>
			
				<asp:GridView ID="UserDataGrid" runat="server" DataKeyNames="UserID" DataSourceID="UserGridDataSource">
					<Columns>
						<asp:BoundField DataField="UserCode" HeaderText="Code"  />				
						<asp:BoundField DataField="FirstName" HeaderText="First Name" />
						<asp:BoundField DataField="Surname" HeaderText="Surname" />
						<asp:BoundField DataField="password" HeaderText="Password" />
						<asp:CheckBoxField DataField="Enabled" HeaderText="Enabled" />
						<asp:CommandField ButtonType="Image" ShowSelectButton="true" ShowDeleteButton="false" SelectImageUrl="/Images/ico_edit.gif" />
					</Columns>
				</asp:GridView>
			
			</ContentTemplate>
			<Triggers>
				<asp:AsyncPostBackTrigger ControlID="TabContainer$DetailsTab$DetailUpdatePanel$UserDetailsView" EventName="ItemInserted" />
				<asp:AsyncPostBackTrigger ControlID="DetailsTab$UserDetails$DetailUpdatePanel$UserDetailsView" EventName="ItemUpdated" />
			</Triggers>
			
			</asp:UpdatePanel>
			
			<asp:SqlDataSource ID="UserGridDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:myConnectionString %>"
				SelectCommand="SELECT * FROM [Users]">
			</asp:SqlDataSource>
	
		</ContentTemplate>
	</ajaxToolkit:TabPanel>
	
	<ajaxToolkit:TabPanel ID="DetailsTab" runat="server" HeaderText="Details" Width="100%">
		<ContentTemplate>
		
			<asp:UpdatePanel ID="DetailUpdatePanel" runat="server">
			<ContentTemplate>
				<asp:DetailsView ID="UserDetailsView" runat="server" DataKeyNames="UserID" DataSourceID="UserDataSource" DefaultMode="Insert">
				<Fields>
					<asp:TemplateField HeaderText="Code">
						<HeaderStyle Width="300"></HeaderStyle>
						<InsertItemTemplate>
							<asp:TextBox ID="txtUserCode" runat="server" Text='<%# Bind("UserCode") %>' MaxLength="8" />
						</InsertItemTemplate>
						
						<EditItemTemplate>
							<asp:TextBox ID="txtUserCode" runat="server" Text='<%# Bind("UserCode") %>'  MaxLength="8"/>
						</EditItemTemplate>
					</asp:TemplateField>
					
					<asp:TemplateField HeaderText="First Name">
						<InsertItemTemplate>
							<asp:TextBox ID="txtFirstName"  runat="server" Text='<%# Bind("FirstName")  %>'   MaxLength="20"/>
						</InsertItemTemplate>
						<EditItemTemplate>
							<asp:TextBox ID="txtFirstName" runat="server" Text='<%# Bind("FirstName") %>' MaxLength="20" />
						</EditItemTemplate>
					</asp:TemplateField>
					
					<asp:TemplateField HeaderText="Surname">
						<InsertItemTemplate>
							<asp:TextBox ID="txtSurname" runat="server" Text='<%# Bind("Surname") %>' MaxLength="20" />
						</InsertItemTemplate>
						<EditItemTemplate>
							<asp:TextBox ID="txtSurname" runat="server" Text='<%# Bind("Surname") %>' MaxLength="20" />
						</EditItemTemplate>
					</asp:TemplateField>
					
					<asp:TemplateField HeaderText="Password<br><small>(10 char max)</small>">
						<InsertItemTemplate>
							<asp:TextBox ID="txtPassword" runat="server" Text='<%# Bind("password") %>' MaxLength="10" />
						</InsertItemTemplate>
						<EditItemTemplate>
							<asp:TextBox ID="txtPassword" runat="server" Text='<%# Bind("password") %>' MaxLength="10" />
						</EditItemTemplate>
					</asp:TemplateField>
				</Fields>
			</asp:DetailsView>

			<asp:SqlDataSource ID="UserDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:myConnectionString %>"
				InsertCommand="INSERT INTO [Users] ([UserCode], [FirstName], [Surname], [password]) VALUES (@UserCode, @FirstName, @Surname, @password, @isAdmin, @profile_id_fk, @DaysOff, @Enabled);SELECT @UserID = SCOPE_IDENTITY()"
				SelectCommand="SELECT [UserID], [UserCode], [FirstName], [Surname], [password] FROM [Users] WHERE ([UserID] = @UserID)"
				UpdateCommand="UPDATE [Users] SET [UserCode] = @UserCode, [FirstName] = @FirstName, [Surname] = @Surname, [password] = @password WHERE [UserID] = @UserID">
				<SelectParameters>
					<asp:ControlParameter ControlID="TabContainer$GridTab$GridUpdatePanel$UserDataGrid" Name="UserID" PropertyName="SelectedValue" Type="String" />
				</SelectParameters>
				<UpdateParameters>
					<asp:Parameter Name="UserID" Type="Int16" />
					<asp:Parameter Name="UserCode" Type="String" />
					<asp:Parameter Name="FirstName" Type="String" />
					<asp:Parameter Name="Surname" Type="String" />
					<asp:Parameter Name="password" Type="String" />
					<asp:Parameter Name="isAdmin" Type="Boolean" />
					<asp:Parameter Name="profile_id_fk" Type="Int16" />
					<asp:Parameter Name="DaysOff" Type="Int16" />
					<asp:Parameter Name="Enabled" Type="Boolean" />
				</UpdateParameters>
				<InsertParameters>
					<asp:Parameter Name="UserID" Type="Int32" Direction="Output" />
					<asp:Parameter Name="UserCode" Type="String" />
					<asp:Parameter Name="FirstName" Type="String" />
					<asp:Parameter Name="Surname" Type="String" />
					<asp:Parameter Name="password" Type="String" />
					<asp:Parameter Name="isAdmin" Type="Boolean" />
					<asp:Parameter Name="profile_id_fk" Type="Int16" />
					<asp:Parameter Name="DaysOff" Type="Int16" />
					<asp:Parameter Name="Enabled" Type="Boolean" />
				</InsertParameters>
			</asp:SqlDataSource>

			</ContentTemplate>
			<Triggers>
				<asp:AsyncPostBackTrigger ControlID="TabContainer$GridTab$GridUpdatePanel$UserDataGrid" EventName="SelectedIndexChanged" />
			</Triggers>
			</asp:UpdatePanel>
		
		</ContentTemplate>
	</ajaxToolkit:TabPanel>
	
</ajaxToolkit:TabContainer>

Take a look at line 115. This is a trigger which will cause a change to happen in the User grid. When the data changes in the Details View, we want to trigger the Grid View to refresh.

Notice the ControlID attribute of the Trigger. There are dollar signs separating the control hierarchy. This allows the ASP.Net engine to navigate the DOM tree and find the required element. If you did not reference the ControlID in this way, you would get an error along the lines of “Could not find control 'GridView1' in ControlParameter *******. We need to specifically reference the control.

In much the same way we do when we want to reference controls in our code behind pages. If you want to reference a GridView (gv1) inside an UpdatePanel (up1), you need to use the FindControl method of the UpdatePanel. Like this.

Control gv1 = up1.FindControl("gv1");

You see that we need to "search" for the control because it is within another element that is marked as "runat=server".

So, now you can don't need to be limited where you place your ASP.Net controls in your DOM. You can separate them and still reference them quickly and easily.

Til next time ...