0

So I am having an ASP.NET page for material ordering, where the users can order components for the production of a given product. The way it is structured:

  • First DropDownList lists all active production lines, using SqlDataSource
  • User selects production line
  • Second DropDownList lists all products being made on that production line, using SqlDataSource with a ControlParameter that is set to use the production line from the first DropDownList
  • User selects product
  • Third DropDownList lists all components required for that product, using SqlDataSource with a ControlParameter that is set to use the product from the second DropDownList

Below you can see how it looks:

<div class="input-group mb-3">
  <asp:DropDownList ID="ProductionLine" runat="server" CssClass="form-control" AutoPostBack="true" OnSelectedIndexChanged="ProductionLine_SelectedIndexChanged">
    <asp:ListItem Text="Choose a production line..." Value="" Hidden="true" />
    <asp:ListItem Text="Band 45" Value="Band 45" />
    <asp:ListItem Text="Band 46" Value="Band 46" />
    <asp:ListItem Text="Band 47" Value="Band 47" />
    <asp:ListItem Text="Band 48" Value="Band 48" />
  </asp:DropDownList>
  <asp:DropDownList ID="Product" runat="server" CssClass="form-control" Enabled="false" AutoPostBack="true" OnSelectedIndexChanged="Product_SelectedIndexChanged" DataSourceID="Products" DataTextField="Material" DataValueField="Material" AppendDataBoundItems="true">
    <asp:ListItem Text="Choose a product..." Value="" Hidden="true" />
  </asp:DropDownList>
  <asp:DropDownList ID="Component" runat="server" CssClass="form-control" Enabled="false" AutoPostBack="true" OnSelectedIndexChanged="Component_SelectedIndexChanged" DataSourceID="Components" DataTextField="Component" DataValueField="Component" AppendDataBoundItems="true">
    <asp:ListItem Text="Choose a component..." Value="" Hidden="true" />
  </asp:DropDownList>
</div>
<asp:SqlDataSource ID="Products" runat="server" ConnectionString='<%$ ConnectionStrings:conn %>' SelectCommand="SELECT DISTINCT SUBSTRING([Material],1, 4) + '.' + SUBSTRING([Material], 5, 3) + '.' + SUBSTRING([Material], 8, 3) AS 'Material' FROM [RBBE_sl_scan_log] WHERE [ProdLine] = @ProdLine">
  <SelectParameters>
    <asp:ControlParameter Name="ProdLine" ControlID="ProductionLine" PropertyName="SelectedValue" />
  </SelectParameters>
</asp:SqlDataSource>
<asp:SqlDataSource ID="Components" runat="server" ConnectionString='<%$ ConnectionStrings:conn %>' SelectCommand="SELECT DISTINCT [Component] FROM [RBBE_sl_BOM] WHERE [Material] = @Material">
  <SelectParameters>
    <asp:ControlParameter Name="Material" ControlID="Product" PropertyName="SelectedValue" />
  </SelectParameters>
</asp:SqlDataSource>

This is all good so far, but I wanted to add a blank row at the start of each DropDownList in the form of "Select a production line...", "Select a product..." and "Select a component...". As you can see, I have those blank values added manually, then used AppendDataBoundItems="true" on the DropDownLists so this blank row is not removed once they get filled by the SqlDataSource.

The issue is, this way the rows from the SqlDataSource will get appended again every time the selected index of a DropDownList is changed, meaning the DropDownLists will end up with more and more options, instead of old options disappearing and new options appearing. Of course the solution to this would be to remove the AppendDataBoundItems="true" setting from the DropDownLists, but then the manually added blank row also gets removed.

In this case, what would be the best/cleanest method to keep the first blank row, while also making the DropDownLists properly rebind with new values? I know I could just add the the blank row from the code behind after databinding, but I was hoping for a more clean solution. Adding the blank row using UNION in the SQL query is also not an option, as I would like to localize the page with different languages.

1 Answer 1

0

Well, "best" code would be rather subjective.

I suggest building a simple routine that all such code in the project can use.

For this example we have a “common” cascade of 2 combo boxes.

We select a city, and then cascade (filter) the 2nd combo with hotels in that given city.

So, this markup:

        <div style="float:left">
            <h3>Select City</h3>
            <asp:DropDownList ID="cboCity" runat="server" Width="200px"
                AutoPostBack="true"
                OnSelectedIndexChanged="cboCity_SelectedIndexChanged" >
            </asp:DropDownList>
        </div>

        <div style="float:left;margin-left:25px">
            <h3>Select Hotel</h3>
            <asp:DropDownList ID="cboHotels" runat="server" Width="200px" >
            </asp:DropDownList>
        </div>

And code behind on the web page is this:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadCityCbo();
    }

    void LoadCityCbo()
    {
        SqlCommand cmd = 
            new SqlCommand("Select City FROM City ORDER BY City");
        General.Cbo(cboCity, cmd, "", "City", "Select City");
    }

    protected void cboCity_SelectedIndexChanged(object sender, EventArgs e)
    {
        // cascade
        SqlCommand cmd =
            new SqlCommand(@"SELECT ID, HotelName
                           FROM tblhotelsA WHERE City = @City
                           ORDER BY HotelName");

        cmd.Parameters.Add("@City", SqlDbType.NVarChar).Value = cboCity.SelectedItem.Text;
        General.Cbo(cboHotels, cmd, "ID", "HotelName", "Select Hotel");
    }

So above approach allows setting of the data source for the combo box, the “value” and “text” field settings, and the extra prompt row.

And in our “general” catch all class (used by all web pages and code behind) we have these 2 routines.

public static void Cbo(DropDownList ddl,
                    SqlCommand cmd,
                    string sValue, string sText,
                    string sPrompt)
{
    ddl.DataValueField = sValue;
    ddl.DataTextField = sText;
    ddl.DataSource = General.MyRstP(cmd);
    ddl.DataBind();
    ddl.Items.Insert(0, sPrompt);
}


public static DataTable MyRstP(SqlCommand cmdSQL)
{
    using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
    {
        using (cmdSQL)
        {
            cmdSQL.Connection = conn;
            conn.Open();
            rstData.Load(cmdSQL.ExecuteReader());
        }
    }
    return rstData;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.