Share on FacebookThe problem
I've been playing with ASP.NET's ListView this past days, trying to add functionality for an internal development framework here in Iceberg, and one of the features I wanted to add was the ability to click a "row" to select it.
The problem is that this control is not like past controls like the GridView. It allows the programmer to define anything as the layout, working 100% with templates, so there isn't a row per se, because the developer can define anything in the template for the item, even grouping.
As far as I know, you can't know server-side (at least easily) which row was clicked (unless there is a Button in the row, but this is an ugly requirement to have in my custom grid).
The idea
I tried a lot of different approaches, but I also wanted to minimize code (also I couldn't find anything useful already done).
I was suspicious as to why it worked so easily with a button, and I found this:
Because the ListView control uses templates, it does not provide a way to automatically generate buttons to perform update, delete, insert, sort, or select operations. You must manually include these buttons in the appropriate template. The ListView control recognizes certain buttons whose CommandName property is set to a specific value.
Would it work with a hidden button? It would! All I'd need to do is capture the click somewhere and pass it to the button using JavaScript
But leaving the work for the developer making the template wasn't good nor polite
The solution
I decided to create a custom table row to add the functionality
/// <summary>
/// ListView selectable row
/// </summary>
[ToolboxData("<{0}:ListViewExSelectableRow ClickAction=\"Select\" runat=\"server\"></{0}:ListViewExSelectableRow>")]
public class ListViewExSelectableRow : HtmlTableRow
{
// (...)
}
Next we need to append the button to the row, like this
private Button _dummyCommand;
protected override void OnInit(EventArgs e)
{
_dummyCommand = new Button();
_dummyCommand.CommandName = "Select";
_dummyCommand.Style["display"] = "none";
HtmlTableCell c = new HtmlTableCell();
c.Controls.Add(_dummyCommand);
this.Controls.Add(c);
base.OnInit(e);
}
The only requirement is for it to have some valid CommandName
The final step in the component is to render it, so we override the Render method, like so
protected override void Render(HtmlTextWriter writer)
{
writer.WriteBeginTag("tr");
if (this.DesignMode == false)
{
if (this.ClickAction != ListViewExSelectableRowClickActions.None)
writer.WriteAttribute("onclick",
"document.getElementById('" + _dummyCommand.ClientID + "').click();");
}
writer.Write(HtmlTextWriter.TagRightChar);
RenderChildren(writer);
writer.WriteEndTag("tr");
}
And that's it! You just use it in your template as you would any table row, and it will automatically select the row you click
<ItemTemplate>
<JMF:ListViewExSelectableRow ClickAction="Select" runat="server" >
<td>
<asp:Label ID="ProductIDLabel" runat="server" Text='<%# Eval("ProductID") %>' />
</td>
<td>
<asp:Label ID="ProductNameLabel" runat="server" Text='<%# Eval("ProductName") %>' />
</td>
<td>
<asp:Label ID="UnitPriceLabel" runat="server" Text='<%# Eval("UnitPrice") %>' />
</td>
<td>
<asp:Label ID="CategoryNameLabel" runat="server" Text='<%# Eval("CategoryName") %>' />
</td>
</JMF:ListViewExSelectableRow>
</ItemTemplate>
Of course you can extend it and add any property you need, like for example making the click action customizable by the developer.
I hope this is useful to someone.