ایجاد Dropdownlist با انتخاب چندگانه با استفاده از BootsStrap

در این مقاله آموزش می دهیم که چگونه با استفاده از BootStrap در4.0 ASP.Net کنترل Dropdownlist با امکان انتخاب چندگانه ایجاد کنیم.

ایجاد Dropdownlist با انتخاب چندگانه با استفاده از BootsStrap

زمانی که روی یک پروژه کار می کنیم، گاهی اوقات نیاز به Dropdownlistهایی با انتخاب چندگانه داریم که ویژگی های زیر را دارد:

- امکان فیلتر auto compelete

- براساس نیاز کاربر می تواند انتخاب چندگانه یا یگانه باشد.

- در حالت multiSelect باید گزینه انتخاب همه موارد نیز وجود داشته باشد.

- گزینه های انتخاب شده باید به صورت یک لیست در زیر dropdownlist نمایش داده شود.

شکل زیر، موارد گفته شده را نشان می دهد:

به طور پیش فرض، ASP.Net چنین کنترلی ندارد. اما امکانی را فراهم کرده است که با ترکیب چند کنترل مانند وب سرور، کنترل های HTML و... می توانیم کنترل موردنظر خود را بسازیم. برای پیاده سازی این کنترل، ما از BootStrap استفاده می کنیم که در این مورد بهترین انتخاب است.

کنترل انتخاب چندگانه Bootstrap با امکانات بسیاری برای تنظیمات و سفارشی سازی براساس نیاز کاربر می باشد که آن را مطابق نیاز خود تنظیم نماید. برای تنظیم این گزینه ها در user control خود از property ها استفاده می کنیم که یا مقدار ذخیره شده در فیلدهای پنهان را برمی گرداند یا ست می کند.

با استفاده از ویژوال استودیو 2013 یک پروژه ASP.Net از نوع web Application و به نام DummyWebApp می سازیم، در Solution Explorer خود یک فولدر جدید به نام User Control ایجاد می کنیم. در این فولدر کامپوننت جدیدی از نوع “Web User Control” اضافه می کنیم و نام آن را “AutoCompleteDdl” می گذاریم.

کد زیر، مربوط به AutoCompleteDdl.aspx می باشد.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="AutoCompleteDdl.ascx.cs" Inherits="UserControl_AutoCompleteDdl" %>  
<div style="width: 380px;" id="divAutoComplete" runat="server">  
    <asp:HiddenField ID="hdnButtonWidth" runat="server" Value="320px" />  
    <asp:HiddenField ID="hdnNonSelectedText" runat="server" Value="--Select--" />  
    <asp:HiddenField ID="hdnIncludeSelectAllOption" runat="server" Value="false" />  
    <asp:HiddenField ID="hdnSelectAllText" runat="server" Value="All" />  
    <asp:HiddenField ID="hdnEnableFiltering" runat="server" Value="False" />  
    <asp:HiddenField ID="hdnEnableFilteringIgnoreCase" runat="server" Value="False" />  
    <asp:HiddenField ID="hdnDisableIfEmpty" runat="server" Value="False" />  
    <asp:HiddenField ID="hdnMaxHeight" runat="server" Value="200" />  
    <asp:HiddenField ID="hdnFilterPlaceholder" runat="server" Value="Search for something..." />  
    <asp:HiddenField ID="hdnAllSelectedText" runat="server" Value="No option left..." />  
    <asp:HiddenField ID="hdnText" runat="server" />  
    <asp:HiddenField ID="hdnValue" runat="server" />  
    <asp:ListBox ID="ddlAutoCompleteSelect" runat="server" Style="width: 350px;"   
        SelectionMode="Multiple">  
    </asp:ListBox>  
    <p>  
        <asp:Label ID="lblSelectedItems" runat="server" Style="word-wrap: break-word; height: 120px;  
            float: left; overflow-y: auto;"></asp:Label>  
    </p>  
