Tuesday, May 3, 2011

How to create ASP.NET user/server control that uses a list of asp:ListItem as child controls?

I am looking to create a user/server control that will be created with something like the following:

<my:MyListControl runat="server">
   <asp:ListItem Text="Test1" Value="Test1" />
   <asp:ListItem Text="Test2" Value="Test2" />
</my:MyListControl>

I am just looking for a start here: Articles or code samples.

What base class should I inherit from? What to override?

Possibly how to customize what sub items my control accepts (my:ListItem instead of asp:ListItem).

What I am looking to do is create a very simple bread crumb control for a small section of my site. I have it all working with stock ASP.NET controls, but the items are added in code, which means fixing a spelling mistake or formatting bug involves a recompile, which is not ideal.

EDIT:

Here's my code with the addition of Josh's suggestion below:

Namespace MySite.Controls Partial Class BreadCrumbs Inherits UserControl

    Private m_BreadCrumbs As New List(Of BreadCrumbItem)

    <PersistenceMode(PersistenceMode.InnerProperty)> _
    Public Property Items() As List(Of BreadCrumbItem)
        Get
            Return m_BreadCrumbs
        End Get
        Set(ByVal value As List(Of BreadCrumbItem))
            m_BreadCrumbs = value
        End Set
    End Property

    Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        Bind()
    End Sub

    Private Sub Bind()
        lvCrumbs.DataSource = Items
        Me.DataBind()
    End Sub
End Class

Public Class BreadCrumbItem
    Private m_Text As String
    Public Property Text() As String
        Get
            Return m_Text
        End Get
        Set(ByVal value As String)
            m_Text = value
        End Set
    End Property

    Private m_Url As String
    Public Property Url() As String
        Get
            Return m_Url
        End Get
        Set(ByVal value As String)
            m_Url = value
        End Set
    End Property
End Class

End Namespace

Then my page code looks like this:

<%@ Page Language="VB" AutoEventWireup="false" Inherits="MySite.MyPage" Title="My Page" Codebehind="MyPage.aspx.vb" %>
<%@ Register TagPrefix="my" TagName="BreadCrumbs" Src="~/BreadCrumbs.ascx" %>
<%@ Register TagPrefix="my" Namespace="MySite.Controls" Assembly="MySite" %>
<rc:BreadCrumbs ID="breadcrumbs" runat="server">
 <Items>
  <my:BreadCrumbItem Text="Another page" Url="AnotherPage.aspx" />
 </Items>
</rc:BreadCrumbs>
From stackoverflow
  • You can add a property on a user control's code behind like:

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public List<ListItem> Items
    {
        get;
        set;
    }
    

    Your markup would then be:

    <my:MyListControl runat="server">
      <Items>
        <asp:ListItem/>
      </Items>
    </my:myListControl>
    

    To make it so ListItem can be your own list item (Which I recommend doing as opposed to using asp.net.) You'll want to create your own class.

    Here's an example of a Server Control I use (I removed a lot of the noise as such this is just a skeleton):

       [ToolboxData("<{0}:Menubar runat=server></{0}:Menubar>")]
        [System.ComponentModel.DesignTimeVisible(false)]
        public class Menubar : WebControl, IPostBackEventHandler
        {
    
            private List<MenuItem> _menuItems = new List<MenuItem>();
            [PersistenceMode(PersistenceMode.InnerProperty)]
            public List<MenuItem> MenuItems
            {
                get
                {
                    return _menuItems;
                }
            }
    
        }
        [ToolboxItem(false)]
        [ParseChildren(true, "MenuItems")]
        public class MenuItem
        {
            private string _clientClick;
            private List<MenuItem> _menuItems = new List<MenuItem>();
    
            [Localizable(true)]
            public string Title { get; set; }
            public string Href { get; set; }
            public string Id { get; set; }
            [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
            public List<MenuItem> MenuItems
            {
                get { return _menuItems; }
                set { _menuItems = value; }
            }
        }
    

    Now I can use this like:

    <my:Menubar runat="server" ID="menuBar">
        <MenuItems>
            <my:MenuItem Title="Save" Href="javascript:saveItem(this);"  />
            <my:MenuItem Title="Print" Href="javascript:void(0);">
                <MenuItems>
                    <my:MenuItem Title="Preview" Href=""/>
                    <my:MenuItem Title="To Pdf" Href="javascript:"/>
                </MenuItems>
            </my:MenuItem>
        </MenuItems>
    </my:Menubar>
    
    slolife : This appears to be what I need. My only hang up now is why my @Register TagPrefix="my" Namespace="MySite.Controls" doesn't work to get the custom list item class? Any ideas? It gets the parent control (Menubar in your example) but I cannot do my:MyListItem inside the Items tag.
    JoshBerke : No ideas can you share the code you got?
    slolife : Never mind. Needed to add the Assembly param to the @Register directive. Now it works. Thanks Josh
    JoshBerke : Sweet glad it works;-)
  • Use an asp:Repeater then bind it to a List of the objects you want in your breadcrumb. You can use the template to customize the items themselves and the separators.

    slolife : I wanted something a little more defined than the repeater control
  • This is the only online resource I've been able to track down that tackles this issue. I tried integrating a namespace in my ascx file and it seemed okay.

        <my:webform id="webform1">
           <fields>
               <my:formfield dbname="first_name" required="false" />
           </fields>
        </my:webform>
    

    where formfield is saved just like menuitem in the menubar. however i get a an error: object reference not set to instance of object pointing to the my:formfield which is a public class FormField in my namespace, just like menuitem, i can't seem to trace the source of that error. help, if you can please.

0 comments:

Post a Comment