locked
Problem with creating a custom control that uses templates RRS feed

  • Question

  • User-1753546938 posted

    I am trying to refactor my context menu control to use ITemplate.

    This is what I have so far in my codebehind:

    [ParseChildren(true, "MenuItems"), PersistChildren(false)] 
    public partial class CustomControls_ContextMenu : System.Web.UI.UserControl
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public MenuItemsList MenuItems { get; set; }
        
        protected void Page_Load(object sender, EventArgs e)
        {
            MenuItems = new MenuItemsList();
        }
    }
    
    protected override void RenderChildren(HtmlTextWriter writer) 
    { 
            foreach (MenuItem item in MenuItems) 
            { 
                item.InstantiateIn(MenuPnl); 
            } 
            
            base.RenderChildren(writer); 
    } 
    
        public class MenuItemsList : Collection<MenuItem> { }
        
        public class MenuItem : ITemplate 
        { 
            public string Text { get; set; } 
            public EventHandler OnClick { get; set; }
            public string URL { get; set; } 
            
            public void InstantiateIn(Control container) 
            { 
                //var btn1 = new HtmlGenericControl("button"); 
                //btn1.InnerText = this.Text; container.Controls.Add(btn1);
    
                if (string.IsNullOrEmpty(Text))
                    throw new ArgumentNullException("Text can not be null or empty");
    
                Unit menuWidth = GetMenuWidth(Text);
    
                Button btn = new Button() { ID = Text + "Btn", Text = this.Text };
    
                btn.Style.Add(HtmlTextWriterStyle.BackgroundColor, "transparent");
                btn.Style.Add(HtmlTextWriterStyle.BorderColor, "transparent");
                btn.Style.Add(HtmlTextWriterStyle.TextAlign, "left");
                btn.Style.Add(HtmlTextWriterStyle.FontFamily, "tahoma");
                btn.Style.Add(HtmlTextWriterStyle.Width, "100%");
    
                btn.Attributes.Add("onmouseover", "this.style.backgroundColor='#316AC5'");
                btn.Attributes.Add("onmouseout", "this.style.backgroundColor='white'");
    
                if (OnClick != null)
                    btn.Click += new EventHandler(OnClick);
    
                if (!string.IsNullOrEmpty(URL))
                    btn.Attributes.Add("OnClick", "javascript:return window.open('" + URL + "')");
    
                container.Controls.Add(btn);
    
                if (((Panel)container).Width.Value < menuWidth.Value)
                {
                    ((Panel)container).Width = menuWidth;
                }
    
            }
     
    And my markup contains a Panel:
     
    <asp:Panel ID="MenuPnl" BackColor="White" BorderColor="ActiveBorder" BorderWidth="1px" Width="100px" CssClass="context_menu_hide" runat="server">
    </asp:Panel>

    When I try to add my MenuItem under MenuItems I get the error: CustomControls_ContextMenu+MenuItemsList must have items of type 'CustomControls_ContextMenu+MenuItem'. 'MenuItem' is of type 'System.Web.UI.HtmlControls.HtmlGenericControl'.

    <ui:ContextMenu ID="NavMenu" TargetControl="ContentDiv" runat="server" >
                <MenuItems>
                    <MenuItem Text="Item Request" URL="~/ItemRequest.aspx" OnClick="ProcessItem_OnClick" />
                </MenuItems>
    </ui:ContextMenu>
     
    I'm at a loss here. Any ideas?
    Friday, November 11, 2011 12:46 PM

Answers

  • User-691759321 posted

    I am not sure exactly what the issue is with the supplied code but it seems to work for me when I converted it to VB.NET and made some minor modifications.  First this I did was remove the need for a TargetControlID.  I simply render the surrounding Panel as part of the ContextMenu control.  Next, I added the MenuItems in the CreateChildControls method and removed the RenderChildren method.  You helped answer a question I have been trying to find for months so I hope this code leads you to your answer:

    <ParseChildren(True, "MenuItems"), PersistChildren(False)> _
        Partial Public Class ContextMenu
            Inherits CompositeControl
    
            <PersistenceMode(PersistenceMode.InnerProperty)> Public property MenuItems As MenuItemsList
                Get
                    If MyBase.ViewState("MenuItems") Is Nothing Then
                        Return New MenuItemsList
                    End If
                    Return MyBase.ViewState("MenuItems")
                End Get
                Set(value As MenuItemsList)
                    MyBase.ViewState("MenuItems") = value
                End Set
            End Property
    
            Protected Overrides Sub CreateChildControls()
                Dim ContextPanel As New Panel
                ContextPanel.ID = Me.ID
    
                For Each Item As MenuItem In Me.MenuItems
                    Item.InstantiateIn(ContextPanel)
                Next
    
                Controls.Add(ContextPanel)
            End Sub
        End Class
    
        Public Class MenuItemsList
            Inherits List(Of MenuItem)
        End Class
    
        Public Class MenuItem
            Implements ITemplate
    
            Public Text As String
            Public URL As String
            Public Event OnClick As EventHandler
    
            Public Sub InstantiateIn(ByVal container As Control) Implements ITemplate.InstantiateIn
                If String.IsNullOrEmpty(Text) Then
                    Throw New ArgumentNullException("Text can not be null or empty")
                End If
    
                Dim MyButton As New Button
                MyButton.ID = Me.Text + "Btn"
                MyButton.Text = Me.Text
                MyButton.Style.Add(HtmlTextWriterStyle.BackgroundColor, "transparent")
                MyButton.Style.Add(HtmlTextWriterStyle.BorderColor, "transparent")
                MyButton.Style.Add(HtmlTextWriterStyle.TextAlign, "left")
                MyButton.Style.Add(HtmlTextWriterStyle.FontFamily, "tahoma")
                MyButton.Style.Add(HtmlTextWriterStyle.Width, "100%")
                MyButton.Attributes.Add("onmouseover", "this.style.backgroundColor='#316AC5'")
                MyButton.Attributes.Add("onmouseout", "this.style.backgroundColor='white'")
                AddHandler MyButton.Click, AddressOf ContextMenuItemClick
    
                If Not String.IsNullOrEmpty(URL) Then
                    MyButton.Attributes.Add("onclick", "javascript:return window.open('" + URL + "')")
                End If
                container.Controls.Add(MyButton)
            End Sub
    
            Protected Overridable Sub ContextMenuItemClick(ByVal sender As Object, ByVal e As EventArgs)
                RaiseEvent OnClick(Me, e)
            End Sub
        End Class
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, November 11, 2011 2:42 PM

All replies

  • User-691759321 posted

    I am not sure exactly what the issue is with the supplied code but it seems to work for me when I converted it to VB.NET and made some minor modifications.  First this I did was remove the need for a TargetControlID.  I simply render the surrounding Panel as part of the ContextMenu control.  Next, I added the MenuItems in the CreateChildControls method and removed the RenderChildren method.  You helped answer a question I have been trying to find for months so I hope this code leads you to your answer:

    <ParseChildren(True, "MenuItems"), PersistChildren(False)> _
        Partial Public Class ContextMenu
            Inherits CompositeControl
    
            <PersistenceMode(PersistenceMode.InnerProperty)> Public property MenuItems As MenuItemsList
                Get
                    If MyBase.ViewState("MenuItems") Is Nothing Then
                        Return New MenuItemsList
                    End If
                    Return MyBase.ViewState("MenuItems")
                End Get
                Set(value As MenuItemsList)
                    MyBase.ViewState("MenuItems") = value
                End Set
            End Property
    
            Protected Overrides Sub CreateChildControls()
                Dim ContextPanel As New Panel
                ContextPanel.ID = Me.ID
    
                For Each Item As MenuItem In Me.MenuItems
                    Item.InstantiateIn(ContextPanel)
                Next
    
                Controls.Add(ContextPanel)
            End Sub
        End Class
    
        Public Class MenuItemsList
            Inherits List(Of MenuItem)
        End Class
    
        Public Class MenuItem
            Implements ITemplate
    
            Public Text As String
            Public URL As String
            Public Event OnClick As EventHandler
    
            Public Sub InstantiateIn(ByVal container As Control) Implements ITemplate.InstantiateIn
                If String.IsNullOrEmpty(Text) Then
                    Throw New ArgumentNullException("Text can not be null or empty")
                End If
    
                Dim MyButton As New Button
                MyButton.ID = Me.Text + "Btn"
                MyButton.Text = Me.Text
                MyButton.Style.Add(HtmlTextWriterStyle.BackgroundColor, "transparent")
                MyButton.Style.Add(HtmlTextWriterStyle.BorderColor, "transparent")
                MyButton.Style.Add(HtmlTextWriterStyle.TextAlign, "left")
                MyButton.Style.Add(HtmlTextWriterStyle.FontFamily, "tahoma")
                MyButton.Style.Add(HtmlTextWriterStyle.Width, "100%")
                MyButton.Attributes.Add("onmouseover", "this.style.backgroundColor='#316AC5'")
                MyButton.Attributes.Add("onmouseout", "this.style.backgroundColor='white'")
                AddHandler MyButton.Click, AddressOf ContextMenuItemClick
    
                If Not String.IsNullOrEmpty(URL) Then
                    MyButton.Attributes.Add("onclick", "javascript:return window.open('" + URL + "')")
                End If
                container.Controls.Add(MyButton)
            End Sub
    
            Protected Overridable Sub ContextMenuItemClick(ByVal sender As Object, ByVal e As EventArgs)
                RaiseEvent OnClick(Me, e)
            End Sub
        End Class
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, November 11, 2011 2:42 PM
  • User3866881 posted

    Hello Kenshinofkin:)

    Have you tried "ddelella"'s solution? and if you needs a version of C# codes, plz use this tool to convert them with:

    http://www.developerfusion.com/tools/convert/vb-to-csharp/

    And the codes below is just what I've helped you to translate from VB.net to C#:)

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    [ParseChildren(true, "MenuItems"), PersistChildren(false)]
    public partial class ContextMenu : CompositeControl
    {

    [PersistenceMode(PersistenceMode.InnerProperty)]
    public MenuItemsList MenuItems {
    get {
    if (base.ViewState("MenuItems") == null) {
    return new MenuItemsList();
    }
    return base.ViewState("MenuItems");
    }
    set { base.ViewState("MenuItems") = value; }
    }

    protected override void CreateChildControls()
    {
    Panel ContextPanel = new Panel();
    ContextPanel.ID = this.ID;

    foreach (MenuItem Item in this.MenuItems) {
    Item.InstantiateIn(ContextPanel);
    }

    Controls.Add(ContextPanel);
    }
    }

    public class MenuItemsList : List<MenuItem>
    {
    }

    public class MenuItem : ITemplate
    {

    public string Text;
    public string URL;
    public event EventHandler OnClick;

    public void InstantiateIn(Control container)
    {
    if (string.IsNullOrEmpty(Text)) {
    throw new ArgumentNullException("Text can not be null or empty");
    }

    Button MyButton = new Button();
    MyButton.ID = this.Text + "Btn";
    MyButton.Text = this.Text;
    MyButton.Style.Add(HtmlTextWriterStyle.BackgroundColor, "transparent");
    MyButton.Style.Add(HtmlTextWriterStyle.BorderColor, "transparent");
    MyButton.Style.Add(HtmlTextWriterStyle.TextAlign, "left");
    MyButton.Style.Add(HtmlTextWriterStyle.FontFamily, "tahoma");
    MyButton.Style.Add(HtmlTextWriterStyle.Width, "100%");
    MyButton.Attributes.Add("onmouseover", "this.style.backgroundColor='#316AC5'");
    MyButton.Attributes.Add("onmouseout", "this.style.backgroundColor='white'");
    MyButton.Click += ContextMenuItemClick;

    if (!string.IsNullOrEmpty(URL)) {
    MyButton.Attributes.Add("onclick", "javascript:return window.open('" + URL + "')");
    }
    container.Controls.Add(MyButton);
    }

    protected virtual void ContextMenuItemClick(object sender, EventArgs e)
    {
    if (OnClick != null) {
    OnClick(this, e);
    }
    }
    }

    Saturday, November 12, 2011 7:49 PM