</div> 

همانطور که مشاهده می کنید، از چند فیلد پنهان برای نگه داری داده ها بین سرور و کلاینت استفاده کردیم و یک listbox وجود دارد که امکان انتخاب چندگانه یا یگانه را به کاربر می دهد. درواقع، فیلدهای پنهان برای نگه داری مقادیر استفاده شده در تنظیمات کنترل انتخاب چندگانه Bootstrap می باشند. در اینجا userهایی با propertyهایی ایجاد کرده ایم که نشان می دهد هر کاربر از چه کنترلی استفاده خواهد کرد.

کد زیر، مربوط به AutoCompleteDdl.aspx.cs می باشد:

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.UI;  
using System.Web.UI.WebControls;  
  
public partial class UserControl_AutoCompleteDdl : System.Web.UI.UserControl  
{  
    #region Variable Declaration  
  
    private string _text;  
    private string _value;  
    private List<SelectModel> _dataSource;  
    private ListSelectionMode _selectionMode;  
    private const string _dataTextField = "Text";  
    private const string _dataValueField = "Value";  
 
    #endregion  
 
    #region Properties  
  
    /// <summary>  
    /// Set the placeholder for the DropDownlist.  
    /// </summary>  
    public string NonSelectedText  
    {  
        get { return hdnNonSelectedText.Value; }  
        set { hdnNonSelectedText.Value = value; }  
    }  
  
    /// <summary>  
    /// Get Or Set the value whether select all option should be there or not.  
    /// </summary>  
    public bool IncludeSelectAllOption  
    {  
        get { return Convert.ToBoolean(hdnIncludeSelectAllOption.Value); }  
        set { hdnIncludeSelectAllOption.Value = Convert.ToString(value); }  
    }  
  
    public bool EnableFiltering  
    {  
        get { return Convert.ToBoolean(hdnEnableFiltering.Value); }  
        set { hdnEnableFiltering.Value = Convert.ToString(value); }  
    }  
  
    /// <summary>  
    /// Configure the Type of DropDownlist it is. For e.g. whether single select or multi select.  
    /// </summary>  
    public ListSelectionMode SelectionMode  
    {  
        get { return _selectionMode; }  
        set  
        {  
            _selectionMode = value;  
            if (value == ListSelectionMode.Single)  
            {  
                ddlAutoCompleteSelect.SelectionMode = ListSelectionMode.Single;  
            }  
            else  
            {  
                ddlAutoCompleteSelect.SelectionMode = ListSelectionMode.Multiple;  
            }  
        }  
    }  
  
    /// <summary>  
    /// Set the Width of the Combo  
    /// </summary>  
    public string ButtonWidth  
    {  
        get { return hdnButtonWidth.Value; }  
        set  
        {  
            hdnButtonWidth.Value = value;  
            lblSelectedItems.Style.Add("width", hdnButtonWidth.Value);  
        }  
    }  
  
    /// <summary>  
    /// To set text search enabled  
    /// </summary>   
    public bool Enabled  
    {  
        get { return ddlAutoCompleteSelect.Enabled; }  
        set { ddlAutoCompleteSelect.Enabled = value; }  
    }  
  
    /// <summary>  
    /// To set Data Source for Control  
    /// </summary>  
    public List<SelectModel> DataSource  
    {  
        set  
        {  
            _dataSource = value;  
            ddlAutoCompleteSelect.DataSource = value;  
            ddlAutoCompleteSelect.DataBind();  
        }  
    }  
  
    /// <summary>  
    /// To set Text field  
    /// </summary>  
    public string DataTextField  
    {  
        get { return ddlAutoCompleteSelect.DataTextField; }  
        set { ddlAutoCompleteSelect.DataTextField = _dataTextField; }  
    }  
  
    /// <summary>  
    /// To set Value field  
    /// </summary>  
    public string DataValueField  
    {  
        get { return ddlAutoCompleteSelect.DataValueField; }  
        set { ddlAutoCompleteSelect.DataValueField = _dataValueField; }  
    }  
  
    /// <summary>  
    /// Get the value of the Selected Items from the dropdownlist.  
    /// </summary>  
    public string Value  
    {  
        get  
        {  
            string strValue = string.Empty;  
            return hdnValue.Value;  
        }  
    }  
  
    /// <summary>  
    /// Get the text of the Selected Items from the dropdownlist.  
    /// </summary>  
    public string Text  
    {  
        get  
        {  
            string strText = string.Empty;  
            return hdnText.Value;  
        }  
    }  
  
    public bool DisableIfEmpty  
    {  
        get { return Convert.ToBoolean(hdnDisableIfEmpty.Value); }  
        set { hdnDisableIfEmpty.Value = Convert.ToString(value); }  
    }  
  
    public int MaxHeight  
    {  
        get { return Convert.ToInt32(hdnMaxHeight.Value); }  
        set { hdnMaxHeight.Value = Convert.ToString(value); }  
    }  
  
    public string SelectAllText  
    {  
        get { return hdnSelectAllText.Value; }  
        set { hdnSelectAllText.Value = value; }  
    }  
  
    public bool EnableFilteringIgnoreCase  
    {  
        get { return Convert.ToBoolean(hdnEnableFilteringIgnoreCase.Value); }  
        set { hdnEnableFilteringIgnoreCase.Value = Convert.ToString(value); }  
    }  
  
    public string FilterPlaceholder  
    {  
        get { return hdnFilterPlaceholder.Value; }  
        set { hdnFilterPlaceholder.Value = value; }  
    }  
 
 
    #endregion  
  
    protected void Page_Load(object sender, EventArgs e)  
    {  
  
        try  
        {  
            if (!(string.IsNullOrEmpty(hdnText.Value) && string.IsNullOrEmpty(hdnValue.Value)))  
            {  
                var selectedItemsText = hdnText.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);  
                var selectedItemsValue = hdnValue.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);  
  
                foreach (ListItem item in ddlAutoCompleteSelect.Items)  
                {  
                    if ((selectedItemsText.Contains(item.Text) && selectedItemsValue.Contains(item.Value)))  
                    {  
                        //selecting the item at server side.  
                        item.Selected = true;  
                    }  
                }  
            }  
        }  
        catch (Exception ex)  
        {  
            throw ex;  
        }  
  
    }  
  
  
    protected void Page_PreRender(object sender, System.EventArgs e)  
    {  
        try  
        {  
            //Registering the scripts file used by the user control. Alternatively you can also set these files inside your master page or the web page on which you are going to use it.  
            ScriptManager.RegisterClientScriptInclude(this, this.GetType(), "bootStrapJs", ResolveUrl("~/Scripts/bootstrap.min.js"));  
            ScriptManager.RegisterClientScriptInclude(this, this.GetType(), "bootStrapMultiSelectJs", ResolveUrl("~/Scripts/bootstrap-multiselect.js"));  
            ScriptManager.RegisterClientScriptInclude(this, this.GetType(), "autoCompleteDdlJs", ResolveUrl("~/Scripts/app/AutoCompleteDdl.js"));  
        }  
        catch (Exception ex)  
        {  
            throw ex;  
        }  
    }  
} 

برای تنظیم انتخاب چندگانه به صورت سفارشی از یک فایل اسکریپت برای usercontrol استفاده می کنیم.

AutoCompleteDdl.js

//Controls  
var labelId = '';  
var hdnTextId = '';  
var hdnValueId = '';  
  
var ddlCntrl = '';  
  
//Property Values Controls  
var hdnButtonWidth = '';  
var hdnNonSelectedText = '';  
var hdnIncludeSelectAllOption = '';  
var hdnSelectAllText = '';  
var hdnEnableFiltering = '';  
var hdnEnableFilteringIgnoreCase = '';  
var hdnDisableIfEmpty = '';  
var hdnMaxHeight = '';  
var hdnFilterPlaceholder = '';  
var hdnAllSelectedText = '';  
  
