How to create section 508 compliant Data Grid - ASP.NET v1.1

C

Corey B

I have a data grid that has three columns: First Name, Middle Name,
and Last Name. The grid has a list of people with a blank row at the
bottom of the grid (in the footer) that allows the user to add a new
person to the list. There is also an Edit button at the end of each
row that allows the user to edit a particular row. So far - no problem
- easy to do.

I have been asked to make the grid section 508 compliant. The
requirement is that the column headers must have labels that reference
the text boxes in the footer row. On a normal web form it would look
like this:

<label for="txtFirstName">First Name</label>
<asp:textbox id="txtFirstName" runat="server" />

This allows a text reader to figure out that the words "First Name" are
associated with the text box "txtFirstName" and helps a blind person
understand how to fill out the form.

Now, back to my problem. I need to figure out a way to get the header
of a Data Grid column to work the same way. The biggest problem is
that I have no idea what the ClientID of the textbox will be. So I
definitely can't hard code the <label> item.

Does anyone have any idea how I can do this?

Thanks,
Corey
 
V

vMike

Corey B said:
I have a data grid that has three columns: First Name, Middle Name,
and Last Name. The grid has a list of people with a blank row at the
bottom of the grid (in the footer) that allows the user to add a new
person to the list. There is also an Edit button at the end of each
row that allows the user to edit a particular row. So far - no problem
- easy to do.

I have been asked to make the grid section 508 compliant. The
requirement is that the column headers must have labels that reference
the text boxes in the footer row. On a normal web form it would look
like this:

<label for="txtFirstName">First Name</label>
<asp:textbox id="txtFirstName" runat="server" />

This allows a text reader to figure out that the words "First Name" are
associated with the text box "txtFirstName" and helps a blind person
understand how to fill out the form.

Now, back to my problem. I need to figure out a way to get the header
of a Data Grid column to work the same way. The biggest problem is
that I have no idea what the ClientID of the textbox will be. So I
definitely can't hard code the <label> item.

Does anyone have any idea how I can do this?

Thanks,
Corey
I have never looked at it that much but there is an accessibilty section in
the sdk doc. It makes reference to a gridview property call
UseAccessibleHeader. Maybe that will help or you might search the docs for
Accessibility Support in ASP.NET or ASP.NET Controls and Accessibility

Mike
 
A

addup

vMike said:
I have never looked at it that much but there is an accessibilty section in
the sdk doc. It makes reference to a gridview property call
UseAccessibleHeader. Maybe that will help or you might search the docs for
Accessibility Support in ASP.NET or ASP.NET Controls and Accessibility

Mike

Or do it the hard way (for framework 1.x).

example:
If your column is defined as
<ASP:TEMPLATECOLUMN>
<HEADERTEMPLATE><ASP:LABEL Runat="server" ID="lblHeaderLabel" >Name
Column</ASP:LABEL></HEADERTEMPLATE>
<ITEMTEMPLATE>&hellip;</ITEMTEMPLATE>
<EDITITEMTEMPLATE>&hellip;</EDITITEMTEMPLATE>
<FOOTERTEMPLATE><INPUT runat="server" id="txtFoot"
class="raTextBox"></FOOTERTEMPLATE>
</ASP:TEMPLATECOLUMN>

your datagrid ItemDataBound handler can then do something analogus to
(VB example)


Static lblHeaderLabel As System.Web.UI.WebControls.Label
Dim txtFoot As HtmlControls.HtmlInputText
Try
Select Case e.Item.ItemType
Case ListItemType.Header
lblHeaderLabel = e.Item.FindControl("lblHeaderLabel")
Case ListItemType.Footer
txtFoot = e.Item.FindControl("txtFoot")
If Not lblHeaderLabel Is Nothing AndAlso _
Not txtFoot Is Nothing Then
lblHeaderLabel.Text = "<LABEL for='" & txtFoot.ClientID
& "'>Name Column</LABEL>"
End If
End Select
Catch ex As Exception : DisplayError(ex)
End Try


Hope this helps
--- a ---
 
C

Corey B

addup said:
Or do it the hard way (for framework 1.x).

example:
If your column is defined as
<ASP:TEMPLATECOLUMN>
<HEADERTEMPLATE><ASP:LABEL Runat="server" ID="lblHeaderLabel" >Name
Column</ASP:LABEL></HEADERTEMPLATE>
<ITEMTEMPLATE>&hellip;</ITEMTEMPLATE>
<EDITITEMTEMPLATE>&hellip;</EDITITEMTEMPLATE>
<FOOTERTEMPLATE><INPUT runat="server" id="txtFoot"
class="raTextBox"></FOOTERTEMPLATE>
</ASP:TEMPLATECOLUMN>

your datagrid ItemDataBound handler can then do something analogus to
(VB example)


Static lblHeaderLabel As System.Web.UI.WebControls.Label
Dim txtFoot As HtmlControls.HtmlInputText
Try
Select Case e.Item.ItemType
Case ListItemType.Header
lblHeaderLabel = e.Item.FindControl("lblHeaderLabel")
Case ListItemType.Footer
txtFoot = e.Item.FindControl("txtFoot")
If Not lblHeaderLabel Is Nothing AndAlso _
Not txtFoot Is Nothing Then
lblHeaderLabel.Text = "<LABEL for='" & txtFoot.ClientID
& "'>Name Column</LABEL>"
End If
End Select
Catch ex As Exception : DisplayError(ex)
End Try


Hope this helps
--- a ---

Thanks - I think that I am close to a solution now. I found that
ASP.NET v1.1 SP1 has added a property to a label called
AssociatedControlID. If you have the following:

<asp:label id="myLabel" runat="server"
AssociatedControlID="myTextBox">First Name"</asp:label>
<asp:TextBox id="myTextBox" runat="server" />

Then ASP.NET will spit out the following:
<label for="myTextBox">First Name</label>

And it's smart enough that it will emit the proper client id even if
the text box is a nested control.

So here's my problem. Let's say I have this set up:

<ASP:TEMPLATECOLUMN>
<HEADERTEMPLATE>
<ASP:LABEL Runat="server" ID="lblHeaderLabel" >Name
Column</ASP:LABEL>
</HEADERTEMPLATE>
<ITEMTEMPLATE>&hellip;</ITEMTEMPLATE>
<EDITITEMTEMPLATE>&hellip;</EDITITEMTEMPLATE>
<FOOTERTEMPLATE>
<ASP:TEXTBOX ID="txtInput" RUNAT="Server" />
</FOOTERTEMPLATE>
</ASP:TEMPLATECOLUMN>

Somehow I need to add the AssociatedControlID property to the
lblHeaderLabel label control to get it to reference the txtInput
textbox. So I am not sure where to add the code. Should it go in the
header handler in ItemDataBound or should it go in the footer handler?
It seems like if I put it in the header handler I wouldn't be able to
reference a control that lives in the footer yet. But if I put it in
the footer handler I wouldn't be able to modify a control that lives in
the header. Any help would be appreciated.

Thanks,
Corey
 
A

addup

Corey said:
Thanks - I think that I am close to a solution now. I found that
ASP.NET v1.1 SP1 has added a property to a label called
AssociatedControlID. If you have the following:

<asp:label id="myLabel" runat="server"
AssociatedControlID="myTextBox">First Name"</asp:label>
<asp:TextBox id="myTextBox" runat="server" />

Then ASP.NET will spit out the following:
<label for="myTextBox">First Name</label>

And it's smart enough that it will emit the proper client id even if
the text box is a nested control.

So here's my problem. Let's say I have this set up:

<ASP:TEMPLATECOLUMN>
<HEADERTEMPLATE>
<ASP:LABEL Runat="server" ID="lblHeaderLabel" >Name
Column</ASP:LABEL>
</HEADERTEMPLATE>
<ITEMTEMPLATE>&hellip;</ITEMTEMPLATE>
<EDITITEMTEMPLATE>&hellip;</EDITITEMTEMPLATE>
<FOOTERTEMPLATE>
<ASP:TEXTBOX ID="txtInput" RUNAT="Server" />
</FOOTERTEMPLATE>
</ASP:TEMPLATECOLUMN>

