views:

438

answers:

3

I have a listview that displays a list of textboxes that get created on a button click. I would also like a label to be created next to each txtbox that would increment what is says from step x: to step x+1:

Do I need to create another listview control for this, or is there a much easier way (which I hope)?

Here is the current web code for my listview:

<tr align="center" valign="middle">
    <td>
        <asp:ListView ID="lvDynamicTextboxes" runat="server" ItemPlaceholderID="itemPlaceholder" onitemdatabound="lvDynamicTextboxes_ItemDataBound">
            <LayoutTemplate>
                <asp:PlaceHolder ID="itemPlaceholder" runat="server" />
            </LayoutTemplate>
            <ItemTemplate>
                <asp:Label ID="lblStep" runat="server" Text="Step 1:" />
                <asp:TextBox ID="txtStep" runat="server" TextMode="MultiLine" Rows="3" Width="300" style="margin-top:10px;" />
            </ItemTemplate>
        </asp:ListView>

        <br /><asp:Button ID="btnAddNewStep" runat="server" Text="Add another step" onclick="btnAddNewStep_Click" style="margin-top:5px;" />
    </td>
</tr>

And here is the code-behind

protected void btnAddNewStep_Click( object sender, EventArgs e )
{
    this.UpdateDataSource();
    this.IncrementTextboxCount();
    this.BindListView();
}

private void BindListView()
{
    //create an enumerable range based on the current count
    List< string > dataSource = this.GetDataSource();

    //bind the listview
    this.lvDynamicTextboxes.DataSource = dataSource;
    this.lvDynamicTextboxes.DataBind();
}

private void IncrementTextboxCount()
{
    List< string > dataSource = this.GetDataSource();

    dataSource.Add( string.Empty );
    this.SetDataSource( dataSource );
}

private List< string > GetDataSource()
{
    List< string > dataSource = null;

    if ( ViewState[ "DataSource" ] != null )
        dataSource = ( List< string > )ViewState[ "DataSource" ];
    else
    {
        dataSource = new List< string >();
        dataSource.Add( string.Empty );
        ViewState[ "DataSource" ] = dataSource;
    }

    return dataSource;
}

private void UpdateDataSource()
{
    List< string > dataSource = new List< string >();

    foreach ( ListViewItem item in this.lvDynamicTextboxes.Items )
        if ( item is ListViewDataItem )
        {
            TextBox txt = (TextBox)item.FindControl( "txtStep" );
            dataSource.Add( txt.Text );
        }

    this.SetDataSource( dataSource );
}

protected void lvDynamicTextboxes_ItemDataBound( object sender, ListViewItemEventArgs e )
{
    if ( e.Item is ListViewDataItem )
    {
        TextBox txt = (TextBox)e.Item.FindControl( "txtStep" );
        txt.Text = ( (ListViewDataItem)e.Item ).DataItem.ToString();
    }
}

private void SetDataSource( List< string > dataSource )
{
    ViewState[ "DataSource" ] = dataSource;
}

EDIT::

Since there seems to be a bit of confusion, I'll try to clarify:

As of now, I have a textbox in a listview with a button underneath.

 ________
| txtbox |
|________|
  _____
 |_btn_|

When you click a button, it generates another text box, so clicking it twice results in this:

 ________
| txtbox |
|________|
 ________
| txtbox |
|________|
 ________
| txtbox |
|________|
  _____
 |_btn_|

These textboxes are to create steps in a process, so all I would like to do is add a generated label next to each generated textbox to say which step it is. So I want it to look like this:

               ________
["Step 1"]    | txtbox |
              |________|
               ________
["Step 2"]    | txtbox |
              |________|
               ________
["Step 3"]    | txtbox |
              |________|
                _____
               |_btn_|

And if they click the button again, then another label is generated with the text "Step 4"

+1  A: 

Looks like you've got all the code you need... just missing one thing... add a private class variable that gets set to 0 on Page_Load. Then increment it in your ItemDataBound to get the current step number. Then call FindControl for your label, just like for the text box... and change lblStep.Text to "Step X". Nothin' to it.

