I'm attempting to build a templated control. You'll see that as part of the Field section, I'd like to be able to define controls as children. When I attempt to compile I'm receiving the error message "MyTemplateControl.Field' does not have a public property named 'Button'". Does anyone know how to accomplish what I'm trying to do?
Below you'll find an example XHTML markup, and below that my control implementation.
I've editted my example to hopefully clarify what I'm looking for. Thanks everyone for the MSDN links. I've been through those already.
What I'm trying to build is a data entry control that will auto format a table for me. We do a lot of data entry webforms, and I'd like to ease the implementation time.
<cc1:MyForm ID="MyForm1" runat="server">
<ViewTemplate>
<cc1:Field Question="What is your name?">
<asp:Label ID="myLabel" runat="server" />
</cc1:Field>
</ViewTemplate>
<EditTemplate>
<cc1:Field Question="What is your name?">
<asp:Textbox ID="myTextbox" runat="server" />
</cc1:Field>
</EditTemplate>
</cc1:MyForm>
public class MyForm : WebControl, INamingContainer
{
private FieldCollection _fieldCollection;
private FieldCollection _field2Collection;
public FieldCollection ViewTemplate
{
get
{
if (_fieldCollection == null)
{
_fieldCollection = new FieldCollection();
}
return _fieldCollection;
}
}
public FieldCollection EditTemplate
{
get
{
if (_field2Collection == null)
{
_field2Collection = new FieldCollection();
}
return _field2Collection;
}
}
}
public class FieldCollection : CollectionBase
{
.
.
.
}
[ParseChildren(false)]
public class Field
{
.
.
.
}
There's something strange in your implementation. It's difficult to understand what you'd like to do, either to build a templated control, or a control on which you can add a list of child controls.
For the templated approach you'd have to do something like this:
public class MyForm : WebControl, INamingContainer
{
private TemplateOwner templateOwner{ get; set; }
private ITemplate _Content;
public ITemplate Content
{
get
{
return this._Content;
}
set
{
_Content = value;
}
}
protected override void CreateChildControls()
{
base.Controls.Clear();
templateOwner = new TemplateOwner();
this.Content.InstantiateIn(templateOwner);
this.Controls.Add(templateOwner);
}
...
}
[ToolboxItem(false)]
public class TemplateOwner : WebControl{
public TemplateOwner():base(HtmlTextWriterTag.Div)
{
}
}
The usage would then look like
<cc1:MyForm ID="MyForm1" runat="server">
<Content>
<!-- Place whatever HTML, ASP.net controls etc you like -->
</Content>
</cc1:MyForm>
You still have to add the appropriate annotations to configure the behavior of the designer. I just wrote this down quickly to give you an idea.
GridView also doesn't allow such manipulation,
Instead use MyForm1.FindControl("mybutton")
Also I have notice your template is not derived from ITemplate interface. As usual ASP.Net also uses System.Web.UI.Control as base class for this impl.
I used the following code:
public partial class SmallFramedControl : UserControl, INamingContainer
{
private TemplateOwner templateOwner { get; set; }
private ITemplate _Content;
public ITemplate Content
{
get
{
return this._Content;
}
set
{
_Content = value;
}
}
protected override void CreateChildControls()
{
//base.Controls.Clear();
templateOwner = new TemplateOwner();
this.Content.InstantiateIn(templateOwner);
plchAdd.Controls.Add(templateOwner);
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
[ToolboxItem(false)]
public class TemplateOwner : WebControl
{
public TemplateOwner()
: base(HtmlTextWriterTag.Div)
{
}
}
and ascx page look like:
<%# Control Language="C#" AutoEventWireup="true"
CodeBehind="SmallFramedControl.ascx.cs"
Inherits="OtPortalNewDesign.UserControls.SmallFramedControl" %>
this will be top of frame
<asp:PlaceHolder ID="plchAdd" runat="server"></asp:PlaceHolder>
this will be bottom of frame
Related
Why are my custom control's property setters not getting called by the parent page when using data binding expressions to set the control's properties? The setters are called when using a constant to set the control's properties.
MyPage.aspx:
...
<foo:MyControl ID="bar" runat="server" MyProperty="<%# Thirteen %>" />
...
MyPage.aspx.cs:
...
protected int Thirteen { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Thirteen = 13;
DataBind();
}
}
...
MyControl.ascx.cs:
public partial class MyControl : System.Web.UI.UserControl
{
public int MyProperty
{
get => (int)(ViewState["MyProperty"] ?? 0);
set => ViewState["MyProperty"] = value;
}
}
I set a breakpoint on the setter, and it's never called when I load the page.
However, if I change MyPage.aspx from/to this, it works:
<foo:MyControl ID="bar" runat="server" MyProperty="13" />
Is it possible to have the setter called using the data binding expression? What am I doing wrong?
Embarrassing...
My actual code for MyControl.ascx.cs was more appropriately the following:
public partial class MyControl : System.Web.UI.UserControl
{
public int MyProperty
{
get => (int)(ViewState["MyProperty"] ?? 0);
set => ViewState["MyProperty"] = value;
}
// THIS IS THE KEY
protected override void OnDataBinding(EventArgs e)
{
// bunch of stuff, but never calling base.OnDataBinding(e)
}
}
After adding base.OnDataBinding(e); to the overridden method, everything worked as expected.
I implement a custom control that handle post back event:
namespace CalendarControls
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class CalendarPostback : WebControl, IPostBackEventHandler
{
public delegate void CalendarPostBackHandler(object sender, CalendarPostbackEventArgs e);
public event CalendarPostBackHandler CalendarPostBack;
public CalendarPostback()
{
}
#region IPostBackEventHandler Members
public void RaisePostBackEvent(string eventArgument)
{
string[] values = eventArgument.Split('|');
CalendarPostbackEventArgs args = new CalendarPostbackEventArgs();
args.EventType = (eEventType)Convert.ToInt32(values[0]);
if (args.EventType == eEventType.Insert)
args.EventDate = Convert.ToDateTime(values[1]);
else
args.EventId = Convert.ToInt32(values[1]);
if (CalendarPostBack != null)
CalendarPostBack(this, args);
}
#endregion
}
public class CalendarPostbackEventArgs : EventArgs
{
public CalendarPostbackEventArgs()
{
}
public eEventType EventType
{
get;
set;
}
public DateTime EventDate
{
get;
set;
}
public int? EventId
{
get;
set;
}
}
}
and I use this custom control in a usercontrol ,and call it on a button click inside my usercontrol with following javascript code:
function CallPostBack(eventArguments) {
__doPostBack('<%=ctl1.ClientID %>', eventArguments);
}
and my html code in usercontrol:
<input type="button" value="Insert" onclick="CallPostBack('1|2014/06/12')" />
<CalendarControls:CalendarPostback ID="ctl1" runat="server" ClientIDMode="Static" OnCalendarPostBack="ctl1_CalendarPostBack" />
but when I click on my button, page postback occur but ctl1_CalendarPostBack event not fired. also I must to say that I add user control directly to aspx page (not dynamically) and my aspx page have a master page.
now I have two question:
what's wrong with my code? how to solve this issue?
if I want to add my custom control to an update panel and I want to have an async post back, what I must do?
thank you,
best regards
I found problem! when we use master page ,client id and unique id of control is different and we must to use unique id instead of client id:
function CallPostBack(eventArguments) {
__doPostBack('<%=ctl1.UniqueID %>', eventArguments);
}
I need to write a user control that can be used with the following syntax:
<quiz:Question runat="server">
<Answer>Foo</Answer>
<Answer>Bar</Answer>
</quiz:Question>
I tried the following property declaration:
[ParseChildren(true, "Answer")]
public class Question : UserControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public string[] Answer { get; set; }
}
But then the Visual Studio editor insist that <Answers > should be self-closing and I get this exception if I decide otherwise:
Literal content ('Foo') is not allowed within a 'System.String[]'.
I have been looking at <asp:DropDownList> in Reflector, which inherits from ListControl which declares the Items property as follows:
ParseChildren(true, "Items")
public abstract class ListControl
{
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public virtual ListItemCollection Items { get; }
}
It's not really the same as what I want, because in DropDownList you must add <asp:ListItem> as children. And there are some things I don't understand about control design, which is currently preventing me from finding the solution:
Why doesn't the <asp:ListItem> tag require a runat="server" attribute?
Can I declare such a 'control'?
What's so special about the ListItemCollection that it translates to this particular syntax?
What code can I write that will translate to the syntax given in the first code example above?
I mixed behavior of controls those can have child items (like ListControl), with controls like Panel (ParseChildren=false) :
[ParseChildren(true, "Answers")]
public class Question : WebControl, INamingContainer
{
private AnswerCollection answers;
public virtual AnswerCollection Answers
{
get
{
if (this.answers == null)
{
this.answers = new AnswerCollection();
}
return this.answers;
}
}
public override void RenderControl(HtmlTextWriter writer)
{
//access to each answer value
foreach (var a in this.Answers)
writer.WriteLine(((LiteralControl)a.Controls[0]).Text + "<br/>");
}
}
[ParseChildren(false), PersistChildren(true)]
public class Answer : Control
{
}
public class AnswerCollection : List<Answer>
{
}
Then you would be able to have something like :
<cc1:Question runat="server">
<cc1:Answer>Answer1</cc1:Answer>
<cc1:Answer>Answer2</cc1:Answer>
</cc1:Question>
Hope it helps
I'm attempting to set a class on the body tag in my asp.net site which uses a master page and content web forms. I simply want to be able to do this by adding a bodycssclass property (see below) to the content web form page directive.
It works through the solution below but when i attempt to view Default.aspx the Content1 control loses its content. Any ideas why?
Here is how I'm doing it. I have a master page with the following content:
<%# Master Language="C#" ... %>
<html><head>...</head>
<body id=ctlBody runat=server>
<asp:ContentPlaceHolder ID="cphMain" runat="server" />
</body>
</html>
it's code behind looks like:
public partial class Site : MasterPageBase
{
public override string BodyCssClass
{
get { return ctlBody.Attributes["class"]; }
set { ctlBody.Attributes["class"] = value; }
}
}
it inherits from:
public abstract class MasterPageBase : MasterPage
{
public abstract string BodyCssClass
{
get;
set;
}
}
my default.aspx is defined as:
<%# Page Title="..." [master page definition etc..] bodycssclass="home" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" runat="server">
Some content
</asp:Content>
the code behind for this file looks like:
public partial class Default : PageBase { ... }
and it inherits from :
public class PageBase : Page
{
public string BodyCssClass
{
get
{
MasterPageBase mpbCurrent = this.Master as MasterPageBase;
return mpbCurrent.BodyCssClass;
}
set
{
MasterPageBase mpbCurrent = this.Master as MasterPageBase;
mpbCurrent.BodyCssClass = value;
}
}
}
This works for me now...
public class PageBase : Page
{
public string BodyCssClass
{
get;
set;
}
protected override void OnPreInit(EventArgs e)
{
MasterPageBase mpbCurrent = this.Master as MasterPageBase;
mpbCurrent.BodyCssClass = BodyCssClass;
base.OnLoadComplete(e);
}
}
Have you tried adding the MasterType directive to your content page? Like so:
I recommend doing that anyway. Let's see if that helps you...
See http://msdn.microsoft.com/en-us/library/ms228274.aspx
and http://msdn.microsoft.com/en-us/library/c8y19k6h.aspx
I can't seem to find any examples on how to implement ITemplates with multiple instances. All I need to do is build out a navigation that has a template for the content of each navigation item.
If your control is not a databound control, you can solve the problem by something as follows. But, I haven't tested it.
public class FooItem : WebControl, INamingContainer
{
protected override CreateChildControls()
{
Control placeHolder = this.FindControl(((Foo)this.Parent).ItemPlaceHolderId);
if (placeHolder != null)
{
// Add some controls to the placeHolder.
}
}
}
public class Foo : WebControl, INamingContainer
{
public ITemplate ItemTemplate { get; set; }
public string ItemPlaceHolderId
{
get { ... }
set { ... }
}
public FooItemCollection Items
{
get { ... }
}
protected override CreateChildControls()
{
foreach (FooItem item in this.Items)
{
this.ItemTemplate.InstantiateIn(item);
}
}
}
Look at documentation of creating a data-bound templated control.
Unfortunately, the best documentation I've found is from .NET 1.1:
Developing a Templated Data-Bound Control.
Note from MSDN:
This TemplateInstanceAttribute class
is optional. If a template property is
not extended with a
TemplateInstanceAttribute class, the
default value, the Multiple field, is
used.
So any ITemplate example that does not use the TemplateInstanceAttribute is using TemplateInstance.Multiple.