Somehow I need to add the AssociatedControlID property to the
lblHeaderLabel label control to get it to reference the txtInput
textbox. So I am not sure where to add the code. Should it go in the
header handler in ItemDataBound or should it go in the footer handler?
It seems like if I put it in the header handler I wouldn't be able to
reference a control that lives in the footer yet. But if I put it in
the footer handler I wouldn't be able to modify a control that lives in
the header. Any help would be appreciated.

Thanks,
Corey

The answer is to do both.
Get a reference to the label in the "header handler", and use it in the
"footer handler"

refer my earlier post.
see the

*Static* lblHeaderLabel As System.Web.UI.WebControls.Label

to quote M$,
Static variables remain in existence and retain their latest values
after termination of the procedure in which they are declared.

if AssociatedControlID works for you, change my earlier post from

lblHeaderLabel.Text = "<LABEL for='" & txtFoot.ClientID & "'>Name
Column</LABEL>"

to

lblHeaderLabel.AssociatedControlID = txtFoot.ClientID

It's *ClientID*, not just ID

Hope this helps,
--- a ---
PS: Notice the *ClientID*
 
C

Corey B

Yeah - I tried that and it didn't work. Actually I think it is
supposed to be just ID instead of ClientID but I tried both and neither
works. Each time I get an error that says the following:

Unable to find the control with id 'grdMyGrid__ctl2_txtFoot' that is
associated with the Label 'lblHeaderLabel'.

It's like it can't find the text box control that I am trying to
associate the label with.

I declared a Private variable at the top of the page called objLabel.
Then in the "header handler" in the ItemDataBound event for the grid I
set objLabel equal to the label that is in the header template.

Then in the "footer handler" in the ItemDataBound event for the grid I
did the following:

Dim objTextBox As TextBox = e.Item.FindControl("txtFoot")
objLabel.AssociatedControlID = objTextBox.ID

(I also tried objTextBox.ClientID)

Am I doing something wrong?

Corey
 
A

addup

Corey said:
Yeah - I tried that and it didn't work. Actually I think it is
supposed to be just ID instead of ClientID but I tried both and neither
works. Each time I get an error that says the following:

Unable to find the control with id 'grdMyGrid__ctl2_txtFoot' that is
associated with the Label 'lblHeaderLabel'.

It's like it can't find the text box control that I am trying to
associate the label with.

I declared a Private variable at the top of the page called objLabel.
Then in the "header handler" in the ItemDataBound event for the grid I
set objLabel equal to the label that is in the header template.

Then in the "footer handler" in the ItemDataBound event for the grid I
did the following:

Dim objTextBox As TextBox = e.Item.FindControl("txtFoot")
objLabel.AssociatedControlID = objTextBox.ID

(I also tried objTextBox.ClientID)

Am I doing something wrong?

Corey

There's something wrong, since it doesn't work :)

You are correct, it should be ID, not ClientID, but that's not the
answe either

Here's what i would do to trace the problem/s
1. comment out the objLabel.AssociatedControlID = objTextBox.ID line
2. Check the HTML rendering of the datagridgrid. Confirm that
'grdMyGrid__ctl2_txtFoot' is the clien-side id/name of the footer text
box
3. And the kicker - * don't * use AssociatedControlID, try the
old-fashioned

objLabel.Text = "<LABEL for='" & objTextBox.ClientID & "'>Name
Column</LABEL>"

lemmie know how it works out

-- a --
 
C

Corey B

addup said:
There's something wrong, since it doesn't work :)

You are correct, it should be ID, not ClientID, but that's not the
answe either

Here's what i would do to trace the problem/s
1. comment out the objLabel.AssociatedControlID = objTextBox.ID line
2. Check the HTML rendering of the datagridgrid. Confirm that
'grdMyGrid__ctl2_txtFoot' is the clien-side id/name of the footer text
box
3. And the kicker - * don't * use AssociatedControlID, try the
old-fashioned

