Getting your data out of the data controls

After you've created a layout with a data control of your choice and eventually bound them to some datasource, you almost always want to get that data back out of them(and why wouldn't you, its your data). One thing you'll learn about developing asp.net webpages is that its like petting a porcupine (yikes). If you go with the flow, you probably won't get stuck, but the moment you try to go against the grain you end up with a hand full of thorns.

Most of the data controls have events ending in ing and ed e.g. RowUpdating, RowUpdated etc. In most of the event handlers of the ing events it is easy to get the values from the event args.

private void OnGridViewRowUpdating(object sender, GridViewUpdateEventArgs e) {

    IDictionary keys = e.Keys;

    IDictionary newValues = e.NewValues;

    IDictionary oldValues = e.OldValues;

}

Forgive me for the variation in my code formatting I'm trying to find the right one.

If we look at this event we can see the GridView nicely packages for us the new values, old values and the keys for the updating row. Unfortunately these dictionaries are only filled out if you are bound to a datasource control :(. So that means when you bind to some raw collection and hook up to the DataSource property then call databind, if you try to handle the delete or update events these dictionaries are going to be empty. Right now you must be asking yourself how can you get those dictionaries filled out just as if you were bound to a Datasource control? The good news is you CAN do it(this is what this blog post is all about right?).

Going Hunting in the Control Tree



This is probably one of the worst things you can do. From the time you write code that depends on the immediate layout of your page then your asking for trouble. I often see people on the forums writing code like this:

((TextBox)GridView1.Rows[e.RowIndex].Cells[2].Controls[1]).Text

Seeing stuff like that makes my spine tingle (and not in a good way). DO NOT write code like this!

FindControl
FindControl is a very powerful method on Control that allows you to search for a nested control, NOT synonymous to DOM function getElementById. I Often see people abusing find control and not understand that it is NOT recursive by default and complain when code like this

((TextBox)GridView1.FindControl("TextBox1")).Text

Throws a null reference exception. My advice is use Use FindControl as a last resort.

2-way DataBinding
2-way Databinding is a cool feature in ASP.NET 2.0 which allows the user to write some special syntax to bind against property values they would like to extract from a control.
There is alot of magic going on behind the scenes (which I will blog about in a separate post), but you do not need to know how it works to use it, e.g

<asp:GridView runat="server" ID="GridView1">

    <Columns>

        <asp:TemplateField>

            <EditItemTemplate>

                <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("ProductName") %>'></asp:TextBox>

            </EditItemTemplate>

        </asp:TemplateField>

    </Columns>

</asp:GridView>

When you write the <%# Bind("ProductName") #> expression then the value of the Text property is pushed into a dictionary which the data control can retrieve later. So how does GridView/FormView/DetailsView/ListView get these values? Each data control has a method which is responsible for populating a dictionary of name value pairs from field name to value. Here is a mapping of data control to method used to extract values:

GridView => protected ExtractRowValues
FormView => protected ExtractRowValues
DetailsView = > protected ExtractRowValues
ListView => public ExtractItemValues

As we can see with the exception of ListView these oh so useful methods are protected, that means we can't call them from our code if we are using these built in controls. What can we do to surface these methods:

  • Derive new controls that expose ExtractRowValues through a public method
  • Use private reflection to call the protected method (yikes)
  • Stick all of your interesting fields in DataKeyNames, then use the DataKeys[rowIndex].Values[fieldName] (and watch your ViewState grow :( )
  • Do Nothing :)

To rectify some of this I've written a little helper for the GridView (and the other controls if you demand) that basically duplicates the functionality of ExtractRowValues method.

public static IDictionary GetValues(GridViewRow row) {           

    IOrderedDictionary values = new OrderedDictionary();

    foreach (DataControlFieldCell cell in row.Cells) {

        if (cell.Visible) {

            // Extract values from the cell

            cell.ContainingField.ExtractValuesFromCell(values, cell, row.RowState, true);

        }

    }

 

    return values;

}

The method itself is pretty simple. It iterates over the control collection of the GridViewRow and calls ExtractValuesFromCell on each cell which puts values into the dictionary.

Uses
Most data controls support writing custom commands and handling some Command event(RowCommand for GridView). This method would come in handy if you needed to get the values out of the GridView for some custom command you wanted to execute.

If you can, use 2 way databinding, if your control doesn't have a useful property to bind against and you need to do some more logic to get the right value then use FindControl.

Edit:
I've updated the code to loop over the Cells collection instead of the Controls collection (which is alot cleaner). Also there is no real compelling reason to return a strongly typed IDictionary<string, object>, so I just return the IDictionary instead.

DetailsView:

Reader daveh551 needed some help adapting the code to DetailsView so here it is:

public static IDictionary GetValues(DetailsView detailsView) {
    IOrderedDictionary values = new OrderedDictionary();
    foreach (DetailsViewRow row in detailsView.Rows) {
        // Only look at Data Rows
        if (row.RowType != DataControlRowType.DataRow) {
            continue;
        }
        // Assume the first cell is a header cell
        DataControlFieldCell dataCell = (DataControlFieldCell)row.Cells[0];                
        // If we are showing the header for this row then the data is in the adjacent cell
        if (dataCell.ContainingField.ShowHeader) {
            dataCell = (DataControlFieldCell)row.Cells[1];
        }
 
        dataCell.ContainingField.ExtractValuesFromCell(values, dataCell, row.RowState, true);
    }
    return values;
}

FormView:

public static IDictionary GetValues(FormView formView) {
    IOrderedDictionary fieldValues = new OrderedDictionary();
    ExtractValuesFromBindableControls(fieldValues, formView);
    IBindableTemplate itemTemplate = null;
 
    if (formView.CurrentMode == FormViewMode.ReadOnly && formView.ItemTemplate != null) {
        itemTemplate = formView.ItemTemplate as IBindableTemplate;
    }
    else if (formView.CurrentMode == FormViewMode.Edit && formView.EditItemTemplate != null) {
        itemTemplate = formView.EditItemTemplate as IBindableTemplate;
    }
    else if (formView.CurrentMode == FormViewMode.Insert && formView.InsertItemTemplate != null) {
        itemTemplate = formView.InsertItemTemplate as IBindableTemplate;
    }
    if (itemTemplate != null) {
        foreach (DictionaryEntry entry in itemTemplate.ExtractValues(formView)) {
            fieldValues[entry.Key] = entry.Value;
        }
    }
    return fieldValues;
}
 
private static void ExtractValuesFromBindableControls(IOrderedDictionary values, Control container) {
    IBindableControl control = container as IBindableControl;
    if (control != null) {
        control.ExtractValues(values);
    }
    foreach (Control childControl in container.Controls) {
        ExtractValuesFromBindableControls(values, childControl);
    }
} 

Let me know if you find any bugs.
Hope this helps

UPDATE: I've had some requests to add more data controls and to convert the code to VB etc. So I decided to instead put the code in an assembly and make available for download here.

Published Friday, December 12, 2008 3:03 PM by davidfowl

Comments

# re: Getting your data out of the data controls

Saturday, December 13, 2008 9:11 AM by SGWellens

>> ((TextBox)GridView1.Rows[e.RowIndex].Cells[2].Controls[1]).Text

>> DO NOT write code like this!

I couldn't agree more.

# re: Getting your data out of the data controls

Saturday, December 13, 2008 9:23 AM by Will Green

What a cool bit of code! I've been doing the FindControl thing quite a bit on a recent project, and it always just smelled bad. I knew that there had to be a better, more decoupled way to do it. Thanks!

One minor quibble, though. FindControl() is not synonymous to the DOM's getElementById(). This is because the DOM has no concept of Naming Containers. That is, in the DOM, there can be only one element with the id "TextBox1". However, in the Code DOM, you can have multiple elements with the id "TextBox1", so long as they are in distinct Naming Containers (like a FormView or a GridViewRow).

The big difference is that getElementById() is a method of only the document object, while FindControl() is a method on every control derived from System.Web.UI.Control; FindControl() is always scoped to the control you're calling it on.

# re: Getting your data out of the data controls

Saturday, December 13, 2008 10:18 AM by davidfowl

Your right, thanks for correcting me :)

