Monday, May 28, 2012

How to get cascaded List Boxes using SharePoint lists as data source..

List boxes have been usually used for selecting multiple values e.g. "Countries".  Now you need to populate another list box containing "Landmarks" of that all selected countries. In this case we need  two cascaded list boxes, on the selection of list box "Countries" items can trigger to population of another list box "Landmarks" and also depopulate the  list box "Landmarks" on the deselection of list box "Countries" items. In this kind of cases we need cascaded list boxes. Here are the steps to do it using SharePoint list as a data source.

1.) At first we need to put four list boxes in our .aspx page as pairs of selected and available and also four  buttons in pairs of two to add and remove functionality. I have also put it in Update Panel so that whole page should not be post back on buttons click. Code is as follows:

                <asp:UpdatePanel runat="server" ID="updatePanelCountries" UpdateMode="Conditional">
                    <contenttemplate>
                        <table width="50%">
                            <tr>
                                <td>
                                    <asp:ListBox ID="lstCountryAvailable" runat="server"
                                        SelectionMode="Multiple" BackColor="#f2f2f7" >
                                    </asp:ListBox>
                                </td>
                                <td>
                                    <asp:ImageButton runat="server" ID="btnCountryAdd" ImageUrl="/_layouts/Images/AddSelect.jpg" OnClick="btnCountryAdd_Click" /><br />
                                    <asp:ImageButton runat="server" ID="btnCountryRemove" ImageUrl="/_layouts/Images/RemoveSelect.jpg" OnClick="btnCountryRemove_Click" /><br />
                               <td rowspan="4" class="tdListBox_Skills">
                            <asp:ListBox ID="llstCountrySelected" runat="server"
                                SelectionMode="Multiple" BackColor="#f2f2f7">
                            </asp:ListBox>
                        </td>
                    </tr>
                </table>
                 </contenttemplate>
                    <triggers>
                    <asp:AsyncPostBackTrigger ControlID="btnLandmarksSelected" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="BtnLandmarksRemove" EventName="Click" />
                </triggers>
                </asp:UpdatePanel>
            </td>
        </tr>
        <tr>
            <td >
             <asp:UpdatePanel runat="server" ID="updatePanelLandmarks" UpdateMode="Conditional">
                    <contenttemplate>
                <table>
                    <tr>
                        <td>
                            <asp:ListBox ID="lstLandmarksAvailable" runat="server" SelectionMode="Multiple"
                                Rows="12" BackColor="#f2f2f7" ></asp:ListBox>
                        </td>
                        <td class="tdAddRemoveButton">
                            <asp:ImageButton ID="btnLandmarksAdd" runat="server" ImageUrl="/_layouts/Images/AddSelect.jpg" OnClick="btnLandmarksAdd_Click" /><br />
                            <asp:ImageButton runat="server" ID="btnLandmarksRemove" ImageUrl="/_layouts/Images/RemoveSelect.jpg" OnClick="btnLandmarksRemove_Click" /><br />
                        </td>
                        <td>
                            <asp:ListBox ID="lstLandmarksSelected" runat="server"
                                SelectionMode="Multiple" Rows="12">
                            </asp:ListBox>
                        </td>
                    </tr>
                </table>
                 </contenttemplate>
                    <triggers>
                    <asp:AsyncPostBackTrigger ControlID="btnCountryAdd" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="btnCountryRemove" EventName="Click" />
                </triggers>
                </asp:UpdatePanel>

2.) After putting all these code in your .aspx page you need to write .cs file for handling all button clicks. So we need to add following code.

