Solved

Dynamically TextBox ID from DataBase - Generating and Retrieving

Posted on 2004-09-02
18
517 Views
Last Modified: 2008-01-09
Hi I have a form that contains a DataRepeater where a varying number of text and textboxes are created depending on the data returned from the database in a given situation:

<asp:Repeater ID="myInputList" runat="server">
   <ItemTemplate>
        <tr>
          <td><%#DataBinder.Eval(Container.DataItem,"DetailName")%>:
           <asp:TextBox ID="<%#DataBinder.Eval(Container.DataItem,"DetailID")%>" runat="server"/>
          </td>
        </tr>
  </ItemTemplate>
</asp:Repeater>

My problem is that I don't know which DetailID is displayed for each textbox and how to refer to it when trying to retrieve the text input.

Should I use some kind of loop somewhere instead?

0
Comment
Question by:champ_010
  • 13
  • 5
18 Comments
 
LVL 8

Expert Comment

by:shovavnik
ID: 11962086
Does your textbox throw an onchange event?

Are you trying to get the id on the server or on the client?
0
 
LVL 1

Author Comment

by:champ_010
ID: 11963990
No, my textbox doesn't have an onChange event.

I'm not sure what you mean with the second question.

I'm thinking I need some kind of loop to create the IDs or to retreive them.  Maybe for exampl in the html form <input type="text" name="someName+1each time" id="different from the database each time">  Does that work? How can I do that with asp:textbox etc.?  Thanks.
0
 
LVL 8

Expert Comment

by:shovavnik
ID: 11964116
Are you tring to retrieve the text input in your code behind file after post back, or using javascript on the client?

The DataRepeater essentially uses a loop to create your text boxes.

Your code should be creating textboxes correctly.  If you look at the source (View Source), you'll be able to see exactly what the id of the textbox looks like on the client.
0
 
LVL 1

Author Comment

by:champ_010
ID: 11966073

I'm trying to retrieve the text input after postback--or onClick of a button to submit the form fields. Yes, the texboxes are being created correctly and yes the IDs are correct if I view source but when the DataRepeater creates the textBoxes   <asp:TextBox ID="<%#DataBinder.Eval(Container.DataItem,"DetailID")%>" runat="server"/> I don't know what the ID is so I don't know how to refer to each one by it's id to retrieve it.

There is a dropdownlist in my form and depending on what selection the user makes, a different set of textboxes should be generated. If the value of the dropdown selected is 2 and that is sent to the database then all DetailNames and DetailIDs that have a SubcategoryID = 2 is returned.  That can mean 6 textboxes with DetailIDs from 6-12.  Likewise if the dropdown selected has a value of 4 then 8 textboxes would be generated with DetailIDs from 13-20. etc.

Should I scrap the DataRepeater and somehow create the the textboxes by looping while dataReader.Read(){created the textboxes using some kind of for each}?

Thanks for your patience.  I see this isn't so straight forward. Points increased.

0
 
LVL 8

Expert Comment

by:shovavnik
ID: 11966529
Well, using the DataRepeater is more elegant, so we'll try using that first before going to the code behind.

If I understand correctly, *after* you've created the textboxes, the user enters data and then clicks on a submit button, which then posts back.  Once it posts back, you need to get the detailid represented by the textbox along with the detailname that the user may have changed.

If I'm still with you, then the solution is very simple.  In fact, you have two options:

1. Use TextChanged post back events.

In the aspx file, you should have:

<asp:TextBox ID="<%#DataBinder.Eval(Container.DataItem,"DetailID")%>" runat="server"  OnTextChanged="DetailTextBox_TextChanged" />

That tells asp.net to "hook up" every textbox to an event by that name.  All the textboxes will use the same event handler.  So if, the user changes 3 different textboxes, the event handler will be called 3 times, once for each textbox whose value was changed.

So in the code behind, you should have something like this:

private void DetailTextBox_TextChanged(object sender, System.EventArgs e)
{
  TextBox thisTextBox = (TextBox) sender; // get a reference to the textbox that threw the event
  int detailId = -1;
  detailId = Convert.ToInt32( thisTextBox.ID );
  detailName = thisTextBox.Text;
  // Now you have both the id and the name of the text that's changed.
  // You should update your datatable with the new value.  For example:
  DataRow row = MyDataTable.Select( "DetailID = " + detailId.ToString() )[ 0 ]; // this assumes there's actually a row like this in your datatable
  row[ "DetailName" ] = detailName;
}