//Helper variables  
var selectedItemsText = '';  
var selectedItemsValue = '';  
  
//This function is used for converting C# boolean values to   
//Javascript boolean Values.  
function convertToBoolean(value) {  
    return (value === "true");  
}  
  
//Writing function inside the pageLoad function is for rebinding the events after partial postback.  
function pageLoad() {  
    $(document).ready(function () {  
    //Iterating over the no of select element whose Id contains value of "ddlAutoCompleteSelect". This iteration is necessary because on a single page the user control might can be used on   
        //single times or multiple times. For e.g. if the user contorl is placed inside a gridview row, or if the user control is used on some kind of registration forms.  
  
        $("select[id*='ddlAutoCompleteSelect']").each(function () {  
            ddlCntrl = $(this);  
            divUCParent = $(ddlCntrl).parent();  
  
            //retrieving the Id's of the controls/elements  
            labelId = ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "lblSelectedItems");  
            hdnTextId = ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnText");  
            hdnValueId = ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnValue");  
  
            //retrieving the values of the hidden fields/ property values  
            hdnButtonWidth = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnButtonWidth")).val();  
            hdnNonSelectedText = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnNonSelectedText")).val();  
            hdnIncludeSelectAllOption = convertToBoolean($("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnIncludeSelectAllOption")).val().toString().toLowerCase());  
            hdnSelectAllText = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnSelectAllText")).val();  
            hdnEnableFiltering = convertToBoolean($("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnEnableFiltering")).val().toString().toLowerCase());  
            hdnEnableFilteringIgnoreCase = convertToBoolean($("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnEnableFilteringIgnoreCase")).val().toString().toLowerCase());  
            hdnDisableIfEmpty = convertToBoolean($("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnDisableIfEmpty")).val().toString().toLowerCase());  
            hdnMaxHeight = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnMaxHeight")).val();  
            hdnFilterPlaceholder = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnFilterPlaceholder")).val();  
            hdnAllSelectedText = $("#" + ddlCntrl.attr("id").replace("ddlAutoCompleteSelect", "hdnAllSelectedText")).val();  
  
  
            selectedItemsText = $("#" + hdnTextId).val();  
            selectedItemsValue = $("#" + hdnValueId).val();  
          
         //configuring the bootstrap multiselect with the custom specification which user has set through the properties.  
            $(this).multiselect({  
                buttonWidth: hdnButtonWidth,  
                includeSelectAllOption: hdnIncludeSelectAllOption,  
                enableFiltering: hdnEnableFiltering,  
                enableCaseInsensitiveFiltering: hdnEnableFilteringIgnoreCase,  
                selectAllText: hdnSelectAllText,  
                nonSelectedText: hdnNonSelectedText,  
                disableIfEmpty: hdnDisableIfEmpty,  
                maxHeight: hdnMaxHeight,  
                filterPlaceholder: hdnFilterPlaceholder,  
                allSelectedText: hdnAllSelectedText,  
                buttonText: function (options, select) {  
                    if (options.length === 0) {  
                        return this.nonSelectedText;  
                    }  
                    else if (this.allSelectedText && options.length == $('option', $(select)).length) {  
                        if (this.selectAllNumber) {  
                            return this.allSelectedText + ' (' + options.length + ')';  
                        }  
                        else {  
                            return this.allSelectedText;  
                        }  
                    }  
                    else {  
                        var selected = '';  
                        options.each(function () {  
                            var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();  
                //forming a list.  
                            selected += "<li>" + label + "</li>";  
                        });  
                        return selected;  
                    }  
                },  
                onChange: function (option, checked, select) {  
  
                    var options = this.getSelected();  
  
                    // resetting the updateButtonText event values  
                    if (this.options.enableHTML) {  
                        $('.multiselect .multiselect-selected-text', this.$container).html(hdnNonSelectedText);  
                    }  
                    else {  
                        $('.multiselect .multiselect-selected-text', this.$container).text(hdnNonSelectedText);  
                    }  
                    $('.multiselect', this.$container).attr('title', hdnNonSelectedText);  
  
                    //setting the Label data by forming a unordered list.  
                    $("#" + labelId).html("<ul>" + this.options.buttonText(options, this.$select) + "</ul>");  
  
                    //resetting the variable values to ''  
                    selectedItemsText = '';  
                    selectedItemsValue = '';  
  
                    //iterating over the selected options and appending it to the variables  
                    $.each(options, function (index, option) {  
                        selectedItemsText += option.text + ",";  
                        selectedItemsValue += option.value + ",";  
                    });  
  
selectedItemsText = selectedItemsText.substring(0, selectedItemsText.lastIndexOf(","));  
selectedItemsValue = selectedItemsValue.substring(0, selectedItemsValue.lastIndexOf(","));  
  
                    //finally storing the value to the respective hidden fields.  
                    $("#" + hdnTextId).val(selectedItemsText);  
                    $("#" + hdnValueId).val(selectedItemsValue);  
                }  
            });  
        });  
      
     //incase of post back get the data from the hiddenField hdnText and hdnValues for restoring it back to the userControl selected values.  
        if (selectedItemsText != '' && selectedItemsValue != '') {  
  
            var selectedTextsArr = selectedItemsText.split(",");  
            var selectedValuesArr = selectedItemsValue.split(",");  
  
            $("#" + divUCParent.attr("id")).children(".btn-group").find("ul.multiselect-container li").each(function () {  
                var currentLI = $(this);  
                var anchorElement = $(this).find("a");  
                if (anchorElement != undefined && anchorElement.length != 0) {  
                    var checkBox = $(anchorElement).children("label[class='checkbox']").children("input[type='checkbox']");  
                    var checkBoxValue = $(checkBox).val();  
  
                    if ($.inArray(checkBoxValue, selectedValuesArr) > -1) {  
                        $(currentLI).addClass("active");  
                        $(checkBox).trigger("change");  
                    }  
                }  
            });  
        }  
  
    });  
}

نکات:

1. در اسکریپت usercontrol، متد ButtonText مربوط به Multiselect BootStrap را مطابق آنچه که user control ما نیاز دارد، override می کنیم. به عنوان مثال، لیستی از آیتم های انتخاب شده را به صورت یک لیست مرتب نشده نشان دهیم.

2. در رویداد onChange، تنظیمات پیش فرض multiselect dropdown مربوط به Bootstrap را ریست می کنیم. Bootdtrap به طور پیش فرض، آیتم انتخاب شده را در عنوان دکمه نمایش می دهد. در این رویداد، آیتم های انتخاب شده را زیر usercontrol در یک لیست مرتب نشده درون lableهایی نشان می دهیم.

3. اگر به فایل aspx. مربوط به usercontrol نگاه کنید، برای فیلدهای پنهان نیز مقدار پیش فرضی در نظر گرفته ایم. اگر کاربر زمان پیاده سازی usercontrol برای هیچ یک از property ها مقداری ارسال نکند، مقدار پیش فرض برای آن از این فیلدهای پنهان مورد استفاده قرار می گیرد، در غیر این صورت مقدار وارد شده توسط کاربر استفاده خواهد شد.

4. در این قسمت، تنها ویژگی هایی که موردنیاز ما بود اضافه کردیم. اما علاوه بر این ها، property های زیادی وجود دارد که ممکن است برای تنظیم برنامه خود به آن ها نیاز داشته باشید.

5. برای پیاده سازی propertyهای دیگر نیز به همین ترتیب عمل می کنیم. با ایجاد یک property در apsx.cs. و یک فیلد پنهان در فایل aspx. می توانید این کار را انجام دهید.

