Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 526
  • Last Modified:

Dynamically TextBox ID from DataBase - Generating and Retrieving

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
champ_010
Asked:
champ_010
  • 13
  • 5
1 Solution
 
shovavnikCommented:
Does your textbox throw an onchange event?

Are you trying to get the id on the server or on the client?
0
 
champ_010Author Commented:
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
 
shovavnikCommented:
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
champ_010Author Commented:

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
 
shovavnikCommented:
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
 
champ_010Author Commented:
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
 
champ_010Author Commented:

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
 
champ_010Author Commented:
sorry the last column of the database table is detailValue
0
 
champ_010Author Commented:
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
 
champ_010Author Commented:

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
 
shovavnikCommented:
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
 
champ_010Author Commented:
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
 
champ_010Author Commented:
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
 
champ_010Author Commented:

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
 
champ_010Author Commented:
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
 
champ_010Author Commented:
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
 
champ_010Author Commented:
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
 
shovavnikCommented:
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

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 13
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now