objLabel.Text = "<LABEL for='" & objTextBox.ClientID & "'>Name
Column</LABEL>"

lemmie know how it works out

-- a --

Well it sorta works. The problem is that when you just set the
Label.Text property like you suggested then ASP.NET emits a SPAN tag
around the LABEL tag. I'm not sure, but I think that is not strictly
508 compliant.

I am getting the feeling that when I use the AssociatedControlID
property that ASP.NET is expecting that the label and the associated
control will be in the same container. Is it possible that because the
Label is in the Header of a DataGrid and the text box is in the Footer
of a DataGrid that they can't properly reference each other?

For now I have figured out a work around. I have decided not to have
the column header be the label. I will create individual labels for
each textbox. I will just use CSS to make them hidden so that sighted
users don't see them, but text readers will read them. I have tested
this and it works fine.

Thanks for the help. I have read that in ASP.NET v2.0 that they have
improved 508 compliant output.

Corey
 
A

addup

Corey said:
Well it sorta works. The problem is that when you just set the
Label.Text property like you suggested then ASP.NET emits a SPAN tag
around the LABEL tag. I'm not sure, but I think that is not strictly
508 compliant.

I am getting the feeling that when I use the AssociatedControlID
property that ASP.NET is expecting that the label and the associated
control will be in the same container. Is it possible that because the
Label is in the Header of a DataGrid and the text box is in the Footer
of a DataGrid that they can't properly reference each other?

For now I have figured out a work around. I have decided not to have
the column header be the label. I will create individual labels for
each textbox. I will just use CSS to make them hidden so that sighted
users don't see them, but text readers will read them. I have tested
this and it works fine.

Thanks for the help. I have read that in ASP.NET v2.0 that they have
improved 508 compliant output.

Corey

That's easy enough to fix ...

Remove the label from your headertemplate (heck, remove the
headertemplate altogether!) and then,

Static tdHeader As WebControls.TableCell
Dim objTextBox As HtmlControls.HtmlInputText

Select Case e.Item.ItemType
Case ListItemType.Header
tdHeader = e.Item.Cells(0) '.FindControl("lblHeaderLabel")
Case ListItemType.Footer
objTextBox = e.Item.FindControl("txtFoot")
If Not tdHeader Is Nothing AndAlso _
Not objTextBox Is Nothing Then
tdHeader.Text = "<LABEL for='" & objTextBox.ClientID &
"'>Name</LABEL>"
End If
End Select


I said easy, not pretty :)

Once you are confident this works for you, you may think of
generalizing it; create your custom column, inherited from
bound/template column as required.

It's not always easy to extend the asp.net datagrid, but the rewards
are often worth the effort.

-- a --
 
C

Corey B

addup said:
That's easy enough to fix ...

Remove the label from your headertemplate (heck, remove the
headertemplate altogether!) and then,

Static tdHeader As WebControls.TableCell
Dim objTextBox As HtmlControls.HtmlInputText

Select Case e.Item.ItemType
Case ListItemType.Header
tdHeader = e.Item.Cells(0) '.FindControl("lblHeaderLabel")
Case ListItemType.Footer
objTextBox = e.Item.FindControl("txtFoot")
If Not tdHeader Is Nothing AndAlso _
Not objTextBox Is Nothing Then
tdHeader.Text = "<LABEL for='" & objTextBox.ClientID &
"'>Name</LABEL>"
End If
End Select


I said easy, not pretty :)

Once you are confident this works for you, you may think of
generalizing it; create your custom column, inherited from
bound/template column as required.

It's not always easy to extend the asp.net datagrid, but the rewards
are often worth the effort.

-- a --

Thanks. That did the trick. I just needed to think outside the box a
little bit. Thanks again for your help.

Corey
 
A

Alan Silver

objLabel.Text = "<LABEL for='" & objTextBox.ClientID & "'>Name
Column</LABEL>"

Actually, a more robust way to do this is the following HTML:

<label for="<%= objTextBox.ClientID %>">Name</label>

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top