mhinze.com archive

this is an archive of the old blog, ended 6/16/08





    09
    Jun

    Where is the Row in RowCommand? DisplayIndex

    So I sat down to do a little "data-driven web development with ASP.NET 2.0" today. 

    GridView, check.  DataSource, DataBind, check. 

    Next I wanted to add a button to each row to perform an action on the item the row represented. 

    So I added my ButtonField and ran into this problem: GridViewCommandEventArgs doesn't expose the Row. (!)

    Fair enough, I'll just add a Template Column and set the CommandArgument like Rick Strahl explains.

    But that didn't work either - actually there are a couple problems.  First, I needed two fields, or the entire row, not just the PK.  And second, I want it to eventually handle paging and it has some concurrency issues because RowIndex no worky-worky since it looks at the data source and not at the grid on the page. 

    Also, I have mutliple grids firing this event handler, so I didn't ever want to reference a specific instance of the GridView.

    Well at first I tried hard coding both fields into the CommandArgument and then splitting them up in the code-behind:

    <asp:gridview id="GridView1" runat="server" onrowcommand="Grid_Command">
        <Columns>
            <asp:TemplateField ShowHeader="False">
                <ItemTemplate>
                   <asp:LinkButton ID="Link1" runat="server"
                        CausesValidation="false"
                        CommandName="Cmd"
                        <!– in the ghetto, below –>
                        CommandArgument='<%#Eval("ID") + ":" + Eval("Name") %>'
                        Text="DoStuff">
                        </asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:gridview>

     

    protected void Grid_Command(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName == "Cmd")
        {
            string[] arg = (string[])e.CommandArgument.ToString().Split(':');
            int id = int.Parse(arg[0]);
            string name = arg[1];
            DoStuff(id, name);
        }
    }
     

    The above works great, and I should be commended for my hackery.  But it's WRONG!

    So the solution to this unfortunate hubbub lies with a little known property called DisplayIndex, which gives you the index of the selected row on the page.

    Also, make sure you set the DataKeyNames property of the GridView.

    <asp:GridView ID="GridView1" runat="server" OnRowCommand="Grid_Command" DataKeyNames="ID,Name">
        <Columns>
             <asp:TemplateField ShowHeader="False">
                <ItemTemplate>
                    <asp:LinkButton ID="Link1" runat="server"
                        CausesValidation="false"
                        CommandName="Cmd"
                        CommandArgument='<%# Container.DisplayIndex %>'
                        Text="DoStuff">
                    </asp:LinkButton>
                </ItemTemplate>
            </asp:TemplateField>
         </Columns>
    </asp:GridView>
     
     

    protected void Grid_Command(object sender, GridViewCommandEventArgs e)
    {
        if (e.CommandName == "Cmd")
        {
            GridView gv = (GridView) sender;
            int index = int.Parse(e.CommandArgument.ToString());
            int id = (int) gv.DataKeys[index]["ID"];
            string name = (string) gv.DataKeys[index]["Name"];
            DoStuff(id, name);
        }
    }

     

    Not new, but the hotness!

     

     

    Technorati tags: , ,

    2 Responses to “Where is the Row in RowCommand? DisplayIndex”

    1. Rick Strahl Says:

      Ah - one more way to do this :-}.

      But that was part of my point - there are so many different ways to get at these values and none of them are easily discoverable nor obvious in how you hook them up. In fact I didn't even know that you can use the datakeys this way (apparently I don't use the grid often enough)…

      It sure would be much easier if the selected DataItem came back directly in which case we'd have access to the underlying data directly.

    2. Rob Kent Says:

      Datakeys depends on ViewState, as I discovered with much pain on a previous application. I tend not to use ViewState if I can avoid it because I don't like that much data being transported in the HTML.

      However, that means you must rebind your grid during Init in order to fire the command events. This in turn means that you must have a persistent store for your original datasource and the list contents must not change since the previous bind, otherwise row 'n' may no longer be row 'n' and you end up performing the action against the wrong PK row.

      The above problem happened on an auction site I was working on in which I had to make sure the list contents were always bang up to date. The list I bound to had changed and I selected the wrong row (during testing, I hasten to add).

      This problem is exacerbated if you use an ObjectDataSource against a business facade, because the ODS will fetch the data from the data tier when it wants to. Even if you turn on caching to avoid this, it is not optimal because it uses application cache, so expiring the key applies to all users. I ended up overriding the ODS cache with my own in session and setting a flag when I wanted it to refresh for the db, normally when the user sorted or paged. Also the ODS does not work if you are using sorting, so is completely useless IMHO.

      After looking in depth at all this, I think that the concept behind the server-side event structure is wrong somehow, in that you should be able to fire row or command events without using viewstate or rebinding - which are both inefficient.

      Even without using Javascript and the explicit Viewstate object, it would have been better to have some PK collection implemented in a hidden form field and properly wire up each control to reference that, so that you could post back and fire an event whose argument was always your PK, or a pseudo-PK of the row index if you didn't provide one.

      I tend to use different approaches dependent on the needs of the grid. Typically, I avoid ODS, turn off Viewstate, use templated fields with the PK argument, and store the list source in Session. However, I don't like this at all and think MS should redesign databinding and event firing for listbound controls.

    Leave a Reply

    You must be logged in to post a comment.

    © 2007 mhinze.com