Bryan
BTW... these are not really "dynamically created" controls. You should reserve that term for controls which YOU define and instantiate in the code-behind and add to a PlaceHolder or Controls collection. ie, not defined in ASPX or ASCX. Just so everyone's on the same page. :)
Bryan
Oh, I was under the assumption that it meant they are not defined in the aspx page but created during the application runtime.
Justen
I think you might've misunderstood. I want to create a label next each textbox, not just have one and push it down.
Justen
You're right, I don't get it... you want to create ANOTHER label control aside from "lblStep"? One that contains the exact text you've already assigned to lblStep? You can see why I'm confused.
Bryan
On the page load, there is one label, one txtbox, and one button. "'Step 1' --- txtStep1 --- btnAddNewStep" When the user clicks the button, another row of this is created (except for another button), so now visible would be 'Step 1' txtStep1, and below it it would be 'Step 2' txtStep2 --- btnAddNewStep
Justen
clarified in the OP
Justen
A: 

I ended up making duplicate functions for another listview that contained the labels and then putting them in a table. It's not pretty, but it works. I've also added a remove link next the newest textbox. Just a heads up for anyone looking through the code.

<table>
    <tr>
        <td valign="top">
            <asp:ListView ID="lvDynamicLabels" runat="server" ItemPlaceholderID="itemPlaceholder2" onitemdatabound="lvDynamicLabels_ItemDataBound">
                <LayoutTemplate>
                    <asp:PlaceHolder ID="itemPlaceholder2" runat="server" />
                </LayoutTemplate>
                <ItemTemplate>
                    <asp:Label ID="lblStep" runat="server" Width="100px" style="margin-top:30px; margin-bottom:16px;" />
                </ItemTemplate>
            </asp:ListView>
        </td>
        <td>
            <asp:ListView ID="lvDynamicTextboxes" runat="server" ItemPlaceholderID="itemPlaceholder" onitemdatabound="lvDynamicTextboxes_ItemDataBound">
                <LayoutTemplate>
                    <asp:PlaceHolder ID="itemPlaceholder" runat="server" />
                </LayoutTemplate>
                <ItemTemplate>
                    <asp:TextBox ID="txtStep" runat="server" TextMode="MultiLine" Rows="3" Width="275px" style="margin-top:10px;" />
                </ItemTemplate>
            </asp:ListView>

            <asp:LinkButton ID="lnkRemove" runat="server" Text="Remove" Visible="false" OnClick="lnkRemove_Click" style="font-size:small; position:absolute; margin-top:30px;" />

            <br /><asp:Button ID="btnAddNewStep" runat="server" Text="Add another step" onclick="btnAddNewStep_Click" style="margin-top:5px;" />
        </td>
    </tr>
</table>

And the code behind:

    protected void lnkRemove_Click( object sender, EventArgs e  )
    {
        UpdateDataSource( true );
    UpdateLabelDataSource( true );

    BindListView();
    BindLabelListView();

    if ( lvDynamicTextboxes.Items.Count == 1 ) 
        lnkRemove.Visible = false;
}

private void BindListView()
{
    List< string > dataSource = this.GetDataSource();

    this.lvDynamicTextboxes.DataSource = dataSource;
    this.lvDynamicTextboxes.DataBind();
}

private void IncrementTextboxCount()
{
    List< string > dataSource = this.GetDataSource();

    dataSource.Add( string.Empty );
    this.SetDataSource( dataSource );
}

private List< string > GetDataSource()
{
    List< string > dataSource = null;

    if ( ViewState[ "DataSource" ] != null )
        dataSource = ( List< string > )ViewState[ "DataSource" ];
    else
    {
        dataSource = new List< string >();
        dataSource.Add( string.Empty );
        ViewState[ "DataSource" ] = dataSource;
    }

    return dataSource;
}