Then, you should ALSO have an event handler for your submit button, in which you update the database based on your datatable.
You only need to update the values that have changed, or you can just upload the whole table to the database.
This event handler will always be fired last, so you can be sure that your 3 values are set before the database is updated.

private void MySubmitButton_Click(object sender, System.EventArgs e)
{
  DataTable modifiedTable = MyDataTable.GetChanges(); // this gets a "sub"-table that contains all the rows that have changed.
  // now update the database.
}

Let me know if that's clear.
0
 
LVL 1

Author Comment

by:champ_010
ID: 11967890
Hey, that sounds good--I'm going to try it.  I don't actually have DetailNames in the textboxes--they are only lables for the empty textboxes but I assume changing the textboxes from blank to whatever the user enters will work the same.  Will let you know as soon as I try it out....
0
 
LVL 1

Author Comment

by:champ_010
ID: 11968307

I don't have a DataTable to update.  Everything that is entered is going to be inserted as new rows right into the database. I got the detailIDs with a DataReader and bound them to the textBoxes.  Now I want to retrieve them and Insert the new rows with ExecuteNonQuery I guess.

The new rows inserted into the database table resembles this:

productID      detailID        detailID
   3                  16               textbox1.Text
   3                  17               textBox2.Text
   3                  18               textBox3.Text

I will have the new productID from an Sql statemet where info from the other formfields are inserted into the Products table first then use that new productID to insert the remaining textboxes--the ones generated in the DataRepeater we are talking about, into the the database with another Sql statement.

If we have the detailID and detailValue with detailId = Convert.ToInt32( thisTextBox.ID );
  detailValue = thisTextBox.Text; I'm not clear how I insert each one into the database as new rows--there will of course be a different number of new rows each time as stated before depending on how many textboxes were created.


0
 
LVL 1

Author Comment

by:champ_010
ID: 11968319
sorry the last column of the database table is detailValue
0
 
LVL 1

Author Comment

by:champ_010
ID: 11968789
A problem here:

<asp:TextBox ID="<%#DataBinder.Eval(Container.DataItem,"DetailID")%>" runat="server"  OnTextChanged="DetailTextBox_TextChanged" />

Doesn't show a textbox where user can type something. it just shows a space where the textbox should appear. If I type: <input type="text" id="<%#DataBinder.Eval(Container.DataItem,"DetailID")%>"> the textbox does show with the correct ID but I can't use OnTextChanged.

I've experimented just typing in a string value into <asp:TextBox ID="testID"> which produces textboxes but an error will show if I typed <asp:TextBox ID="1">.  
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 1

Author Comment

by:champ_010
ID: 11968897

shovavnik,

I've posted my last problem regarding textbox problems as a separate question just to keep things cleaner:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/ASP_DOT_NET/Q_21117539.html

You can answer that one separately and continue with my original question here if you'ld like.

Thanks
0
 
LVL 8

Accepted Solution

by:
shovavnik earned 400 total points
ID: 11973079
Ok.

1. Try setting the id of the textboxes with a letter before the number as I also offered on the other question you posted.  So the id should be "t15" for example.  To retrieve it use:

detailId = Convert.ToInt32( thisTextBox.ID.SubString( 1 ));

Let me know if that works.  It should.

2. You have two options for updating the database.  I'll call them a and b.

a. Add to the database each time the textchanged event handler is hit.  It's hit once for each text box whose text was changed, so this way you're guaranteed to enter new stuff into the database, especially since you produce empty textboxes to begin with.

You basically use the following pseudo-code (which should look *very* similar the the method you used to retrieve your data).  If you use a stored procedure or paramterized query, your TextChanged event will look something like:

..TextChanged() {
  TextBox thisTextBox = (TextBox) sender;
  if( thisTextBox.Text == String.Empty ) return; // you don't want to add empty values.  This should never occur anyway - just here for safety reasons

  int detailId = -1;
  detailId = Convert.ToInt32( thisTextBox.ID.SubString( 1 ));
  detailName = thisTextBox.Text;
  int productId = {you should be able to get your product id, based on what you've said.  maybe it's a property of your page?}

  SqlConnection conn = new SqlConnection( connectionString );
  SqlCommand cmd = new SqlCommand();
  cmd.Connection = conn;
  cmd.CommandText = "spr_AddNewTextValue"; // or whatever your stored procedure is called
  cmd.CommandType = CommandType.StoredProcedure;

  cmd.Parameters.Add( new SqlParameter( "ProductID", productId ));
  cmd.Parameters.Add( new SqlParameter( "DetailID", detailId ));
  cmd.Parameters.Add( new SqlParameter( "DetailName", detailName ));

  try {
    conn.Open();
    cmd.ExecuteNonQuery(); // like you said
  }
  catch( Exception exc ) {
    //handle your exception
  }
  finally {
    conn.Close();  // the connection MUST be closed after the query, even if there was an exception
  }
}


So this option will actually add each textbox's value to the database independantly of the rest.  It's easier to implement, but is a bit slower and opens and closes connections a lot.  A better option, but not necessary, is to use a temporary DataTable just for storage of changed values.

b. Use a datatable.

Add a new member (or property) to your page in the code behind:

DataTable detailTable = null;

In your Page_Load event handler should look something like:

... Page_Load () {
  if( !IsPostBack ) {
    // load all your data using the DataReader exactly the way you're doing it now.
  }
  else {
    detailTable = new DataTable();
    detailTable.Columns.Add( new DataColumn( "ProductID", DbType.Int ));
    detailTable.Columns.Add( new DataColumn( "DetailID", DbType.Int ));
    detailTable.Columns.Add( new DataColumn( "DetailName", DbType.NVarChar )); // not sure about the data type's name - it's somethign like that
  }
}

Then in you textchanged event handler:

..TextChanged() {
  TextBox thisTextBox = (TextBox) sender;
  if( thisTextBox.Text == String.Empty ) return; // you don't want to add empty values.  This should never occur anyway - just here for safety reasons

  int detailId = -1;
  detailId = Convert.ToInt32( thisTextBox.ID.SubString( 1 ));
  detailName = thisTextBox.Text;
  int productId = {you should be able to get your product id, based on what you've said.  maybe it's a property of your page?}

  DataRow row = detailTable.NewRow();
  row[ "ProductID" ] = productId;
  row[ "DetailID" ] = detailId;
  row[ "DetailName" ] = detailName;
  detailTable.Rows.Add( row );
}

And finally in the onclick event of your submit button:

.. SubmitButton_OnClick() {
  if( detailTable != null && detailTable.Rows.Count > 0 ) {
    SqlConnection conn = new SqlConnection( connectionString );
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = conn;
    cmd.CommandText = "spr_AddNewTextValue"; // or whatever your stored procedure is called
    cmd.CommandType = CommandType.StoredProcedure;

    cmd.Parameters.Add( new SqlParameter( "ProductID", productId ));
    cmd.Parameters.Add( new SqlParameter( "DetailID", detailId ));
    cmd.Parameters.Add( new SqlParameter( "DetailName", detailName ));

    int detailId;
    string detailName;
    int productId;

    try {
      conn.Open(); // open the connection once for all the rows

      // add a detail in the db for each row
      foreach( DataRow row in detailTable.Rows ) {
        productId = Convert.ToInt32( row[ "ProductID" ] );
        detailId = Convert.ToInt32( row[ "DetailID" ] );
        detailName = row[ "DetailName" ].ToString();

        cmd.Parameters[ "ProductID" ] = productId;
        cmd.Parameters[ "DetailID" ] = detailId;
        cmd.Parameters[ "DetailName" ] = detailName;

        // add error handling for each row, so that if one row fails, the rest can still be added
        try {
          cmd.ExecuteNonQuery(); // like you said
        }
        catch( Exception exc ) {
          //handle your exception
        }
      }
    }
    catch( Exception exc ) {
      //handle your exception
    }
    finally {
      conn.Close();  // the connection MUST be closed after the query, even if there was an exception
    }
  }
}


0
 
LVL 1

Author Comment

by:champ_010
ID: 11975385
Wow thanks for showing me the two options--especially the one using DataTables. I was beginning to think it's time to start getting more familiar with them.

Your examples are very clear and I'm going to try it out but first, I'm still having problems with the <asp:Textbox>--(thanks for posting at the other question too) I'm not clear what how to apply it to the ID:

SqlDataReader dr3=cmd3.ExecuteReader();
 dr3.Read();
 dID= "t" + dr3[0].ToString();

 myDataRepeater.DataSource=dr3;
 myDataRepeater.DataBind();
 dr3.Close();

...And then in the Repeater ItemTemplate:

<asp:TextBox ID="<%#dID%>" runat="server"/>

This is obviously wrong as I am getting an error.  Am I suppose to create the textbox in the code and then somehow bind it to the repeater?? (I'll just award points at the other question for the answer to it here)

Thanks.  
0
 
LVL 1

Author Comment

by:champ_010
ID: 11975657
Sorry, here's the correct code:

while (dr3.Read())
  {
  dID= "t" + dr3[0].ToString();
  dName=dr3[1].ToString();
  } while (dr3.NextResult());

 myDataReader.DataSource=dr3;
 myDataReader.DataBind();
 dr3.Close();


...so I is it right that I bind the dr3 to the repeater or should I have done something else?
0
 
LVL 1

Author Comment

by:champ_010
ID: 11976138

Now I've simply done added the "t" before the DataBinder.Eval:

<asp:TextBox ID="t<%#DataBinder.Eval(Container.DataItem,"detailID")%>" runat="server"/>

I view source and see id="t1", id="t2" etc.  but the textboxes still don't show! ???
0
 
LVL 1

Author Comment

by:champ_010
ID: 11977813
Hey, I think I got the textbox problem solved.  
Please take a look and let me know if you think there's anything wrong.
The reason why I used a Panel was because the textboxes don't show until the subcategoryID has been chosen from a dropdownlist. Seems to work well here.

I'll resume with the rest of the above code...not sure about the OnTextChanged event--can't seem to add it to my tBox....

---- Solution:-------

 while (dr3.Read())
  {
   Label tLabel=new Label();
   tLabel.Text="<br>"+dr3[1].ToString()+"<br>";
   tLabel.CssClass="h4_666666";
   
   TextBox tBox=new TextBox();
   tBox.ID=dr3[0].ToString();
   tBox.CssClass="textfield_grey";
   tBox.Columns=35;
   
   form1.Controls.Add(tLabel);
   form1.Controls.Add(tBox);
   myPanel.Controls.Add(tLabel);
   myPanel.Controls.Add(tBox);
 }

0
 
LVL 1

Author Comment

by:champ_010
ID: 11982525
shovanik,

I'd like to get to your code example but my dyanmic textboxes are not persisting in viewstate so I'm trying to get that solved first in order to retrieve the values to try out your code.  If you are interested, the posting is here:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/ASP_DOT_NET/Q_21119397.html

Once that's fixed I will get back to here asap!
0
 
LVL 1

Author Comment

by:champ_010
ID: 11986516
shovanik,

Thanks--I learned alot here.  I'm still dealing with viewstate of the dynamic textboxes in other postings but meanwhile am using another version with <input type="hidden"> and in it I am not using OnTextChanged--just on submit of the form I loop each one, open only one connection and ExecuteNonQuery() for each input as you've shown me--without a data table.  You made a comment against opening and closing a connection multiple times but I'm assuming executing multiple queries is o.k. if it's within one open connection.

0
 
LVL 8

Expert Comment

by:shovavnik
ID: 11988443
Hey champ

Sorry it took me so long to get back to you.  It was unavoidable.

Anyway, I'm glad you have it working more or less.

I'll respond on the other posts, time permitting.

As for the connections, asp.net does some smart work in the background to ensure you don't waste resources when using connections.  Opening and closing connections lots of times is generally not a good idea, nor is keeping a connection open for the whole life time of the page.  However, asp.net tries to minimize the impact in both cases on the rest of the system, so it's not a bad temporary solution.  In the long run though, you may want to consider learning more about a multi-tier approach to OOP which solves many of these issues.  In any case, that's out of the scope of this question.

Happy coding!
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

In an ASP.NET application, I faced some technical problems. In this article, I list them out and show the solutions that I found.  I hope it will be useful. Problem: After closing a pop-up window, the parent page should be refreshed automaticall…
IntroductionWhile developing web applications, a single page might contain many regions and each region might contain many number of controls with the capability to perform  postback. Many times you might need to perform some action on an ASP.NET po…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now