# re: Getting your data out of the data controls

Wednesday, December 24, 2008 4:24 AM by Prachik

Nice !!!

# re: Getting your data out of the data controls

Tuesday, December 30, 2008 2:38 PM by Vai

Very helpful!!

# re: Getting your data out of the data controls

Tuesday, January 13, 2009 3:14 AM by Deepak Rai

nice blog keep it up.

# re: Getting your data out of the data controls

Tuesday, January 13, 2009 10:53 AM by Naveen Jose

((TextBox)GridView1.Rows[e.RowIndex].Cells[2].Controls[1]).Text

is not good, that I agree.

What to do when using asp:BoundField?

Any other alternatives? [:)]

# re: Getting your data out of the data controls

Wednesday, January 14, 2009 4:22 PM by davidfowl

Hi naveenj, you can use the same code for BoundField. ExtractValuesFromCell is an abstract method on DataControlField and each implementation (BoundField, TemplateField etc.) each have a different implementation.

# re: Getting your data out of the data controls

Thursday, January 15, 2009 8:40 AM by Ed Dolikian

My current issue deals with extracting a non text value from a gridview row being selected.  (suppose I have two fields with a bytestream/array containing a Wav file and  bmp picture (SoundByte, ImageByte) respectively.  I have included them as fields in my Gridview control.

dim datarow as gridview1.selectedrow

'  This is the command I need...

dim Soundbytes() as byte = datarow("SoundByte")

dim photobytes() as byte = datarow("ImageByte")

me.textbox1.text = soundbytes.length & " Bytes retrieved from database ..."

Can you give me the proper syntax / method to retrieve this info into a byte array so I can use it in my code?  It only seems to support the retrieval of Text.

Perhaps what I need is to be able to get the data from the datasource based on the item selected.  Any help is much appreciated.

# re: Getting your data out of the data controls

Friday, January 16, 2009 3:07 AM by davidfowl

Hi Ed, in that case I think you'd need to serialize the bytes into a hidden field. Then when you want to retrieve it, deserialize the string into bytes. I can try to show you a sample if you'd like.

# re: Getting your data out of the data controls

Tuesday, January 20, 2009 8:54 PM by bayonian

How about DataList control ? Can you show me how to get some data from DataList ? I have the following scenario :

Col1 | Col2 | Col3

V1     CH1     1

V1     CH1     2

V1     CH2     3

This DataList data source 's bound to a function that returns IQueryable (LINQ. It works fine.

Anyway, I just want to get the a value from the Col1. Thanks.

# re: Getting your data out of the data controls

Wednesday, January 21, 2009 12:51 AM by davidfowl

@ bayonian

This won't work for DataList because it doesn't have this kind of abstraction. DataList is one of the older asp.net 1.1 controls. Also DataList and Repeater don't have this mechanism so your stuck with FindControl :(.

# re: Getting your data out of the data controls

Tuesday, January 27, 2009 8:13 AM by Kay

Maybe you see a lot of people writing the bad code you described because it's the way official MSDN suggests:

msdn.microsoft.com/.../system.web.ui.webcontrols.gridview.rowupdating.aspx

(see the sample)

# re: Getting your data out of the data controls

Wednesday, January 28, 2009 2:05 AM by davidfowl

@Kay Yeah that is pretty bad. I'll see what I can do about this.

Thanks for the heads up.

# Dont use findcontrol in data control &laquo; Phmadala&#8217;s Weblog

Thursday, January 29, 2009 1:28 AM by Dont use findcontrol in data control « Phmadala’s Weblog

Pingback from  Dont use findcontrol in data control &laquo; Phmadala&#8217;s Weblog

# re: Getting your data out of the data controls

Thursday, January 29, 2009 6:26 PM by daveh551

David,

A little help please.

I'm trying to adapt this for the DetailsView, which has Fields, not Cells. Here's the code I've ended up with:

public static IDictionary GetValues(DetailsView dv)

{

IOrderedDictionary values = new OrderedDictionary();

DataControlRowState state;

if (dv.CurrentMode == DetailsViewMode.Edit)

           state = DataControlRowState.Edit;

else if (dv.CurrentMode == DetailsViewMode.Insert)

           state = DataControlRowState.Insert;

       else state = DataControlRowState.Normal;

       foreach (DataControlField field in dv.Fields)

{

  if (field.Visible)

  {

     // Extract values from the cell

field.ExtractValuesFromCell(values, ??? , state, true);

   }

}

       return values;

}

I can't figure out what to put in for the cell, where I have ???, since it wants a DataControlFieldCell, and what I have is a DataControlField.  I'm not exactly clear on the what the relation between a DataControlFieldCell and a DataControlField is, other than the cell is contained in the field. But I don't know how to find or create the cell in this case.

Can you help?

# re: Getting your data out of the data controls

Friday, January 30, 2009 1:30 AM by davidfowl

Hi daveh551,

DetailsView is a bit more complicated than GridView but I've updated the post with code that works with DetailsView. Hope this helps you out.

# re: Getting your data out of the data controls

Thursday, February 12, 2009 10:19 AM by BenJ83

Can u plz make an example of a listview? Thx

# re: Getting your data out of the data controls

Friday, February 13, 2009 1:29 PM by sjanne

Hi David,

Thank you so much for your post.

Can you please let me know how can we achieve the below task.

Stored proc returns the foll data (using OpenQuery)

Store1               UPC1                        DESC                    QTY   Units to Ship

1                     5000001547    FRS OCEAN FISH CAT 3      3             18

1                     5000005147    FRISKIE GOURMET CAT      3             18

1                     5000042034    FRS BUF 24CT VARTY 4      3             12

9                     5000001547    FRS OCEAN FISH CAT 3      2             12

9                     5000005147    FRISKIE GOURMET CAT      2             12

9                     5000008450    FRISK SIGNATURE BLEN    2              12

And I need to display it like this

Store:  1                 Units to Ship 3

                     upc                  desc                      

                 5000001547    FRS OCEAN FISH CAT 3    

                 5000005147    FRISKIE GOURMET CAT    

Store 9                Units to Ship 2

                   UPC                   DESC                  

                5000001547    FRS OCEAN FISH CAT 3              

                5000005147    FRISKIE GOURMET CAT      

                5000008450    FRISK SIGNATURE BLEN    

As you can see from the data, Units to Ship has same value for a store. And I need to make just that field editable and on top of that I need to push the changes all at once. So user would be able to update multiple values at the same time. I placed a update button outside the grid, though I am yet to achieve the same display, but I am not able to read the values from any of the controls in the button click event which is outside GridView. I am using VB.NET for the first time.

Dim txtShip As New TextBox

Dim lblStoreIds As New Label

Dim hdnStoreIds As New HiddenField

                       'txtShip = CType(gv.Rows(i).FindControl("txtShipQtyInGv"), TextBox)

                       'txtShip = CType(gv.Rows(i).FindControl("txtQty"), TextBox)

                   'lblStoreIds = DirectCast(gvShipperComponents.Rows(i).FindControl("hdnSnum"), HiddenField)

                   'hdnStoreIds = CType(gvShipperComponents.Rows(i).FindControl("hdnSnum"), HiddenField)

                   'hdnStoreIds = TryCast(gvShipperComponents.Rows(i).Cells(8).FindControl("hdnSnum"), HiddenField)

                   'hdnStoreIds = CType(gv.Rows(i).FindControl("hdnSnum"), HiddenField)

                   'hdnStoreIds = CType(gv.Rows(i).Cells(7).Controls(1), HiddenField)

                   'hdnStoreIds.Value = DirectCast(gv.Rows(i).FindControl("hdnSnum"), HiddenField).Value

                   lblStoreIds.Text = CType(gv.Rows(i).FindControl("lblSnum"), Label).Text

                   'If IsNumeric(lblStoreIds.Text.ToString()) = True Then

                   '    txtStoreIds += System.Convert.ToInt32(lblStoreIds.Text) & ","

                   'End If

As you can see I tried all the 3 types I am aware of like TryCast, DirectCast, CType to read the values. But its always empty.

Any suggestions please?

Thanks

Sudhakar Janne

# re: Getting your data out of the data controls

Wednesday, February 18, 2009 4:29 AM by sangam100

Hi dear author,

I came here following the discussion on the asp.net forum: forums.asp.net/.../1362718.aspx

I have always been using FindControl() method: much easier and handy. You need not to write (and call everytime) the functions to extract values (or cells) using ExtractValuesFromCell.

This ExtractValuesFromCell way of extracting values from cell is just another way around it. So what's wrong to go with FindControl. Please give your opinion (perhaps you could update the post to add a sections: Why to not use FindControl(); I am requesting this because the argument among developers would go longer!).

Hey, this could be the advantage: People sometimes ask how to get all the values from my gridview since I have been editing and updating it, so changing the original value from the datasource. In such case this trick could help a lot.

Thanks.

# re: Getting your data out of the data controls

Thursday, February 19, 2009 1:45 AM by davidfowl

@samgram100 I had this same question about FindControl on the forum. Here is what I said:

It's not the use of FindControl that makes my spine tingle its the MISuse and ABuse of it that does. FindControl is a good method and very useful, but look at the name of method, FindControl. Everything has a purpose right? If I'm trying to find a control then i'll call find control. If I'm trying to get data out of a control I might call find control or some other API that is better at getting data, i.e ExtractRowValues. I think that there is alot of knowledge out there, good and bad, about using these data controls and I just want to try to set some things straight by shedding some more light on how stuff works (I love that website).

Before I go lemme show you what I mean by picking the right API.  Lets make a new control IntControl. IntControl has a method int GetValue(). Now OO principals tell us to not break the abstraction by letting consumers grab at the internals but IntControl derives from control, and by default, there is alot of other things exposed to the consumer that shouldn't be. Now we know that IntControl uses a textbox internally so I could never call GetValue and instead write:

int value;

Int32.TryParse(((TextBox)IntControl.Controls[0]).Text, out value);

Everything will work fine until the author of IntControl decides to use a div instead of a textbox.

The REAL lesson to take away from the blog post is to try your best to follow the contract provided by a class or an interface (in this case the DataControl).

# re: Getting your data out of the data controls

Wednesday, March 04, 2009 2:13 PM by Ann

I'm still not getting to read the new updates values of the editing row. Whats missing?

# re: Getting your data out of the data controls

Saturday, March 07, 2009 3:34 AM by davidfowl

What are you trying to do?

# re: Getting your data out of the data controls

Saturday, March 14, 2009 8:15 PM by GrandpaB

David,

I've been looking for a way to extract Insert and Update data from the DetailsView; I hope that your code is the solution.  I have read and re-read the code that you provided for the DetailsView, bit alas I'm a C# dullard. Could you please translate for your VB readers?  My ASP application uses several small CSV files. I wrote a class that extracts a record from the CSV file, loads it into a List(of Members) and binds the list to the DetailsView. It is all working except my ability to grab the Insert & Update data from the DetailsView.  

# re: Getting your data out of the data controls

Thursday, May 14, 2009 12:00 PM by MdV

I'm trying to use the DetailsView version (translated to VB), but the values collection remains empty.

Right now I'm calling GetValues in OnItemUpdating to capture the new values to be able to update my database.

Isn't that the place to do this?

My GetValues code:

Public Function GetValues() As IOrderedDictionary

       Dim l_aValues As IOrderedDictionary = New OrderedDictionary()

       For Each l_oRow As DetailsViewRow In Me.Rows

           ' only look at DataRow

           If l_oRow.RowType = DataControlRowType.DataRow Then

               Dim l_oCell As DataControlFieldCell = CType(l_oRow.Cells(0), DataControlFieldCell)

               If l_oCell.ContainingField.ShowHeader Then

                   ' if we are showing the header for this row then the data is in the adjacent cell

                   l_oCell = CType(l_oRow.Cells(1), DataControlFieldCell)

               End If

               l_oCell.ContainingField.ExtractValuesFromCell(l_aValues, l_oCell, l_oRow.RowState, True)

           End If

       Next

       Return l_aValues

   End Function

# re: Getting your data out of the data controls

Thursday, July 23, 2009 9:06 AM by Marc

The link for the assembly including the vB version is broken. the link at the end of the article. Can this pleae get fixed?

# re: Getting your data out of the data controls

Thursday, July 23, 2009 10:00 AM by Marc

I must say it nice to see some details on this. I have been searching for information on FormView for weeks now and can't find anything helpful.

Can you develop this coding scheme for FormView and exlain how I can populate textboxes on the Insert Template from drop down boxes that I have added to simplify data entry. The DD are also on the Insert template. I can not seem to get at the data and move into the text boxes.

# re: Getting your data out of the data controls

Friday, September 04, 2009 10:55 AM by marco

nice... thank you....

# re: Getting your data out of the data controls

Wednesday, December 02, 2009 10:30 AM by Glenn Byerly

I tried downloading the assembly via the link, but it appears to be broken.

# re: Getting your data out of the data controls

Wednesday, March 31, 2010 1:22 AM by Ravi R A

Hi David,

This is a really interesting way to extract the values from the DataControls.  I've been searching for such a thing for a long time.. and im new to Asp.Net programming...

The thing is, i've got a Business Object which i bind to the GridView.. now wen i extract the values from the GridView in the way u've mentioned i get the values in the IDictionary object.  I jus want to know, how should i map these values back to my Business Object ?

# re: Getting your data out of the data controls

Wednesday, March 31, 2010 1:35 AM by davidfowl

Hey Ravi,

If you send me an email I can show you how you can reconstruct your object from the dictionary. You have to use reflection to create the business object and set property values from the dictionary key value pairs.

# re: Getting your data out of the data controls

Wednesday, March 31, 2010 2:34 AM by Ravi R A

Hi,

Ya Sure... Can u let me know ur EMail ID ?

# re: Getting your data out of the data controls

Wednesday, March 31, 2010 3:37 AM by davidfowl

weblogs.asp.net/.../Profile.aspx

Send me one from here.

Thanks

# re: Getting your data out of the data controls

Sunday, April 04, 2010 9:28 PM by raravi85

Hi David..

I've sent u the mail... Waiting 4 ur reply...

# re: Getting your data out of the data controls

Wednesday, July 28, 2010 11:02 AM by zamdrist

Good stuff for sure, but doesn't anyone find it a little odd that extracting values from a gridview is such a convoluted and painful process?

# re: Getting your data out of the data controls

Friday, October 08, 2010 5:26 AM by lucyconnuk

Thanks for this excellent bit of code, just what I needed.

Had a problem with it to start with, but then I realised it was because I was using Eval("ProductName") in my DetailsView, instead of Bind("ProductName"). Once I'd fixed that, it worked brilliantly.

Leave a Comment

(required) 
(required) 
(optional)
(required)