private void UpdateDataSource( bool delete )
{
    List< string > dataSource = new List< string >();

    foreach ( ListViewItem item in this.lvDynamicTextboxes.Items )
        if ( item is ListViewDataItem )
        {
            TextBox txt = (TextBox)item.FindControl( "txtStep" );
            dataSource.Add( txt.Text );
        }

    if ( delete )
        dataSource.RemoveRange( dataSource.Count-1, 1 );

    this.SetDataSource( dataSource );
}

protected void lvDynamicTextboxes_ItemDataBound( object sender, ListViewItemEventArgs e )
{
    if ( e.Item is ListViewDataItem )
    {
        TextBox txt = (TextBox)e.Item.FindControl( "txtStep" );
        txt.Text = ( (ListViewDataItem)e.Item ).DataItem.ToString();
    }
}

private void SetDataSource( List< string > dataSource )
{
    ViewState[ "DataSource" ] = dataSource;
}

private void BindLabelListView()
{
    List< string > lblDataSource = this.GetLabelDataSource();

    //bind the listview
    this.lvDynamicLabels.DataSource = lblDataSource;
    this.lvDynamicLabels.DataBind();
}

private void IncrementLabelCount()
{
    List< string > lblDataSource = this.GetLabelDataSource();

    lblDataSource.Add( "Step " + ( lblDataSource.Count + 1 ) );

    this.SetLabelDataSource( lblDataSource );
}

private List< string > GetLabelDataSource()
{
    List< string > lblDataSource = null;

    if ( ViewState[ "lblDataSource" ] != null )
        lblDataSource = ( List< string > )ViewState[ "lblDataSource" ];
    else
    {
        lblDataSource = new List< string >();
        lblDataSource.Add( "Step 1" );
        ViewState[ "lblDataSource" ] = lblDataSource;
    }

    return lblDataSource;
}

private void UpdateLabelDataSource( bool delete )
{
    List< string > lblDataSource = new List< string >();
    int count = 1;

    foreach ( ListViewItem item in this.lvDynamicLabels.Items )
        if ( item is ListViewDataItem )
        {
            Label lbl = (Label)item.FindControl( "lblStep" );
            lbl.Text = "Step " + count;
            lblDataSource.Add( lbl.Text );
            count++;
        }

    if ( delete )
        lblDataSource.RemoveRange( lblDataSource.Count-1, 1 );

    this.SetLabelDataSource( lblDataSource );
}

protected void lvDynamicLabels_ItemDataBound( object sender, ListViewItemEventArgs e )
{
    if ( e.Item is ListViewDataItem )
    {
        Label lbl = (Label)e.Item.FindControl( "lblStep" );
        lbl.Text = ( (ListViewDataItem)e.Item ).DataItem.ToString();
    }
}

private void SetLabelDataSource( List< string > lblDataSource )
{
    ViewState[ "lblDataSource" ] = lblDataSource;
}
Justen
This is bizarre. What exactly does using two ListViews get you that you couldn't accomplish with just one? You're using the same datasource and binding them at the same time. You don't see a problem with that?
Bryan
Well I did say that it wasn't pretty. I know this is not even close to elegant, but I don't understand how to control listviews enough to make it better. I've since done a few more attempts with one listview, but I keep hitting a wall. Either the labels don't show up at all, or they all have the same text.
Justen
A: 

I don't understand why you ended up seperating the label from the textbox. Why not leave the template as you had it originally, and have this method in your code behind:

protected void lvDynamicLabels_ItemDataBound( object sender, ListViewItemEventArgs e ) 
{ 
    if ( e.Item is ListViewDataItem ) 
    { 
        Label lbl = (Label)e.Item.FindControl( "lblStep" ); 
        lbl.Text = "Step " + (datasource.Count + 1).ToString();
        TextBox txt = (TextBox)e.Item.FindControl( "txtStep" ); 
        txt.Text = ( (ListViewDataItem)e.Item ).DataItem.ToString(); 
    } 
} 
Smelch
dataSource isn't defined, and if I add my method that gets the data source, the labels increment like they should after every button click, but every label has the same text.
Justen