Answered by:
Problem with creating a custom control that uses templates

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