6. کد خود را در فایل اسکریپت usercontrol بنویسید و یا مقادیر را از فیلد پنهان بخوانید.

حال usercontrol ما آماده است و باید آن را در صفحه قرار دهیم تا کار کند. برای این کار یک Web page به پروژه اضافه کرده و usercontrol را در این صفحه register می کنیم.

کد زیر، مربوطه به فایل aspx. صفحه جدید می باشد:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>  
  
<%@ Register Src="UserControl/AutoCompleteDdl.ascx" TagName="AutoCompleteDdl" TagPrefix="uc1" %>  
  
<!DOCTYPE html>  
  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head runat="server">  
    <title>Bootstrap Multiselect</title>  
    <link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />  
    <link rel="stylesheet" type="text/css" href="Style/bootstrap-multiselect.css" />  
    <script src="Scripts/jquery-1.9.1.min.js"></script>  
</head>  
<body>  
    <form id="form1" runat="server">  
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>  
        <div class="container">  
            <div class="row">  
                <div class="col-md-12">  
                    <h3>Bootstrap MultiSelect Dropdownlist</h3>  
                    <asp:UpdatePanel ID="udpMain" runat="server">  
                        <ContentTemplate>  
  
                            <uc1:AutoCompleteDdl ID="ddlAutoComplete" runat="server" IncludeSelectAllOption="false"  
                                ButtonWidth="320px" SelectAllText="All" NonSelectedText="--Select Option--" MaxHeight="400"  
                                EnableFiltering="true" FilterPlaceholder="Search For Something..." SelectForm="Multiple"  
                                PlaceHolder="Select a Value" />  
                            <p>  
                                <asp:Button ID="btnGet" runat="server" Text="Get Data" OnClick="btnGet_Click" />  
                            </p>  
                            <p>  
                                <asp:Label ID="lblData" runat="server" />  
                            </p>  
                        </ContentTemplate>  
                    </asp:UpdatePanel>  
                </div>  
            </div>  
        </div>  
    </form>  
</body>  
</html>

Default.aspx.cs

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.UI;  
using System.Web.UI.WebControls;  
  
public partial class _Default : System.Web.UI.Page  
{  
    List<SelectModel> lstData = new List<SelectModel>();  
  
    protected void Page_Load(object sender, EventArgs e)  
    {  
        if (!IsPostBack)  
        {  
            for (int i = 0; i < 100; i++)  
            {  
                lstData.Add(new SelectModel() { Value = "Item " + i, Text = "Item " + i });  
            }  
            ddlAutoComplete.DataValueField = "Value";  
            ddlAutoComplete.DataTextField = "Text";  
            //Setting the data source for the dropdownlist  
            ddlAutoComplete.DataSource = lstData;  
        }  
    }  
    protected void btnGet_Click(object sender, EventArgs e)  
    {  
        string value = ddlAutoComplete.Value;  
        string text = ddlAutoComplete.Text;  
  
        lblData.Text = "<b>Value: </b>" + value + "<br/><b>Text: </b>" + text;  
  
    }  
} 

حال اگر برنامه را اجرا نمایید، خروجی زیر را مشاهده خواهید کرد:

آیتم هایی از dropdownlist را انتخاب کنید و می توانید موارد انتخاب شده را پایین این کنترل در یک لیست مشاهده کنید.

همچنین می توانید با تایپ کردن در textbox مربوط به جستجو، آیتم ها را فیلتر نمایید.

اما نکته دیگر، نگه داری داده ها بین Postbackها است. برای تست آن، از دکمه "دریافت داده" و Lableای که برای نمایش متن و مقدار آیتم انتخاب شده را نمایش می دهد، استفاده کرده ایم. با زدن دکمه، پس از Postback می توانیم خروجی را ببینیم.

فایل های ضمیمه