Targeting a Control in the EmptyDataTemplate
Prologue
Recently I've been working on our new corporate website. Some of my previous posts have talked about a web service that I created to use on the new website. When I consume the web service, I've been creating a table and putting the information into the table and then binding it to a GridView. Since our web service could quite feasible dish up an empty result set, it sounds reasonable for us to use the EmptyDataTemplate to display something when there is no data.
Problem
The EmptyDataTemplate would allow us to manually enter the data that we want displayed when no data is present, however I prefer to allow messages like this to be easily customizable (I'll be using it in more than one place), so I prefer to create an entry that I can pull from the web.config. Unfortunately, you can't just target the control directly. I've run into this problem before; the control doesn't really exist until it is created, so targeting it directly won't always work. Luckily I've found a way around this that works nicely.
Solution
To get started, let's start by setting up our page. Create a new page, and call it EmptyDataTemplateTest.aspx. Switch over to design view and drop a GridView control onto the page, and name it gvwTest. Now switch over to source view and let's layout our control.
We'll make it easy, we'll instruct the GridView to generate it's own columns based on our DataSource, so really all we need to do is come up with the EmptyDataTemplate. Inside our EmptyDataTemplate, we want to create a label that we can push our dynamic text to, so our code should look like this:
<asp:gridview ID="gvwTest" runat="server" AutoGenerateColumns="True">
<EmptyDataTemplate>
<asp:Label ID="lblEmptyText" runat="server" />
</EmptyDataTemplate>
</asp:gridview>
Ok, the front-end is all setup, let's setup the code-behind. Open the code-behind file and let's add some code to create a table, populate some data to it and then bind it to our GridView. Let's create a helper function to create a DataTable and create a couple entries in it. Start by importing the System.Data namespace in your project and create a new function as follows:
Public Function CreateDataSource(ByVal bEmpty As Boolean) As DataTable
Dim dt As New DataTable()
dt.Columns.Add(New DataColumn("FirstName", GetType(String)))
dt.Columns.Add(New DataColumn("LastName", GetType(String)))
If bEmpty = False Then
Dim dr As DataRow
dr = dt.NewRow()
dr("FirstName") = "Sally"
dr("LastName") = "Smith"
dt.Rows.Add(dr)
dr = dt.NewRow()
dr("FirstName") = "John"
dr("LastName") = "Doe"
dt.Rows.Add(dr)
End If
Return dt
End Function
Basically, we create a DataTable, add 2 columns (FirstName and Last Name) and then based on whether we requested an empty DataSet (so it's easy to simulate empty data), we either do or don't create two rows and add them to the table. Then we return the table.
Finally to finish our setup, add the following to your Page_Load event:
gvwTest.DataSource = CreateDataSource(False)
gvwTest.DataBind()
This will assign and the bind our DataSource. Run your application and see how it works. If it works properly you should have a table with two entries. Now, let's try to assign a message to the label in our EmptyDataTemplate. Notice that it doesn't appear in our object drop down list (at the top), so how do we assign to it? Well, we have to wait for it to be loaded and the grab it on creation and add our text. Go back to your HTML code and modify your Label definition in your EmptyDataTemplate as follows:
<asp:Label ID="lblEmptyText" runat="server" OnLoad="GetEmptyText" />
Basically we're letting the control know that we want it to run a subroutine when it loads, we can't just use it's event handler like we would a normal control because it doesn't exist unless we actually DON'T have any data. (NOTE: Do NOT put a () after your GetEmptyText declaration, if you do you'll get a runtime error later about not specifying parameters - just skip the parentheses). Now let's go back to our code-behind and create the subroutine to go with it. Create a subroutine as follows (Note: this routine MUST be public or it will not work):
Public Sub GetEmptyText()
lblEmptyText.Text = "Empty Recordset"
End Sub
Notice that we get a compile error because our label doesn't exist. So how do we get around this? Well, technically our Subroutine is the event handler for the load event SO, we can add parameters that match that of any label's load event, so let's modify our Subroutine's declaration line to be:
Public Sub GetEmptyText(ByVal sender As Object, _
ByVal e As System.EventArgs)
So what exactly does our new declaration get us that we didn't have before? The sender object. Now you might think we need to use a FindControl() command and find our label in all the page's mess, and you might be able to do this, but there is an easier way. The sender object is really our label control in disguise, we just need to pull it together and make our change. Modify the contents of your subroutine as follows:
Dim lblEmptyText As Label = CType(sender, Label)
lblEmptyText.text = "Empty Recordset"
We created a label object and cast our sender to it and then we can access it's properties. Now when you run your application with an empty Recordset, you get your customized empty recordset message.
Epilogue
This is an easy way to enable yourself to make your message easily manageable. I use the same message in about 4 different places so it made sense to have a repository (mine's in the web.config) to pull this message from. Now I don't have to remember all the places where it is and do all the changes and hope I don't break anything, I simply change the web.config and re-upload it.