开发者

Better way to handle multiple DropDown selection

Better way to handle multiple DropDown selection

I have a menu with multiple DropDowns. I added code but at present, it entirely resides in the code-behind file. I want to use any design pattern to handle in an easy and uncluttered way the variety of choices.

The report generation criteria is prepared as follows:

Report Type DropDown options include:

  1. Scheme Type-wise
  2. Scheme-wise
  3. District-wise
  4. Block-wise
  5. All

By default, only the first DropDown is enabled. Selecting an option from this DropDown, enables the respective DropDowns.

Not only this, values of Scheme, District and Block DropDowns also change when an item is selected from any of these DropDowns or Scheme Type DropDown using AJAX.

It involves a variety of SQL queries and Enabling/Disabling DropDowns often. My present code has become cluttered with many IF and EndIfs.

I want to know whether Observer pattern or any approach using Classes be used to simplify this operation. Any way to make this multiple selections and filling of DropDowns manageable and simple?

Edited below to clear requirements

Let me clarify further.

The first DropDown is the key DropDown which is enabled by default when the page opens. All the other DropDowns are disabled by default. But this does not mean that Cascading DropDown is the correct choice because, the selection from the child DropDowns is random.

The whole plan is to simplify the code in an u开发者_如何学Gonderstandable form for each DropDown. There are many Ifs and ElseIfs involved to pick the correct query depending upon the selection.

For example: The user selects, District-wise report from the Report Type primary DropDown. In this case, three child DropDowns are enabled, viz.

Scheme Type

Scheme

District

If user selects "ALL" from Scheme Types List, all types of schemes in all categories gets filled in the Scheme DropDown.

If user selects a particular Scheme Type from the options: Urban, Rural or Other, the Scheme DropDown filters the name of the schemes.

Now, Scheme DropDown also has an option ALL. The user can select ALL or pick any particular scheme.

Same is with District. If ALL is selected, schemes in the Scheme DropDown takes all schemes in all districts, but if a particular district is selected, the Scheme DropDown must fill the filtered schemes of this district.

Please note that in this case, we are now moving in a reverse order because District DropDown is again filtering the Scheme DropDown.

The same is applicable with the Block DropDown.

There are variety of conditions to be checked other the selected option. Suppose the user didn't select any option or the user selects ALL.

I want to create separate classes with the names of each DropDown. These classes should keep hearing notifications (Observer) for any changes in the DropDown.

I guess I was able to clarify.


Using AJAX Control Toolkit is the solution that matches your requirements.

In AJAX Control Toolkit, there is CascadingDropDown Control

Tag Syntax:

<ajaxToolkit:CascadingDropDown ID="ddlReportType" runat="server"
    TargetControlID="ddlSchemeType"
    Category="SchemeType"
    PromptText="Please select a ReportType"
    LoadingText="[Loading Report Types...]"
    ServicePath="ReportService.asmx"
    ServiceMethod="GetDropDownReportTypeContents"
    ParentControlID="DropDownList1"
    SelectedValue="SomeValue" />

And then you need to create an Web Service and few Web Method(s) for it having following method signature,

[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public CascadingDropDownNameValue[] GetDropDownReportTypeContents(
       string knownTypeValues, string typevalue) { ... }

UPDATE

You have do something like this using if-Else-If, Answer is given on assumptions and purely an example to the implementations.

    string query = "SELECT * FROM Reports";
    List<string> filters = new List<string>();
    bool ReportType = true;
    bool SchemeType = true;
    bool Scheme = true;
    bool District = true;
    bool Block = true;

    if (ReportType)
        filters.Add("ReportType = true");
    if (SchemeType)
        filters.Add("SchemeType = true");
    if (Scheme)
        filters.Add("Scheme = true");
    if (District)
        filters.Add("District = true");
    if (Block)
        filters.Add("Block = true");

    if (filters.Count() > 0)
    {
        query = query + " WHERE " + string.Join(" AND ", filters.ToArray());
    }

I hope that my answer helps you

Thanks and Regards

Harsh Baid


Since it seems you can do all of this client side using Javascript why are you querying your database so much? You should persist the data client side and server side. You could call all related drop-down data, cache it and from the client side call a WebMethod that would return a JSON with the data you need. For server side object persistence you should check out the Entity Framework 4.1 where each object is a representation of a table.

I would use jQuery with Knockout JS or just jQuery for it, persisting the data on the client side using JSON, there is no need to go back and forward to the sql server.

Here's a nice example of Knockout JS in action.
http://knockoutjs.com/examples/cartEditor.html

Of course unless that data changes extremely frequently.


Not exactly what you are looking for, but you may want to check out how I resolved similar issue in mvc with jQuery. Same can be achieved in webforms using web methods instead of mvc actions.

It is a story in 3 parts, and code is available on bitbucket. You may start form part 2 as first is creation of demo app and setup of database.

http://blog.goranobradovic.com/2011/06/asp-net-mvc3-app-part-2-ajax-cascading-dropdown/

There is working example for 2 dropdowns, but I used same solution for 3 and 4 without problems.

UPDATE As you have source available, I will put only relevant lines to change here. The adjustment to prevent multiple ajax calls would be putting values of options in first select to id's (or id's to values it does not matter), or you could use some attribute in dependent dropdown if you cannot change ids and values, but I will keep it simple for example. Then, in change event of master dropdown you check if target needs to be loaded:

$('#' + target.attr('cascading-dependson')).change(function () {
        if($(this).find("option:selected").val() == $(target).attr("id")){ // this is added if
            $(target).removeAttr("disabled");  // added
            selectFromAjax($(target).attr('cascading-loadfrom'),
            { id: $(this).find("option:selected").val() },
            target);
        }
        else {  //added
            $(target).attr("disabled", "disabled");
        }
    });

You can easily change this if to use some custom attribute of select if you don't want use id.

UPDATE 2 I just saw that you updated question. As I see, there is a case where you need to refresh all dependent dropdowns. For this you need to have id-s of all dropdowns which need to be refreshed in last master select option's value, separeted i.e. with ",", so you can check if any of its val().split(",") is equal to id of dependent dropdown, or you can have attribute in dependend which contain values of all options for which it needs to refresh. You see in my code that options for selectFromAjax are selected value in dropdown and name of master field. As you probably need to have some your value in here to be handled by server code, I suggest that you use second way, that is - add custom attribute to dependend dropdown, in which you will put values of select options from master which are relevant for that field and dependent should load when selected.

Do you understand what I suggest?


If I'm understanding your paradigm correctly, really, you have one drop-down (Report Type) that determines whether to select by Scheme Type, Scheme, District, or Block (or all of the above). If you did not have the "All" option, I would suggest either having only two drop-downs (one for Report Type and one whose label changes to match) or eliminating the Report Type drop-down and putting a radio button next to each of the others to select which you want. When including the "All" option, you might not to complicate things much more; for instance, you could add one more radio button and enable all four drop-down lists.

However, you ask about the Observer Pattern. From GoF, the Observer Pattern is useful when:

  1. An abstraction has two aspects, one dependent on the other;
  2. A change to one object requires changing others, and you don't know how many objects need to be changed; or
  3. An object should be able to notify other objects without making assumptions about who these objects are.

I'm not entirely sure any of these situations applies here. The second situation has some similarities to your problem, but you do know what needs to be changed and how. If the only updating you are doing is by Report Type, it's just a matter of enabling or disabling the right drop-down menu. However, you say that the other drop-downs affect each other, probably in the "All" option. Even in this case, I'm not sure the Observer Pattern per se is the most helpful. Since you are populating the drop-downs using SQL, I'm guessing you might be using a number of different stored procedures (or ad-hoc queries) depending which parameters you need. What I might suggest instead is to have only one query per drop-down, making judicious use of NULL. For example, to populate Block from the other values, you might have:

CREATE PROCEDURE GetBlocks
(
    @SchemeTypeId INT NULL,
    @SchemeId INT NULL,
    @DistrictId INT NULL
)
AS
SELECT b.BlockId, b.BlockName
FROM   Blocks b
INNER JOIN SchemeTypeBlocks stb ON b.BlockId = stb.BlockId
INNER JOIN SchemeBlocks sb ON b.BlockId = sb.BlockId
INNER JOIN DistrictBlocks db ON b.BlockId = db.BlockId
WHERE  (@SchemeTypeId IS NULL OR stb.SchemeTypeId = @SchemeTypeId)
AND    (@SchemeId IS NULL OR sb.SchemeId = @SchemeId)
AND    (@DistrictId IS NULL OR db.DistrictId = @DistrictId)
ORDER BY b.BlockName

Without knowing your database, I do not know exactly what will work, but the idea is that you just pass NULL for anything you do not yet have selected. This could perhaps simplify your code a bit.

If this doesn't answer your question, let me know what I can clarify.


There is a Simple Way to Achieve this, follow this approach: First of All, just add an AjaxUpdatePanel... and then in drop down set AutoPostBack property to true, further just add the handler on OnSelectedIndexChanged event to enable second drop down..

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate><asp:DropDownList id="firstDrpDown" AutoPostBack="true"  
 OnSelectedIndexChanged="firstDropDown_SelectedIndexChanged"AppendDataBoundItems="true"
   name="firstDropDown" runat="server">

<asp:DropDownList id="scndDrpDown" **AutoPostBack="true"**  
OnSelectedIndexChanged="scndDropDown_SelectedIndexChanged" AppendDataBoundItems="true"   
name="scndDropDown" runat="server" > 

 </ContentTemplate>
  <asp:UpdatePanel>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