locked
non-visible column in gridview does not expose data RRS feed

  • Question

  • Hi there,

    I wonder if you can help me.

    I am using a gridview in a web project and need to get a value out of the selected row to pass as an argument in the c# code. the gridviewrow exposes the value if the column is set to visible, but I want to hide the value from the UI because it is a foreign key. when I set visible=false I can no longer access the value:

    code sample

    columns:
    hidden
     <asp:BoundField DataField="state" SortExpression="state" Visible="False" >                </asp:BoundField>

    visible:
    <asp:BoundField DataField="supplierid"                                           SortExpression="supplierid" ></asp:BoundField>

    code behind:
    GridViewRow row = dgRoadpavingRequests.SelectedRow;
    txtMessages.Text = ds.getHistory(Convert.ToInt32(row.Cells[2].Text));

    so if I needed the supplierid I can access it, but the same code does not expose the state value.

    I would appreciate you help with this issue.

    regards
    Helen
    Thursday, November 9, 2006 7:31 AM

Answers

  • You can have any number of data keys.  The DataKeyNames property is an array so you can specify any # of properties that you need.  Technically though you only need to store the property(ies) that you don't display in other columns.  Assuming that you only need the key for a particular row during postback the overhead of a DB lookup should be negliable.  If you are reacting to button clicks then use the CommandArgument property of the button to store the information you'll need. 

    Ultimately I'd recommend that you only send to the client the bare minimum needed to render the page and react to postbacks.  Retrieving data on postback based on a stored PK is efficient in terms of both performance (server to browser and back) and memory utilization.  The overhead of the actual DB call is generally low.  Ultimately though it depends on how you are loading the data.  For example if you are loading a data set with 1000 rows of data just to show 10 rows that is inefficient.  Generally, in a web environment, it is important to limit the data that is returned from each query to only the data that is needed.  Using caching is also a good performance technique when data is shared by multiple users of the application.  You can use session (or profile in v2.0) to store per-user data but this should be limited to small pieces of data in order to support a high number of users.

    Michael Taylor - 11/12/06 

    Sunday, November 12, 2006 7:36 PM

