Home > OS >  foreach by check on two control (dropdownlist and textbox)
foreach by check on two control (dropdownlist and textbox)

Time:08-09

I have issue on doing the foreach part, I have multiple dropdownlist attach together with a textbox for filling quantity But with my current code it will have issue with running all value in the foreach textbox first just until the next foreach of dropdownlist. Below is my code of now.

foreach (DropDownList dropDown in pnlDDLDispensary.Controls.OfType<DropDownList>()){foreach (TextBox textBox in pnltxtDispensaryQty.Controls.OfType<TextBox>()){ code here }}

My objective is want to insert each of the dropdownlist dispensary together with the quantity of that dispensary like example: (MedicineA with 5 quantity, MedicineB with 1 quantity). Is there anything like foreach( a in control && b in control)? Appreciate for help. Thanks~

CodePudding user response:

protected void cmdDelete_ServerClick(object sender, EventArgs e)
{
        GridToTable(); //This function havent done due to also having DataKeys cannot used like a method error
        Button btnDelete = (Button)sender;
        ListViewItem gRow = (ListViewItem)btnDelete.NamingContainer;
        int disID = lvDispensary.DataKeys(gRow.DataItemIndex).Item("dispensaryID");
}```

CS1955: Non-invocable member 'ListView.DataKeys' cannot be used like a method. 

CodePudding user response:

It not at all clear what kind of "system" or choice you have made to show the repeating drop down and text box.

As a general rule, if you need to "repeat" controls, then you use a listview, repeater, or even maybe a grid view.

so it will not matter if you have 1 or 15 row of repeating data (you don't care how many).

So, I might say have 1 or 8 hotels, and I have a combo box to rate the hotel.

So, hum, lets use the repeater.

So, we only need to drop/have one instance of the markup layout.

Say, like this:

<asp:Repeater ID="Repeater1" runat="server" OnItemDataBound="Repeater1_ItemDataBound">
    <ItemTemplate>
        <asp:Label ID="lblH" runat="server" Text="Hotel Name" Font-Size="Medium">
        </asp:Label>

        <asp:TextBox ID="txtHotel" runat="server" 
            Text = '<%# Eval("HotelName") %>'
            style="margin-left:20px" >
        </asp:TextBox>

        <asp:DropDownList ID="DropDownList1" runat="server"
            style="margin-left:20px;width:120px"
            DataValueField = "ID"
            DataTextField = "Rating" >
        </asp:DropDownList>

        <br />

    </ItemTemplate>
</asp:Repeater>

and our code behind is this:

    DataTable rstRating = new DataTable();
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadData();

    }

    void LoadData()
    {
        // set combo box source
        rstRating = 
            MyRst("SELECT ID, Rating FROM tblRating ORDER BY ID");

        DataTable rstData =
            MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
        Repeater1.DataSource = rstData;
        Repeater1.DataBind();
    }


    protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        if (e.Item.ItemType == ListItemType.Item | 
            e.Item.ItemType == ListItemType.AlternatingItem)
        {
            DropDownList dl = (DropDownList)e.Item.FindControl("DropDownList1");
            dl.DataSource = rstRating;
            dl.DataBind();
            dl.Items.Insert(0, new ListItem("Please Select", "0"));
        }
    }

and now we get this:

enter image description here

so, above works for 2 or 20 rows of data. We don't care how many rows.

but, now to do a for each on the repeating controls?

This:

    protected void Button1_Click(object sender, EventArgs e)
    {
        foreach (RepeaterItem OneRow in Repeater1.Items)
        {
            TextBox txtHotel = (TextBox)OneRow.FindControl("txtHotel");
            DropDownList cboRate = (DropDownList)OneRow.FindControl("DropDownList1");

            Debug.Print("Hotel name = "   txtHotel.Text   " -- Rating = "  
                cboRate.SelectedItem.Text);
        }
    }

And output is this:

enter image description here

So, for a "set" or "thing" of repeating data, then use some type of control such as a repeater.

I mean, for above, we could also use a listview, and that also is quite nice, say like this:

<div style="width:24%">
<asp:ListView ID="LVHotels" runat="server" DataKeyNames="ID" OnItemDataBound="LVHotels_ItemDataBound"  >
    <ItemTemplate>
        <tr style="">
            <td>
                <asp:TextBox ID="txtHotel" runat="server" 
                    Text = '<%# Eval("HotelName") %>'
                    style="margin-left:20px" >
                </asp:TextBox>
            </td>
            <td>
                <asp:DropDownList ID="DropDownList1" runat="server"
                    style="margin-left:20px;width:120px"
                    DataValueField = "ID"
                    DataTextField = "Rating" >
                </asp:DropDownList>
            </td>
        </tr>
    </ItemTemplate>
    <LayoutTemplate>
       <table id="itemPlaceholderContainer" runat="server" >
            <tr runat="server" style="">
                <th runat="server">Hotel Name</th>
                <th runat="server">Rating</th>
            </tr>
            <tr id="itemPlaceholder" runat="server">
            </tr>
       </table>
    </LayoutTemplate>
</asp:ListView>

</div>

Same code to bind, and we now have this:

enter image description here

And then once again, our for each code against that listview is much the same.

So, the idea here?

If you have repeating controls, then use some type of gridview, repeater, or listview.

that way, its all data driven, and once done, then 2 or 20 rows - it don't matter, and you can use for each against these containers that repeated the data for you.

Edit: User wants a List View example.

Ok, so markup for this is this:

<div style="width:30%">
   <style> 
       .borderhide input {border:none;background-color:transparent}
       .borderhide textarea {border:none;background-color:transparent}
   </style>

  <asp:ListView ID="LVHotels" runat="server" DataKeyNames="ID" OnItemDataBound="LVHotels_ItemDataBound"  >
    <ItemTemplate>
        <tr style="">
            <td><asp:TextBox ID="txtFirst" runat="server"  Text='<%# Eval("FirstName") %>' width="80px"></asp:TextBox></td>
            <td><asp:TextBox ID="txtLast"  runat="server"  Text='<%# Eval("LastName") %>' width="80px"></asp:TextBox></td>
             <td><asp:TextBox ID="txtHotel" runat="server" Text='<%# Eval("HotelName") %>' width="110px"></asp:TextBox></td>
            <td>
                <asp:DropDownList ID="DropDownList1" runat="server"
                    style="width:120px"
                    DataValueField = "ID"
                    DataTextField = "Rating" >
                </asp:DropDownList>
            </td>
            <td>
                <button id="cmdDelete" runat="server" 
                    onserverclick="cmdDelete_ServerClick"
                    onclick="if (!confirm('Really delete')) {return false}" >
                    <span aria-hidden="true" ></span>
                </button>
            </td>
        </tr>
    </ItemTemplate>
    <LayoutTemplate>
       <table id="itemPlaceholderContainer" runat="server" >
            <tr runat="server" style="">
                <th runat="server">First Name</th>
                <th runat="server">Last Name</th>
                <th runat="server">Hotel Name</th>
                <th runat="server">Rating</th>
                <th runat="server"></th>
            </tr>
            <tr id="itemPlaceholder" runat="server">
            </tr>
           <tfoot><tr><td>
                <button id="cmdAddNew" runat="server"  onserverclick="cmdAddNew_ServerClick" >
                    <span aria-hidden="true" > New</span> 
                 </button>
            </td></tr></tfoot>
       </table>
    </LayoutTemplate>
</asp:ListView>
</div>


<br />

<button id="cmdSave" runat="server"  onserverclick="cmdSave_ServerClick" >
    <span aria-hidden="true" > Save</span> 
 </button>

<button id="cmdCancel" runat="server"  style="margin-left:15px" onserverclick="cmdCancel_ServerClick">
    <span aria-hidden="true" > Back/Cancel</span>
</button>


</div>

And our full code for this is this:

    DataTable rstRating = new DataTable();
    DataTable rstData = new DataTable();
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            LoadData();
            Session["rstData"] = rstData;
            Session["rstRating"] = rstRating;
        }
        else
        {
            rstData = (DataTable)Session["rstData"];
            rstRating = (DataTable)Session["rstRating"];
        }
    }

    void LoadData()
    {
        // set combo box source
        rstRating = MyRst("SELECT ID, Rating FROM tblRating ORDER BY ID");
        rstData = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
        LVHotels.DataSource = rstData;
        LVHotels.DataBind();
    }

    DataTable MyRst(string strSQL)
    {
        DataTable rstData = new DataTable();
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
            {
                cmdSQL.Connection.Open();
                rstData.Load(cmdSQL.ExecuteReader());
            }
        }
        return rstData;
    }


    protected void LVHotels_ItemDataBound(object sender, ListViewItemEventArgs e)
    {
        if (e.Item.ItemType == ListViewItemType.DataItem)
        {
            DropDownList dl = (DropDownList)e.Item.FindControl("DropDownList1");
            dl.DataSource = rstRating;
            dl.DataBind();
            dl.Items.Insert(0, new ListItem("Please Select", "0"));
            // now set to current data row
            DataRowView OneRow = (DataRowView)e.Item.DataItem;
            if (OneRow["Rating"] != null)
                dl.SelectedValue = OneRow["Rating"].ToString();
        }
    }

    protected void cmdSave_ServerClick(object sender, EventArgs e)
    {
        SaveAllData();
        // data saved - move on to next page
        // Response.Redirect("MyMain.aspx");
    }
    protected void cmdAddNew_ServerClick(object sender, EventArgs e)
    {
        DataRow NewRow = rstData.NewRow();

        rstData.Rows.Add(NewRow);
        // show this row
        LVHotels.DataSource = rstData;
        LVHotels.DataBind();
    }

    void SaveAllData()
    {
        GridToTable(); // move grid back to table
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            using (SqlCommand cmdSQL =
                new SqlCommand("SELECT * FROM tblHotelsA WHERE ID = 0", conn))
            {
                conn.Open();
                SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
                SqlCommandBuilder daU = new SqlCommandBuilder(da);
                da.Update(rstData);
                // rstData.AcceptChanges()
            }
        }
    }

    protected void cmdCancel_ServerClick(object sender, EventArgs e)
    {
        // cancel without save - go back some place to other page
        // Response.Redirect("MyMain.aspx");
    }

    void GridToTable()
    {
        // send LV rows back to our table.
        foreach (ListViewItem rRow in LVHotels.Items)
        {
            int RecordPtr = rRow.DataItemIndex;
            DataRow OneDataRow = rstData.Rows[RecordPtr];

            OneDataRow["FirstName"] = ((TextBox)rRow.FindControl("txtFirst")).Text;
            OneDataRow["LastName"] = ((TextBox)rRow.FindControl("txtLast")).Text;
            OneDataRow["HotelName"] = ((TextBox)rRow.FindControl("txtHotel")).Text;
            DropDownList dlRating = (DropDownList)rRow.FindControl("DropDownList1");
            if (dlRating.SelectedIndex == 0)
                OneDataRow["Rating"] = null;
            else
                OneDataRow["Rating"] = dlRating.SelectedItem.Value;
        }
    }

    protected void cmdDelete_ServerClick(object sender, EventArgs e)
    {
        // we actully should saveAllData before we do this
        // we might have added rows without save and thus 
        // pk not correctly valid
        // so we need to actaully check deleted row status here.

        HtmlButton btn = (HtmlButton)sender;
        ListViewItem gRow = (ListViewItem)btn.NamingContainer;

        int RowIndex = gRow.DataItemIndex;
        int PK = (int)LVHotels.DataKeys[RowIndex]["ID"];

        string strSQL = "DELETE FROM tblHotelsA WHERE ID = "   PK;

        Debug.Print("will delete - future code");

    }

And we now have get this:

enter image description here

So, you can:

Tab around - edit change any row - including combo box

Add new rows

THEN hit save button, all edits, adding, edits and even delete(s) will now go back to sql server table.

The data bound event for the listview in above is what you are looking for.

Only part left out is delete button - but that should be easy.

Edit3: adding the delete button code

As noted, if we are to allow delete, then we STILL have to hit the save button, and the un-do changes button WILL still work!!! So, the user can add a few rows, delete a few rows - and then go "opps" wrong rows deleted. Since we not yet hit save, then we not actaully deleted the data. (and this means our un-do button can still work).

But, what this means? well in rstData, then rows of DELETED data WILL exist. We can feed rstData to the LV, and such rows don't show.

But, using for/each on the rstData? Then YES DELETED rows will show up!

And if the LV displays 5 rows, but we had 10 and deleted 5 of them?

Then as noted, we NOW cannot USE the LV "index" to peek into rstData anymore. But, we can still use Datakeys. BEYOND critial then that you set datakeys setting in the LV markup as I have done above.

So, first up, the delete button code. User might bounce around, edit some rows, maybe add. But then hits delete button.

So, this code is easy. (also note the JavaScript confirm prompt to confirm the delete button).

hence this:

Protected Sub cmdDelete_ServerClick(sender As Object, e As EventArgs)

    ' save any edits BACK to table before delete
    ' this ONLY delets from persited table - not SQL server.

    GridToTable()
    Dim btn As HtmlButton = sender
    Dim gRow As ListViewItem = btn.NamingContainer
    Dim PK As Integer = ListView1.DataKeys(gRow.DataItemIndex).Item("ID")

    Dim OneDataRow As DataRow = rstData.Select("id = " & PK)(0)
    OneDataRow.Delete()
    DisplayUpdate()

End Sub

Note VERY close in above. We did not (and can't) use the LV index into our table, since rstData now can have more rows then the LV display. (LV is smart, only display rows that are not deleted).

So, in above, we dumped "index" from LV into rstData, and we now use PK value.

This also now means our GridToTable can't use the simple "index" into rstdata, and we MUST pull each row from rstData by PK value (we have to search/find the PK row based on PK).

So, GridToTable now has to be this:

Sub GridToTable()

    ' send grid rows back to persited table
    For Each gRow As ListViewItem In ListView1.Items
        ' Get database PK value
        Dim PK As Integer = ListView1.DataKeys(gRow.DataItemIndex).Item("ID")

        Dim drT() As DataRow = rstData.Select("ID = " & PK, "", DataViewRowState.CurrentRows)
        If drT.Length > 0 Then
            Dim OneDataRow As DataRow
            OneDataRow = drT(0)
            OneDataRow.Item("FirstName") = CType(gRow.FindControl("txtFirst"), TextBox).Text
            OneDataRow.Item("LastName") = CType(gRow.FindControl("txtLast"), TextBox).Text
            OneDataRow.Item("City") = CType(gRow.FindControl("txtCity"), TextBox).Text
            OneDataRow.Item("Description") = CType(gRow.FindControl("txtDescription"), TextBox).Text
            OneDataRow.Item("Nights") = CType(gRow.FindControl("txtNights"), TextBox).Text
            ' combo box
            Dim cboRank As DropDownList = gRow.FindControl("cboRank")
            If cboRank.Text = "Select" Then
                OneDataRow("Rating") = DBNull.Value
            Else
                OneDataRow("Rating") = cboRank.SelectedItem.Value
            End If
        End If
    Next

End Sub

Note how we dumped "index" and now do search by PK. (note that datakeys by index WILL be ok, since LV only displays rows not deleted.

Edit4: Delete code - full code listing

As noted, I did post some vb code by mistake.

So, here is the full code listing:

    DataTable rstRating = new DataTable();
    DataTable rstData = new DataTable();
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            LoadData();
            Session["rstData"] = rstData;
            Session["rstRating"] = rstRating;
        }
        else
        {
            rstData = (DataTable)Session["rstData"];
            rstRating = (DataTable)Session["rstRating"];
        }
    }

    void LoadData()
    {
        // set combo box source
        rstRating = MyRst("SELECT ID, Rating FROM tblRating ORDER BY ID");
        rstData = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
        LVHotels.DataSource = rstData;
        LVHotels.DataBind();
    }

    DataTable MyRst(string strSQL)
    {
        DataTable rstData = new DataTable();
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
            {
                cmdSQL.Connection.Open();
                rstData.Load(cmdSQL.ExecuteReader());
            }
        }
        return rstData;
    }


    protected void LVHotels_ItemDataBound(object sender, ListViewItemEventArgs e)
    {
        if (e.Item.ItemType == ListViewItemType.DataItem)
        {
            DropDownList dl = (DropDownList)e.Item.FindControl("DropDownList1");
            dl.DataSource = rstRating;
            dl.DataBind();
            dl.Items.Insert(0, new ListItem("Please Select", "0"));
            // now set to current data row
            DataRowView OneRow = (DataRowView)e.Item.DataItem;
            if (OneRow["Rating"] != null)
                dl.SelectedValue = OneRow["Rating"].ToString();
        }
    }

    protected void cmdSave_ServerClick(object sender, EventArgs e)
    {
        SaveAllData();
        // data saved - move on to next page
        // Response.Redirect("MyMain.aspx");
    }
    protected void cmdAddNew_ServerClick(object sender, EventArgs e)
    {
        DataRow NewRow = rstData.NewRow();

        rstData.Rows.Add(NewRow);
        // show this row
        LVHotels.DataSource = rstData;
        LVHotels.DataBind();
    }

    void SaveAllData()
    {
        GridToTable(); // move grid back to table
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            using (SqlCommand cmdSQL =
                new SqlCommand("SELECT * FROM tblHotelsA WHERE ID = 0", conn))
            {
                conn.Open();
                SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
                SqlCommandBuilder daU = new SqlCommandBuilder(da);
                da.Update(rstData);
                // rstData.AcceptChanges()
            }
        }
    }

    protected void cmdCancel_ServerClick(object sender, EventArgs e)
    {
        // cancel without save - go back some place to other page
        // Response.Redirect("MyMain.aspx");
    }

    void GridToTable()
    {
        // send LV rows back to our table.
        foreach (ListViewItem rRow in LVHotels.Items)
        {
            int PK = (int)LVHotels.DataKeys[rRow.DisplayIndex]["ID"];
            // get row by PK
            DataRow[] drT = rstData.Select("ID = "   PK,"", DataViewRowState.CurrentRows);

            if (drT.Length > 0)
            {
                DataRow OneDataRow = drT[0];
                OneDataRow["FirstName"] = ((TextBox)rRow.FindControl("txtFirst")).Text;
                OneDataRow["LastName"] = ((TextBox)rRow.FindControl("txtLast")).Text;
                OneDataRow["HotelName"] = ((TextBox)rRow.FindControl("txtHotel")).Text;
                DropDownList dlRating = (DropDownList)rRow.FindControl("DropDownList1");
                if (dlRating.SelectedIndex == 0)
                    OneDataRow["Rating"] = DBNull.Value;
                else
                    OneDataRow["Rating"] = dlRating.SelectedItem.Value;
            }
        }
    }

    protected void cmdDelete_ServerClick(object sender, EventArgs e)
    {
        GridToTable(); 
        HtmlButton btn = (HtmlButton)sender;
        ListViewItem gRow = (ListViewItem)btn.NamingContainer;

        int PK = (int)LVHotels.DataKeys[gRow.DataItemIndex]["ID"];
        // get row by PK
        DataRow[] drT = rstData.Select("ID = "   PK, "", DataViewRowState.CurrentRows);
        drT[0].Delete();

        LVHotels.DataSource = rstData;
        LVHotels.DataBind();

    }


    protected void cmdUndo_ServerClick(object sender, EventArgs e)
    {
        LoadData();
        Session["rstData"] = rstData;
    }

And the results look like this:

enter image description here

Markup for the buttons was:

<br />

<button id="cmdSave" runat="server"  onserverclick="cmdSave_ServerClick" >
    <span aria-hidden="true" > Save</span> 
 </button>

<button id="cmdCancel" runat="server"  style="margin-left:15px" onserverclick="cmdCancel_ServerClick" >
    <span aria-hidden="true" > Back/Cancel</span>
</button>

<button id="cmdUndo" runat="server"  style="margin-left:15px"
    onserverclick="cmdUndo_ServerClick" >
    <span aria-hidden="true" > Undo&ensp;Edits</span>
</button>

And I was going to add a bootstrap dialog for confirms - the built in browser ones look beyond ugly. But above should clear up the syntax for htis to work.

  • Related