asp.net Web server control with child controls, event not firing - asp.net

I have a simple web control (TaskList) that can have children (Task) which inherit from LinkButton, that can be added declaratively or programatically. This works ok, but I can't get the onclick event of a Task to be fired in my code behind. The code ..
[ToolboxData("<{0}:TaskList runat=\"server\"> </{0}:TaskList>")]
[ParseChildren(true)]
[PersistChildren(false)]
public class TaskList : System.Web.UI.Control
{
//[DefaultProperty("Text")]
public TaskList()
{}
private List<Task> _taskList = new List<Task>();
private string _taskHeading = "";
public string Heading
{
get
{
return this._taskHeading;
}
set
{
this._taskHeading = value;
}
}
[NotifyParentProperty(true)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Task> Tasks
{
get
{
return this._taskList;
}
set
{
this._taskList = value;
}
}
protected override void CreateChildControls()
{
foreach (Task task in this._taskList)
this.Controls.Add(task);
base.CreateChildControls();
}
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<h2>" + this._taskHeading + "</h2>");
writer.Write("<div class='tasks_container'>");
writer.Write("<div class='tasks_list'>");
writer.Write("<ul>");
foreach (Task task in this._taskList)
{
writer.Write("<li>");
task.RenderControl(writer);
writer.Write("</li>");
}
writer.Write("</ul>");
writer.Write("</div>");
writer.Write("</div>");
}
}
public class Task : LinkButton
{
private string _key = "";
public string Key
{
get
{
return this._key;
}
set
{
this._key = value;
}
}
}
Markup:
<rf:TaskList runat="server" ID="tskList" Heading="Tasks">
<Tasks>
<rf:Task Key="ba" ID="L1" Text="Helllo" OnClick="task1_Click" runat="server" />
</Tasks>
</rf:TaskList>
The Onclick event task1_Click never fires when clicked (although a postback occurs).

TaskList needed to implement INamingContainer to correctly route the events of each Task.

You could also inherit from CompositeControl, which implements INamingContainer for you, instead of inheriting from Control.

Have you thought about INamingContainer.

Related

Getting data back from composite control added to placeholder on postback

I'm trying to create a factory pattern based on the CompositeControl class.
Everything is working fine. Except as I understand it I have to add the control back onto the placeholder control on my actual page. I've searched around for examples and got one to almost work expect it only worked on the first postback.
I've created a sample here and I'll post all the code here.
Code on my webpage, the most important here is I guess on OnInit where I'm trying to add the control back onto the placeholder, I guess this is what I might be doing wrong.
using SampleControls;
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Setup First Control
Session.Clear();
BaseCompositeControl ltb = new LabelTextBox();
PlaceHolder1.Controls.Add(ltb);
Session.Add("Control", ltb);
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// Check if the post back and recreate the control
if (IsPostBack)
{
int c = this.Form.Controls.Count;
for (int i = 0; i < Session.Count; i++)
{
if (Session[i].ToString().Contains("Control"))
{
this.PlaceHolder1.Controls.Add((BaseCompositeControl)(Session[i]));
}
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
//Get Postback Date
BaseCompositeControl oltb = (BaseCompositeControl)this.PlaceHolder1.Controls[0];
lblPstBck.Text = oltb.Text;
this.PlaceHolder1.Controls.Clear();
Session.Clear();
//Load next Control
BaseCompositeControl ltb = new LabelCheckBox();
PlaceHolder1.Controls.Add(ltb);
Session.Add("Control", ltb);
}
}
This is composite control classes, I don't know if I have to do something here to handle the viewstate or how?
public abstract class BaseCompositeControl : CompositeControl
{
protected string _Title;
public abstract string Text
{
get;
set;
}
public string Title
{
get { EnsureChildControls();
return _Title; }
set { EnsureChildControls();
_Title = value; }
}
protected override void CreateChildControls()
{
// Clears child controls
Controls.Clear();
// Build the control tree
CreateControlHierarchy();
ClearChildViewState();
}
protected abstract void CreateControlHierarchy();
}
TextBox control
public class LabelCheckBox : BaseCompositeControl
{
protected CheckBox _CheckBox;
public override string Text
{
get
{
EnsureChildControls();
return _CheckBox.Checked.ToString(); ;
}
set
{
EnsureChildControls();
_CheckBox.Checked = Convert.ToBoolean(value);
}
}
protected override void CreateControlHierarchy()
{
_CheckBox = new CheckBox();
Label l = new Label();
// Configure controls
l.Text = "Second Control";
// Connect to the parent
Controls.Add(l);
Controls.Add(_CheckBox);
}
}
Checkbox control
public class LabelTextBox : BaseCompositeControl
{
protected TextBox _Text;
public override string Text
{
get
{
EnsureChildControls();
return _Text.Text;
}
set
{
EnsureChildControls();
_Text.Text = value;
}
}
protected override void CreateControlHierarchy()
{
Label l = new Label();
_Text = new TextBox();
// Configure controls
l.Text = "First Control";
// Connect to the parent
Controls.Add(l);
Controls.Add(_Text);
}
}
I asked this a long time ago,
But I think this is how you should do it. Using a factory pattern for user controls.
http://weblogs.asp.net/sfeldman/archive/2007/12/17/factory-pattern-for-user-controls.aspx

Expose nested HTML property on asp.net UserControl

I have an asp.net UserControl that writes HTML content from a SharePoint list to a Literal control. Currently I insert a title in h5 tags before each SharePoint list item I write to the Literal. Instead of having the title hardcoded to be placed in h5 tags, I'd like to expose a public property of my user control that lets me define the html format for the title. This is a little different from the templated user control questions I've found so many of because it's not really a template for the user control. I just need a string containing html. Here's what I'm looking to do:
public class MyUserControl: UserControl
{
public string TitleFormat { get; set; }
private void ShowContent()
{
...
string output = String.Format(TitleFormat, title) + someContent;
ltlOutput.Text = output.
}
}
In markup:
<UC:MyUserControl id="muc1" runat="server">
<TitleFormat>
<h3>{0}</h3>
</TitleFormat>
</UC:MyUserControl>
How can I set this up?
Here is the answer (provided by Decker Dong in the asp.net forums):
To nest another class into one, you have to declare a new property but
just declare it is an InnerProperty. And set its Design properties.
Now here's a full sample for you:
[ParseChildren(true),PersistChildren(false)]
public partial class MyUserControl : System.Web.UI.UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public string TitleFormat
{
get;
set;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
With these attributes you can use the control as written in the question.
What you need is http://msdn.microsoft.com/en-us/library/system.web.ui.itemplate.aspx
HTML
<test:NamingControl runat="server" ID="NamingControl" TitleFormat="This is myTitle">
<TitleFormatTemplate>
My title is <%# Container.TitleFormat %>
</TitleFormatTemplate>
</test:NamingControl>
UserControl
public partial class MyUserControl : System.Web.UI.UserControl
{
private ITemplate template;
protected void Page_Load(object sender, EventArgs e)
{
}
public string TitleFormat
{
get;
set;
}
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(TitleFormatTemplate))]
public ITemplate TitleFormatTemplate
{
get { return template; }
set { template = value; }
}
protected override void CreateChildControls()
{
base.CreateChildControls();
TitleFormatTemplate t = new TitleFormatTemplate();
t.TitleFormat = this.TitleFormat;
template.InstantiateIn(t);
this.Controls.Add(t);
this.DataBind();
}
}
Child Control - INamingContainer
public class TitleFormatTemplate : Control, INamingContainer
{
private string _TitleFormat = "";
public string TitleFormat
{
get { return _TitleFormat; }
set { _TitleFormat = value; }
}
}
The simpler approach - No more TitleFormat tag
MyUserControl.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="MyUserControl.ascx.cs"
Inherits="testgingweb.usrcontrols.MyUserControl" %>
<h3><asp:Label runat="server" ID="PassedValueLabel"></asp:Label</h3>
The Codebehind - MyUserControl.ascx.cs
public string TitleFormat
{
get { return ViewState["TitleFormat"]; }
set { ViewState["TitleFormat"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
PassedValueLabel.Text = String.Format("Whatever {0} here", this.TitleFormat);
}
HTML
<test:MyUserContorl runat="server" ID="NamingControl" TitleFormat="This is myTitle">
</test:MyUserContorl>
Notice that I didn't have the TitleFormat tag anymore.

Hide FooterTemplate for Asp repeater

I need advices! So I have a custom repeater where I write the footer in the Custom control here is the code :
[ParseChildren(true)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[DefaultProperty("Text")]
[ToolboxData("<{0}:CustomRepeater runat=server></{0}:CustomRepeater>")]
public class CustomRepeater : Repeater
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty),
TemplateContainer(typeof(GuessFooterTemplate))]
private ITemplate FooterTemp {get; set;}
protected override void InitializeItem(RepeaterItem item)
{
base.InitializeItem(item);
FooterTemp = new GFooterTemplate();
this.FooterTemplate = FooterTemp;
}
//Here I try to hide the footer template
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public override ITemplate FooterTemplate
{
get
{
return base.FooterTemplate;
}
set
{
base.FooterTemplate = value;
}
}
}
internal class GFooterTemplate : ITemplate
{
PlaceHolder ph = new PlaceHolder();
public void InstantiateIn(Control Container)
{
ph.Controls.Add(new LiteralControl("Test for Footertemplate"));
Container.Controls.Add(ph);
}
}
So as the footer is writing programmatically, I would like to hide the FooterTemplate for somebody who use the custom repeater.. I was thinking [PersistenceMode(PersistenceMode.InnerDefaultProperty)] will do that but apparently not.. Have somebody an idea?
Thanks a lot
If I understand correctly, you don't want the users of your CustomRepeater to be able to modify the FooterTemplate...
I see 2 possible solutions:
Use the new modifier on your property to completely redefine it:
private new ITemplate FooterTemplate
{
get { return base.FooterTemplate; }
set { base.FooterTemplate = value; }
}
Or modify the setter to do nothing, thus preventing the modification:
public override ITemplate FooterTemplate
{
get
{
return base.FooterTemplate;
}
set
{
/* base.FooterTemplate = value; */
}
}
HTH