All replies

  • Correct.  It boils down to the standard practice for all web controls.  If it isn't visible then the rendering is short-circuited to reduce the payload to the client.  There are workarounds to this problem.  You can use ItemDataBound to hide the column's cell after it is bound.  You could also set the column width to zero.  There are many other techniques that would also work.

    Michael Taylor - 11/9/06

    Thursday, November 9, 2006 2:14 PM
  • Hi Mike,

    thanks for your reply.

    setting the column width to 0 does not work - I'd tried that already. I think possibly because there is data in the column. (I set it on the header, item and footer.)

    I will try the itemdatabound approach and revert

    I find your reasoning interesting because I have used two other hidden fields (to strip the time of the date to facilitate searching on date). When I use the fiterexpression method on the sqldatasource and reference a hidden field it works fine.

    The visibility problem only seems to occur with a gridviewrow.

    regards
    Helen
    Friday, November 10, 2006 7:37 AM
  • It sort of stinks that you'll have to revert to event handling to get the behavior you want.  Attaching data to a hidden column is pretty standard.  However MS has provided another alternative that might work better for you.  The DataKeys/DataKeyNames properties are designed to allow you to attach keys (or more specifically a field's value) to each row in the grid.  This data is available after a postback and is the better option (compared to a hidden column) for getting data associated with a row.  Normally the property contains an item's unique identifier.  The SelectedDataKey will give you the data key for the selected row (if you are using that functionality).  Note however that for this to work you'll need to either ensure that a row is selected or at least have access to the index of the row that you want to interact with.

    Alternatively if you are attaching functionality to a button (or similar control) in the row then you can reference the item's identifier directly.  For example when a row has an update button it is common to set the CommandArgument property to the identifier of the item in the row.  This saves a lookup later and is especially useful when redirecting to another page.  For buttons it resolves the issue of not having to select the row or having to track a row's index.

    As for the visibility issue all web controls (by default) work that way.  They are short-circuited in the base class if I remember correctly.  A hidden field is a little different as it is not designed to be displayed.  The somewhat disappointing issue here is that a column is not actually a web control at all so the short-circuit code occurs in the grid view itself.  It would have been nice if hidden columns would still be generated because sometimes it is nice to be able to expand and collapse columns in a grid without posting back to the server.

    Hope this helps,

    Michael Taylor - 11/10/06

    Friday, November 10, 2006 1:47 PM
  • Hi Mike,

    I have already assigned the datakey to be the pk on the table, and I'm assuming that I can't have more than one?

    I don' t understand why the visibliity issue has arisen in VS2005 - with the gridview, I never used to have this problem in 2003 - with the datagrid and dataset.

    Now that we have to use the sqldatasource and a dynamic dataset is created - you can't access the latter in the code (or can you?)

    I guess I could use the pk and fetch the values I need in another statement - but that doesn't seem very efficient. I'm also using DDA and nhibernate so I have performance issues as it is.

    thanks for your input

     

    regards

    Helen

     

     

     

    Sunday, November 12, 2006 8:08 AM
  • You can have any number of data keys.  The DataKeyNames property is an array so you can specify any # of properties that you need.  Technically though you only need to store the property(ies) that you don't display in other columns.  Assuming that you only need the key for a particular row during postback the overhead of a DB lookup should be negliable.  If you are reacting to button clicks then use the CommandArgument property of the button to store the information you'll need. 

    Ultimately I'd recommend that you only send to the client the bare minimum needed to render the page and react to postbacks.  Retrieving data on postback based on a stored PK is efficient in terms of both performance (server to browser and back) and memory utilization.  The overhead of the actual DB call is generally low.  Ultimately though it depends on how you are loading the data.  For example if you are loading a data set with 1000 rows of data just to show 10 rows that is inefficient.  Generally, in a web environment, it is important to limit the data that is returned from each query to only the data that is needed.  Using caching is also a good performance technique when data is shared by multiple users of the application.  You can use session (or profile in v2.0) to store per-user data but this should be limited to small pieces of data in order to support a high number of users.

    Michael Taylor - 11/12/06 

    Sunday, November 12, 2006 7:36 PM
  • Hi Mike,

    I used the datakeynames array and that has sorted the problem

    thanks for all your help

    regards

    Helen
    Tuesday, November 14, 2006 7:44 AM
  • Taylor

    Could you Please send an example of how to access non-visible column data in gridview.

    Your help will be appreciated

     

     

    Thursday, March 8, 2007 10:21 AM
  • Hi

    Could you Please send an example of how to access non-visible column data in gridview.

    Your help will be appreciated

    Thursday, March 8, 2007 10:23 AM
  • Hi Taylor & others,

    If you look at the properties of the gridview there is one called datakeynames.
    this is usually associated only with the primary key of the row that you are representing.

    but it is actually an array - so you can create a comma delimited list in there of your invisible fields.

    on select index changed, access them as follows(obviously in the order that you listed them):

    DataKey dRow = GridView1.DataKeys[row.DataItemIndex];
    string recordId = dRow[0].ToString();
    string clientId = dRow[1].ToString();
    string supplierId = dRow[2].ToString();

    //use variables as required

    regards
    Helen




    Thursday, March 8, 2007 3:45 PM
  • But how do you access a datakeyvalue on the selected row in the RowCommand event of the GridView
    Monday, March 12, 2007 4:52 PM
  • This worked for both rowcommand and selected index changed.

     

    My version of Helen's code is the following  (in case you got errors with the row object)

     

    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    GridViewRow gvrow = GridView1.SelectedRow;

    DataKey dRow = GridView1.DataKeys[gvrow.DataItemIndex];

    string testID = dRow[0].ToString();

    Session["test"] = testID;

    }

     

    Wednesday, April 25, 2007 9:02 PM
  • I am getting a stange issue.

    When I select the row for the first time, then the session value is not being taken. However from next time onwards, it is working. Can anyone help?

    My code is on selectedindexchanged property of the gridview

     

    protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    GridViewRow gvrow = GridView1.SelectedRow;

    DataKey dRow = GridView1.DataKeys[gvrow.DataItemIndex];

    string testID = dRow[0].ToString();

    Session["test"] = testID;

    }

     

    protected void Page_Load(object sender, EventArgs e)

    {

    if (IsPostBack)

    {

    //Response.Redirect("testSession.aspx");

    TextBox1.Text = (string)Session["test"];

    }

    }

     

    Textbox1 is not populated on the first click. But thereafter it works.

     

    Wednesday, April 25, 2007 9:05 PM
  • Replying to self

     

    Moved the code to selectindexchanged and it started working. This is happening because the postback methods gets called before the selected index is changed!

    Wednesday, April 25, 2007 9:26 PM
  • Hi Taylor - I am also trying to achieve something like what you have explained here. Using DataKeyNames it works great, till the time I am on a different GridPage. Basically when I am on 1st page the DataKeyNames works ok, but when my PageIndexing for the GridView changes and when I am on a different Page, I get Index Out of range when I try to Select a Valye using the Select Link autogenerated by the Grid, when one has AutoGenerateSelectButton="true"

     

    Can someone help me here why I get Index Out of Range error when I am on different GridView Page?

     

    Thanks

     

    Thursday, December 27, 2007 8:14 PM
  • TaylorMichaelL said:
    "update button it is common to set the CommandArgument property to the identifier of the item in the row"

    Thanks! this was a huge help, and saved me some considerable time.

    My challenge was adding up/down buttons to a gridview in VS 2003 to reorder a "rank" value in the database.
    Since the up/down buttons are just imagebuttons, they're not selecting the row before their event fires. 
    So I used your method and added my id to the command Argument property.

    Thanks Again - here is some sample code.



    <asp:GridView ID="GridView1_Beneficiary" runat="server" AllowSorting="True"
                AutoGenerateColumns="False" DataSourceID="SqlDS1_Beneficiaries" CellPadding="4" DataKeyNames="fund_id,email_id,priory_fund_seq_num,unit_priority" ForeColor="#333333" GridLines="None" PageSize="20" CssClass="GridViewStyle stack " AllowPaging="True" OnSelectedIndexChanged="GridView1_Beneficiary_SelectedIndexChanged1" OnRowCommand="GridView1_Beneficiary_ItemCommand" >
                <Columns>
                    <asp:TemplateField >
                        <ItemTemplate>
                            <asp:ImageButton runat="server" ToolTip="Click to move this row up" ImageUrl="../images/up.gif"
                                ID="btnUp" CommandName="MoveUp" CssClass="arrows" CommandArgument='<%# Eval("fund_id") %>' />
                            <asp:ImageButton runat="server" ToolTip="Click here to move this row down" ImageUrl="../images/down.gif"
                                ID="btnDown" CommandName="MoveDown" CssClass="arrows" CommandArgument='<%# Eval("fund_id") %>' />
                        </ItemTemplate>
                    </asp:TemplateField>
                       //...
                       //... (rest of gridview)


    Then in my code behind I was doing this when the column was visible:
    protected void GridView1_Beneficiary_ItemCommand(object sender, GridViewCommandEventArgs e) 
        { 
           GridViewRow row = (GridViewRow)((Control)e.CommandSource).Parent.Parent; 
           int rowID = row.DataItemIndex; 
           cmd.Parameters.AddWithValue("@fund_id", GridView1_Beneficiary.Rows[rowID].Cells[FUND_ID_CELL_NUM].Text); 
           //... 



    But now I can do this:
    protected void GridView1_Beneficiary_ItemCommand(object sender, GridViewCommandEventArgs e) 
        { 
           GridViewRow row = (GridViewRow)((Control)e.CommandSource).Parent.Parent; 
           int fundID = e.CommandArgument.ToString(); 
           int rowID = row.DataItemIndex; 
           cmd.Parameters.AddWithValue("@fund_id"fundID); 
           //... 

    • Proposed as answer by teberg Thursday, October 1, 2009 7:50 PM
    Wednesday, March 25, 2009 7:27 PM
  • Great! Exactly wat I was looking for, thanks!! 
    Thursday, October 1, 2009 7:51 PM