A standard HTML select list lets users pick multiple items, but the interface is clunky users have to hold Ctrl or Cmd and click each option, and there's no visual indication of what's selected until they let go. A multi-select dropdown with checkboxes solves this by rendering each option with a checkbox, showing a "select all" toggle, and keeping the list compact behind a single dropdown trigger.
This article walks through building exactly that in ASP.NET Web Forms, using an asp:ListBox control on the server side and the bootstrap-multiselect jQuery plugin on the client side. You'll see the full markup, the JavaScript initialization, and server-side code in both C# and VB.NET to read back which items the user checked.
One thing worth being upfront about: ASP.NET Web Forms is a legacy technology. Most new ASP.NET development today happens in ASP.NET Core with MVC or Razor Pages, and Microsoft has not added new features to Web Forms in years. If you're maintaining an existing Web Forms application, this pattern still works fine and is worth knowing. If you're starting a new project, the same jQuery plugin and Bootstrap markup shown here will work just as well inside a Razor Pages or MVC view — only the server-side model binding changes.
Prerequisites
To follow along you'll need:
- An existing ASP.NET Web Forms project (or a new one created in Visual Studio) targeting a supported version of .NET Framework.
- Basic familiarity with
asp:ListBox, page lifecycle events, and server-side event handlers. - A current version of jQuery (3.x) and Bootstrap (4 or 5) loaded via CDN or local files.
- The
bootstrap-multiselectplugin by davidstutz, available from its GitHub repository or via npm/unpkg. Check the repository's release notes before using it — it was originally built against Bootstrap 3, and while later releases added Bootstrap 4/5 compatibility, you should confirm the version you pull down matches the Bootstrap version in your project. If you're on Bootstrap 5, test the dropdown rendering carefully, since some older multiselect plugins rely on Bootstrap 3/4 dropdown markup and CSS classes that changed in Bootstrap 5.
What Is the Bootstrap Multiselect Plugin?
bootstrap-multiselect is a jQuery plugin that progressively enhances a plain HTML <select multiple> element (or, in our case, the rendered output of an asp:ListBox) into a Bootstrap-styled dropdown where each option appears with its own checkbox. It adds features a native multi-select can't give you out of the box: a "select all" checkbox, live filtering/search, custom placeholder text when nothing is selected, and a button-style trigger that shows a summary of selected items instead of a long scrollable list box.
Under the hood, the plugin doesn't replace your original <select> element — it hides it and builds a Bootstrap dropdown UI next to it, then keeps both in sync. When a user checks a box in the dropdown, the plugin marks the corresponding <option> as selected in the hidden <select>. That matters for ASP.NET Web Forms specifically, because it means your asp:ListBox still gets posted back with the correct Selected values on each ListItem, exactly as if the user had used the native control.
Step 1: Reference jQuery, Bootstrap, and the Multiselect Plugin
Load the required scripts and stylesheets in your page's <head>. Use HTTPS for every reference — browsers routinely block mixed content (HTTP resources loaded from an HTTPS page), and there's no reason to use plain HTTP for CDN assets anymore.
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/css/bootstrap-multiselect.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/js/bootstrap-multiselect.js"></script>
A note on that plugin reference: Pull the plugin from a maintained package registry (npm via a CDN like jsDelivr or unpkg) or download the built files from the GitHub releases page and host them yourself. Self-hosting is actually the safer long-term choice for a plugin like this one, since it isn't under active heavy development and you don't want a future CDN outage or version bump breaking your production page unexpectedly.
Also pin your CDN version numbers explicitly, as shown above, rather than pointing at "latest" or a branch reference. That keeps your page from silently breaking when an upstream project publishes a breaking change.
Step 2: Build the ListBox Markup
The server-side control is unchanged — this is standard ASP.NET Web Forms markup, and it works the same regardless of which CSS framework version you pair it with:
<asp:ListBox ID="lstStudents" CssClass="form-select" runat="server" SelectionMode="Multiple"><asp:ListItem Text="Nikunj Satasiya" Value="1" /><asp:ListItem Text="Ronak Rabadiya" Value="2" /><asp:ListItem Text="Hiren Dobariya" Value="3" /><asp:ListItem Text="Vivek Ghadiya" Value="4" /><asp:ListItem Text="Pratik Pansuriya" Value="5" /><asp:ListItem Text="Kishan Patel" Value="6" /></asp:ListBox>
Note the CssClass changed to form-select, which is the Bootstrap 5 form control class. If you're still on Bootstrap 3 or 4, use form-control instead — check whichever Bootstrap version you've actually installed rather than copying this blindly.
Step 3: Initialize the Plugin
This is where the plugin turns the plain list box into a checkbox dropdown. A minimal initialization looks like this:
$(function () {$('[id*=lstStudents]').multiselect({includeSelectAllOption: true,nonSelectedText: 'Select Students'});});
The [id*=lstStudents] selector is a practical workaround for Web Forms: because the framework can mangle a control's rendered id attribute with naming-container prefixes (less of an issue if you set ClientIDMode="Static" on the control, which is worth doing in newer Web Forms projects), matching on a substring is a reliable way to grab the right element regardless of the final rendered ID.
Two options matter most here:
includeSelectAllOption adds a "Select all" checkbox at the top of the dropdown. When checked, it selects every option in one click; when any individual item is unchecked, the plugin automatically unchecks "Select all" too, so it always reflects the true state of the list rather than being a one-way toggle. Turn it off (false) if your list is short enough that selecting everything by hand isn't a hassle, or if selecting everything doesn't make sense for your use case.
nonSelectedText sets the label shown on the dropdown button when nothing is checked yet — this is what replaces the default "None selected" text. Once the user checks one or more boxes, the plugin automatically updates the button text to summarize the selection (for example, showing item names up to a limit, then falling back to something like "3 selected" once the list gets long). You don't need to write any extra code to make that summary appear — it's built into the plugin's default rendering behavior.
Accessibility is worth a mention here too: the plugin renders standard <input type="checkbox"> elements with associated <label> elements, so screen readers can announce each option and its checked state reasonably well out of the box. It doesn't add ARIA live-region announcements for the "select all" action or for the summary text changing, though, so if accessibility is a hard requirement for your project, test it with an actual screen reader before shipping and consider supplementing with your own ARIA attributes.
Step 4: Full Page Markup
Put it together in a complete .aspx page:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MultiSelectDemo.aspx.cs" Inherits="MultiSelectDemo" %><!DOCTYPE html><html><head runat="server"><title>Multi-Select Dropdown with Checkboxes in ASP.NET</title><script src="https://code.jquery.com/jquery-3.7.1.min.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/css/bootstrap-multiselect.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/bootstrap-multiselect@1.1.2/dist/js/bootstrap-multiselect.js"></script></head><body><form id="form1" runat="server"><div class="container mt-4"><h1 class="h2">Multi-Select Dropdown with Checkboxes Using jQuery</h1><br /><asp:ListBox ID="lstStudents" CssClass="form-select" runat="server" SelectionMode="Multiple"><asp:ListItem Text="Nikunj Satasiya" Value="1" /><asp:ListItem Text="Ronak Rabadiya" Value="2" /><asp:ListItem Text="Hiren Dobariya" Value="3" /><asp:ListItem Text="Vivek Ghadiya" Value="4" /><asp:ListItem Text="Pratik Pansuriya" Value="5" /><asp:ListItem Text="Kishan Patel" Value="6" /></asp:ListBox><br /><asp:Button Text="Submit Students" CssClass="btn btn-success" runat="server" OnClick="Submit" /></div></form><script>$(function () {$('[id*=lstStudents]').multiselect({includeSelectAllOption: true,nonSelectedText: 'Select Students'});});</script></body></html>
Step 5: Read the Selected Values on the Server
On postback, loop through the ListBox's items and check the Selected property — the multiselect plugin keeps the underlying <select> in sync, so this works exactly as it would without the plugin involved.
C#:
public partial class MultiSelectDemo : System.Web.UI.Page{protected void Submit(object sender, EventArgs e){var selected = new List<string>();foreach (ListItem item in lstStudents.Items){if (item.Selected){selected.Add($"Student Name: {item.Text}, Enrollment No: {item.Value}");}}string message = string.Join("\n", selected);string safeMessage = HttpUtility.JavaScriptStringEncode(message);ClientScript.RegisterStartupScript(this.GetType(), "alert",$"alert('{safeMessage}');", true);}}
VB.NET:
Partial Class MultiSelectDemoInherits System.Web.UI.PageProtected Sub Submit(sender As Object, e As EventArgs)Dim selected As New List(Of String)For Each item As ListItem In lstStudents.ItemsIf item.Selected Thenselected.Add($"Student Name: {item.Text}, Enrollment No: {item.Value}")End IfNextDim message As String = String.Join("\n", selected)Dim safeMessage As String = HttpUtility.JavaScriptStringEncode(message)ClientScript.RegisterStartupScript(Me.GetType(), "alert", $"alert('{safeMessage}');", True)End SubEnd Class
The key change from a naive string-concatenation approach is the call to HttpUtility.JavaScriptStringEncode. The original list item text here is hardcoded and safe, but the moment you populate this list box from a database — student names entered by users, for instance — an unescaped apostrophe or quote in a name will break out of the JavaScript string literal and either throw a script error or, worse, become an injection point. JavaScriptStringEncode escapes quotes, backslashes, and control characters so the resulting string is always safe to drop into a JS literal. This is a small habit worth keeping any time you're building a client script string from server-side data, not just in this example.
How It Works
When the page loads, the plugin scans the matched <select> element, hides it (it stays in the DOM and still participates in postback), and injects a Bootstrap dropdown button plus a checkbox list built from the <option> elements. Every checkbox click updates the corresponding option's selected attribute on the hidden select behind the scenes. Because ASP.NET Web Forms reads form values straight from the posted HTML on submit, it doesn't know or care that a jQuery plugin was involved — it just sees a <select multiple> with some options marked selected, exactly like it would from a native multi-select box.
Common Errors and Troubleshooting
The dropdown renders as a plain unstyled list box. This almost always means the plugin's CSS file didn't load, or it loaded after the plugin JS tried to initialize. Check your browser's network tab for a 404 on the CSS or JS file — a dead rawgit.com link is a common cause if you're working from an older copy of this code.
Clicking checkboxes does nothing, and the console shows `$(...).multiselect is not a function`. This means the plugin script loaded before jQuery, or didn't load at all. Script order matters: jQuery must be referenced before Bootstrap's JS and before the multiselect plugin.
The ListBox comes back empty on postback even though checkboxes were checked. Confirm the ListBox is inside the <form runat="server"> element and that the button causing postback is also inside that form. Also verify SelectionMode="Multiple" is set on the asp:ListBox — without it, the server only reads a single selected value.
The dropdown looks broken specifically on Bootstrap 5. Some multiselect plugin builds assume Bootstrap 4's dropdown and caret markup. If you've upgraded to Bootstrap 5, pull the latest release of the plugin and check its changelog for explicit Bootstrap 5 support before assuming the mismatch is something in your own code.
Best Practices
Keep your plugin and framework versions pinned and documented somewhere in the project, rather than pointing at "latest" tags on a CDN — an unannounced upstream update is a bad way to discover a breaking change in production. Escape any server-side string you inject into inline JavaScript, even when the current data source feels safe; data sources change, and a page that was safe with hardcoded values often isn't safe once it's wired to a database. If you're starting a brand-new project rather than maintaining an existing one, seriously consider building the UI in ASP.NET Core with Razor Pages or MVC instead of Web Forms — the same jQuery plugin and Bootstrap approach carries over directly, and you gain a framework that's still receiving active investment and long-term support.
Conclusion
A checkbox-based multi-select dropdown is a small UI upgrade that meaningfully improves usability over a native multi-select list box, and pairing an asp:ListBox with the bootstrap-multiselect plugin gets you there without changing how your server-side postback logic works. The main things to get right are loading your scripts over HTTPS from a live, maintained source instead of a dead rawgit link, matching your plugin version to your actual Bootstrap version, and escaping any dynamic data before it lands inside inline JavaScript. If you're building something new rather than patching an existing Web Forms app, look at doing the equivalent in ASP.NET Core with Razor Pages — the client-side half of this tutorial transfers over almost unchanged.