protected void btnCountryAdd_Click(object sender, EventArgs e)
        {
            try
            {
                MoveCascadedListBoxItems(lstCountryAvailable, llstCountrySelected, lstLandmarksAvailable, lstLandmarksSelected, true, "ADD");
                SortListBox(lstSkillCategorySelected);
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
        }


public static void MoveCascadedListBoxItems(ListBox lstBoxAvailable, ListBox lstBoxSelected, ListBox lstSkill, ListBox lstSkillSelected, bool Cascade, string action)
        {
            if ((lstBoxAvailable != null) && (lstBoxSelected != null))
            {
                for (int i = lstBoxAvailable.Items.Count - 1; i >= 0; i--)
                {
                    if (lstBoxAvailable.Items[i].Selected)
                    {
                        lstBoxSelected.Items.Add(new ListItem
                        {
                            Text = lstBoxAvailable.Items[i].Text,
                            Value = lstBoxAvailable.Items[i].Value
                        });
                        if (action == "REMOVE")
                        {
                            DataTable dt = GetSPDataTable("Skills", string.Empty);
                            string[] newVal = lstBoxAvailable.Items[i].Text.Split('_');
                            DataRow[] dr = dt.Select("Skill_x0020_Category='" + newVal[1] + "'");
                            if (dr.Length > 0)
                            {
                                for (int j = 0; j < dr.Length; j++)
                                {
                                    if (lstSkill.Items.Count > 0)
                                    {
                                        ListItem item = lstSkill.Items.FindByText(dr[j]["Prefixvalue"].ToString() + "_" + dr[j]["Title"].ToString());
                                        lstSkill.Items.Remove(item);
                                    }
                                    if (lstSkillSelected.Items.Count > 0)
                                    {
                                        ListItem item = lstSkillSelected.Items.FindByText(dr[j]["Prefixvalue"].ToString() + "_" + dr[j]["Title"].ToString());
                                        lstSkillSelected.Items.Remove(item);
                                    }
                                }
                            }
                        }
                        else if (action == "REMOVESkill")
                        {
                            DataTable dt = GetSPDataTable("Skills", string.Empty);
                            DataRow[] dr = dt.Select("Skill_x0020_Category='" + lstBoxAvailable.Items[i].Value + "'");
                            if (dr.Length > 0)
                            {
                                for (int j = 0; j < dr.Length; j++)
                                {
                                    if (lstSkill.Items.Count > 0)
                                    {
                                        ListItem item = lstSkill.Items.FindByValue(dr[j]["Title"].ToString());
                                        lstSkill.Items.Remove(item);
                                    }
                                    if (lstSkillSelected.Items.Count > 0)
                                    {
                                        ListItem item = lstSkillSelected.Items.FindByValue(dr[j]["Title"].ToString());
                                        lstSkillSelected.Items.Remove(item);
                                    }
                                }
                            }
                        }
                        lstBoxAvailable.Items.Remove(lstBoxAvailable.Items[i]);
                    }
                }
            }
            if (Cascade && action == "ADD")
            {
                BindChildListBox(lstBoxSelected, lstSkill, lstSkillSelected);
            }
        }

public static void BindChildListBox(ListBox lstSkillcategorySelected, ListBox lstSkill, ListBox lstSkillSelected)
        {
            List<string> selectedValueList = new List<string>();
            foreach (ListItem item in lstSkillcategorySelected.Items)
            {
                selectedValueList.Add(item.Value);
            }
            string query = PrepareQuery(selectedValueList);
            DataTable colItems = GetChildSPDataTable("Skills", query);
            if (!String.IsNullOrEmpty(query))
            {
                if (null != colItems && colItems.Rows.Count > 0)
                {
                    foreach (DataRow item in colItems.Rows)
                    {
                        if (lstSkill.Items.FindByText((item["Prefixvalue"] + "_" + item[Constants.TITLE].ToString().Trim())) == null && lstSkillSelected.Items.FindByText((item["Prefixvalue"] + "_" + item[Constants.TITLE].ToString().Trim())) == null)
                        {
                            lstSkill.Items.Add(new ListItem((item["Prefixvalue"] + "_" + item[Constants.TITLE].ToString().Trim()), item[Constants.ID].ToString().Trim()));
                        }
                    }
                }
            }
        }

 protected void btnCountryRemove_Click(object sender, EventArgs e)
        {
            try
            {
                MoveCascadedListBoxItems(lstLandmarksSelected, lstLandmarksAvailable, lstCountryAvailable, llstCountrySelected, true, "REMOVE");
                SortListBox(lstSkillCategory);
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
        }

protected void SortListBox(ListBox lstBox)
        {
            List<ListItem> t = new List<ListItem>();
            Comparison<ListItem> compare = new Comparison<ListItem>(CompareListItems);
            foreach (ListItem lbItem in lstBox.Items)
                t.Add(lbItem);

            t.Sort(compare);
            lstBox.Items.Clear();
            lstBox.Items.AddRange(t.ToArray());
        }

int CompareListItems(ListItem li1, ListItem li2)
        {
            return String.Compare(li1.Text, li2.Text);
        }

 protected void btnLandmarksAdd_Click(object sender, EventArgs e)
        {
            try
            {
                MoveCascadedListBoxItems(lstLandmarksAvailable, lstLandmarksSelected, lstLandmarksAvailable, lstLandmarksSelected, false, "ADD");
                SortListBox(lstLandmarksSelected);
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
        }

 protected void btnLandmarksRemove_Click(object sender, EventArgs e)
        {
            try
            {
                MoveCascadedListBoxItems(lstLandmarksSelected, lstLandmarksAvailable, lstLandmarksAvailable, lstLandmarksSelected, false, "REMOVESkill");
                SortListBox(lstLandmarksAvailable);
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
        }
*prefixValue is a string value that I put to combine two different Texts in List Box. Please ignore it.

** I have used Constants.TITLE for column name to query or bind.

*** PrepareQuery is a method which is used to generate CAML query at run time. I have posted it in another blog and you can navigate to it.

1 comment: