I was working with a DataGrid that had a ButtonColumn in it. I had a need to set the CommandArgument for this
button. Did you know there is no way to set a CommandArgument for a ButtonColumn?
I was all prepared to grab that control and set that property in the ItemDataBound event, but it doesn’t seem to
exist. Most people would resort to a template column, stick a button in it and work on that control. Problem was, I was doing everything in code with no markup. That adds a little complexity to that alternative.
Setting a simple breakpoint in the ItemDataBound event, I looked a little closer at what I had to work with in the
Immediate window.
? e.Item.Controls(3) {System.Web.UI.WebControls.TableCell} System.Web.UI.WebControls.TableCell: {System.Web.UI.WebControls.TableCell} ? e.Item.Controls(3).Controls.Count 1 ? e.Item.Controls(3).Controls(0) {Text = "Edit"} System.Web.UI.WebControls.DataGridLinkButton: {Text = "Edit"}
Hmm, it’s a DataGridLinkButton. And it does have a CommandArgument property. So let’s find that control
and cast to that type and set that property.
I see. So this type is not user-accessible. It doesn’t even show up in the Object Browser. However,
it does show up in Reflector, and I can see that it inherits from LinkButton, which is public. Let’s whip up a
quick function to find that control and return a LinkButton for setting the CommandArgument.
Woah, slow down a bit. This is a ButtonColumn and it can be a link button, command button, or an image
button. If we have a function specifically for LinkButton, it’s going to potentially error out. In the
typical, excellent design of the .NET framework, these three button types are all related using the IButtonControl
interface, which has properties for CommandName and CommandArgument. So by using the interface instead of the
exact type, we’re being safe and future-proofing ourselves against other button types.
Private Function GetButtonColumnButton(row As DataGridItem, commandName As String) As IButtonControl
Return RecurseRowControls(row, commandName)
End Function
Private Function RecurseRowControls(ctl As WebControl, commandName As String) As IButtonControl
Dim btn As IButtonControl
' loop through embedded controls
For Each c As WebControl In ctl.Controls
btn = TryCast(c, IButtonControl)
' if it is a button and the command name matches, return it
If btn IsNot Nothing AndAlso String.Compare(btn.CommandName, commandName, True) = 0 Then
Return btn
End If
' if the control has child control, search them for the button
If c.HasControls Then
btn = RecurseRowControls(c, commandName)
If btn IsNot Nothing Then Return btn
End If
Next
' no button found
Return Nothing
End Function
And just like that, we can now have access to the button’s properties like Text, CommandName, CommandArgument, and
CausesValidation. That’s some great stuff there.
Private Sub Grid_ItemDataBound(sender As Object, e As DataGridItemEventArgs) Handles Me.ItemDataBound
Dim btn As IButtonControl
If e.Item.ItemType = ListItemType.AlternatingItem Or e.Item.ItemType = ListItemType.Item Then
btn = GetButtonColumnButton(e.Item, "Edit")
btn.CommandArgument = "something like an ID"
btn.Text = "specific text label"
End If
End Sub