1

I have a webform page that has a Listbox on it with a list of cameras on it. It is populated by a datatable that has a column for camera name, ip address, and group.

    DataClasses1DataContext dc = new DataClasses1DataContext();
    public List<CameraTable> CameraListBox;
    public List<ListItem> SelectedListBox;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            CameraListBox = (from x in dc.CameraTables
                             select x).ToList();
            ListBox1.DataSource = CameraListBox;
            ListBox1.DataTextField = "CameraName";
            ListBox1.DataValueField = "IPAddress";
            ListBox1.DataBind();
        }
    }

That code works fine to populate the listbox with the camera names, but I would like to make it so that it has the group then the cameras in that group. I have no idea how to do this. I learned not to asked questions on here unless I absolutely have to, but I've researched this for a few days and cannot find anything. Is this possible? Would I have to do all of this programmatically?

Jason W
  • 13,026
  • 3
  • 31
  • 62
user3634308
  • 119
  • 1
  • 3
  • 14
  • Try http://stackoverflow.com/questions/1701296/c-sharp-grouping-sorting-a-generic-list-using-linq – mjw May 11 '15 at 17:13
  • what exactly do you want group ? if you want group the list before binding you can use linq lists for do it ! – Enrique YC May 11 '15 at 17:13
  • I want it to have Group name, then the cameras in that group below it, then group2, cameras for group2 below it, etc. – user3634308 May 11 '15 at 19:46
  • @mjw that sorted the list like I want, but it doesn't have the group name listed. Any idea how to put the group name, then the cameras in that group? – user3634308 May 11 '15 at 21:38
  • Just so i'm clear, you want to group the list and display each group name with the cameras in each group directly after? So like: Canon\n EOS1 \n EOS2 etc Fuji\n Finepix1 \n Finepix2 \n Finepix3 etc...? – mjw May 12 '15 at 13:49
  • @mjw Yes, that is what I want. – user3634308 May 18 '15 at 15:41

1 Answers1

1

Based on a great approach from SO answer: How can I add option groups in ASP.NET drop down list?

A ListBox in ASP.NET does not support the optgroup html required to do the grouping you are requesting. One approach to inject this functionality by adding an attribute to the list items to capture the category, then using your favorite front-end framework to modify the DOM to build the appropriate optgroup structure.

Because the ListBox control does not have an OnItemDataBound type of event, you can't get access to each item during the data binding process. Since that would be the only time you could access the group of the CameraTable record, you cannot do databinding - you have to build the list yourself so that you can add the group as an html attribute to each option.

The method below is a helper to create a single list item with the data attribute if possible.

public ListItem GetListItem(CameraTable item)
{
    var listItem = new ListItem(item.CameraName, item.IPAddress);
    if (string.IsNullOrEmpty(item.GroupName) == false)
        listItem.Attributes.Add("data-category", item.GroupName);
    return listItem;
}

Then, instead of your databinding code on the listbox, just build the listbox directly. You'll have to account for the view state and persistence across postbacks, but this at least is the approach:

var itemsToAdd = CameraListBox
    .Select(c => GetListItem(c))
    .ToArray();
ListBox1.Items.AddRange(itemsToAdd);

Finally, pull out your favorite client framework (JQuery below) to build the optgroup elements.

var groups = {};
$("select option[data-category]").each(function () {
    groups[$.trim($(this).attr("data-category"))] = true;
});
$.each(groups, function (c) {
    $("select option[data-category='"+c+"']").wrapAll('<optgroup label="' + c + '">');
});

This should complete the grouping for your elements.

UPDATE BASED ON COMMENT QUESTION

If you're going to put it in the head of the html, you have to ensure the DOM has loaded - otherwise you are manipulating elements that are not yet ready.

To stay with JQuery here, wrap the client script in a $(document).ready event. I included a full sample page below.

ASPX Page

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="Web.WebForm1" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var groups = {};
            $("select option[data-category]").each(function () {
                groups[$.trim($(this).attr("data-category"))] = true;
            });
            $.each(groups, function (c) {
                $("select option[data-category='" + c + "']").wrapAll('<optgroup label="' + c + '">');
            });
        });
    </script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="BodyContent" runat="server">
    <asp:ListBox ID="ListBox1" runat="server" Height="100" Width="200" />
</asp:Content>

ASPX Code Behind (with mocked data context and cameratable)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;

namespace Web
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        DataClasses1DataContext dc = new DataClasses1DataContext();
        public List<CameraTable> CameraListBox;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                CameraListBox = (from x in dc.CameraTables
                                 select x).ToList();
                var itemsToAdd = CameraListBox
                    .Select(c => GetListItem(c))
                    .ToArray();
                ListBox1.Items.AddRange(itemsToAdd);
            }
        }

        public ListItem GetListItem(CameraTable item)
        {
            var listItem = new ListItem(item.CameraName, item.IPAddress);
            if (string.IsNullOrEmpty(item.GroupName) == false)
                listItem.Attributes.Add("data-category", item.GroupName);
            return listItem;
        }

    }

    public class DataClasses1DataContext
    {
        public IQueryable<CameraTable> CameraTables
        {
            get
            {
                return new List<CameraTable>()
                {
                    new CameraTable("Back Hallway", "1.1.1.1", "Floor 1"),
                    new CameraTable("Bedroom 1", "2.2.2.2", "Floor 1"),
                    new CameraTable("Bedroom 2", "3.3.3.3", "Floor 2"),
                }.AsQueryable();
            }
        }
    }

    public class CameraTable
    {
        public string CameraName { get; set; }
        public string IPAddress { get; set; }
        public string GroupName { get; set; }
        public CameraTable(string name, string ip, string group)
        {
            this.CameraName = name;
            this.IPAddress = ip;
            this.GroupName = group;
        }
    }
}
Community
  • 1
  • 1
Jason W
  • 13,026
  • 3
  • 31
  • 62
  • Where do I put the JQuery part in the html? In the section? Because right now it just fills the listbox like before. – user3634308 May 18 '15 at 16:01
  • If you're going to put it in the head of the html, you have to ensure the DOM has loaded - otherwise you are manipulating elements that are not yet ready. I added an update with the full code sample to the answer. – Jason W May 18 '15 at 17:11
  • That was my problem, thanks. And thanks for putting a full code example. – user3634308 May 18 '15 at 17:37