ASP.NET custom control, can template fields have attributes?

For example:
<uc:AdmiralAckbar runat="server" id="myCustomControl">
<Warning SomeAttribute="It's A Trap">
My Data
</Warning>
</uc:AdmiralAckbar>
I'm not sure how to add SomeAttribute. Any ideas?
Code without the attribute is:
private ITemplate warning = null;
[TemplateContainer(typeof(INamingContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate Warning
{
get
{
return warning;
}
set
{
warning = value;
}
}
The answer is yes.
For this you should create a type which implements ITemplate interface and add a custom property/properties there (I added property Name in my example); also add a class which inherits from Collection<YourTemplate>.
Here is an example of doing that:
public class TemplateList : Collection<TemplateItem> { }
public class TemplateItem : ITemplate
{
public string Name { get; set; }
public void InstantiateIn(Control container)
{
var div = new HtmlGenericControl("div");
div.InnerText = this.Name;
container.Controls.Add(div);
}
}
and a control itself:
[ParseChildren(true, "Templates"), PersistChildren(false)]
public class TemplateLibrary : Control
{
public TemplateLibrary()
{
Templates = new TemplateList();
}
[PersistenceMode(PersistenceMode.InnerProperty)]
public TemplateList Templates { get; set; }
protected override void RenderChildren(HtmlTextWriter writer)
{
foreach (var item in Templates)
{
item.InstantiateIn(this);
}
base.RenderChildren(writer);
}
}
and finally an example of usage:
<my:TemplateLibrary runat="server">
<my:TemplateItem Name="hello" />
<my:TemplateItem Name="there" />
</my:TemplateLibrary>
BTW, you could also use it as:
<my:TemplateLibrary runat="server">
<Templates>
<my:TemplateItem Name="hello" />
<my:TemplateItem Name="there" />
</Templates>
</my:TemplateLibrary>
the effect will be the same.

how to access a label form user control in Parent class?

I have a class UserControlBase that inherits System.Web.UI.UserControl and my user controls inherit UserControlBase class. UserControlBase has some common functions that are used in all user controls.
I want to put error display function to UserControlBase as well so that I may not have to declare and manage it in all user controls. Error will be displayed in some label in usercontrol. Issue is how to access label which is in usercontrol in UserControlBase in function ? I don't want to pass label as argument.
In your UserControl Base, expose the text value of the label only:
public abstract class UserControlBase : System.Web.UI.UserControl
{
private Label ErrorLabel { get; set; }
protected string ErrorMessage
{
get { return ErrorLabel.Text; }
set { ErrorLabel.Text = value; }
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ErrorLabel = new Label();
Controls.Add(ErrorLabel);
}
//... Other functions
}
In your user controls that inherit this:
public partial class WebUserControl1 : UserControlBase
{
protected void Page_Load(object sender, EventArgs e)
{
try
{
}
catch (Exception)
{
ErrorMessage = "Error"; //Or whatever
}
}
}

Resources