I've found code examples that demonstrate templated user controls. They work fine for displaying data, but I can't get them to work for entering data. I have a TextBox in my template, and can change the value in it, but I can't get that changed value in my code. I attempt to get the user control's values in a button click handler, but I always get just the initial value. Also, when I click the button, the templated user control's value(s) are reset. Is data entry supposed to work with templated user controls?
My code is similar to the MSDN topic "How to: Create Templated ASP.NET User Controls".
Default.aspx ===============================
<%# Page Language="C#" %>
<%# Register TagPrefix="uc" tagname="TemplateTest" Src="TemplatedUC.ascx" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<script runat="server">
protected void Page_Load()
{
DataBind();
}
protected void ButtonShowContent_Click(object sender, EventArgs e)
{
ContentLabel.Text = string.Format("Index: {0} - Message: {1}<br>", TemplateTest1.Index, TemplateTest1.Message);
}
</script>
<head runat="server">
<title>Templated User Control Test</title>
</head>
<body>
<h1>Testing Templated User Control</h1>
<form id="Form2" runat="server">
<uc:TemplateTest ID="TemplateTest1" runat="server">
<MessageTemplate>
Index: <asp:Label runat="server" ID="Label1" Text='<%# Container.Index %>' /><br />
Message: <asp:TextBox runat="server" ID="Label2" Text='<%# Container.Message %>' /><br />
</MessageTemplate>
</uc:TemplateTest>
<asp:Button ID="ButtonShowContent" runat="server" Text="Show Content" onclick="ButtonShowContent_Click" />
<asp:Label ID="ContentLabel" runat="server" Text=" " Width="100%" />
</form>
</body>
</html>
TemplatedUC.ascx ============================
<%# Control Language="C#" ClassName="TemplatedUC" %>
<%# Import Namespace="System.ComponentModel" %>
<script runat="server">
private ITemplate messageTemplate = null;
private MessageContainer container = new MessageContainer(1, "Initial Text");
[ TemplateContainer(typeof(MessageContainer)) ]
[ PersistenceMode(PersistenceMode.InnerProperty) ]
public ITemplate MessageTemplate {
get
{
return messageTemplate;
}
set
{
messageTemplate = value;
}
}
public int Index
{
get { return container.Index; }
set { container.Index = value; }
}
public string Message
{
get { return container.Message; }
set { container.Message = value; }
}
void Page_Init() {
if (messageTemplate != null) {
messageTemplate.InstantiateIn(container);
PlaceHolder1.Controls.Add(container);
}
}
public class MessageContainer: Control, INamingContainer {
private int m_index;
private String m_message;
internal MessageContainer(int index, String message)
{
m_index = index;
m_message = message;
}
public int Index {
get { return m_index; }
set { m_index = value; }
}
public String Message
{
get { return m_message; }
set { m_message = value; }
}
}
</script>
<asp:Placeholder runat="server" ID="PlaceHolder1" />
If you run this and change the value for 'Message', then when you click the button, the data will be displayed. Thank you.
Related
Below is my simple code to use control state in a custom control,
[DefaultProperty("Text")]
[ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]
public class WebCustomControl1 : WebControl
{
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string Text
{
get { return text; }
set { text = value; }
}
private string text;
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
protected override void OnInit(System.EventArgs e)
{
base.OnInit(e);
Page.RequiresControlState(this);
}
protected override object SaveControlState()
{
object baseSate = base.SaveControlState();
return new Pair(baseSate, Text);
}
protected override void LoadControlState(object savedState)
{
Pair value = savedState as Pair;
text = value.Second;
}
}
But it doesn't seem to work.. The SaveControlState and LoadControlState are not firing. can someone help me..?
Below is the aspx Code. Here is where i use the custom control.
<%# Page Language="C#" AutoEventWireup="true" EnableViewState="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<%# Register Assembly="WebApplication1" Namespace="WebApplication1" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>`enter code here`
<body>
<form id="form1" runat="server">
<div>
<cc1:WebCustomControl1 ID="WebCustomControl1_1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Button" /></div>
</form>
</body>
</html>
You've called RequiresControlState
Determines whether the specified Control object is registered to participate in control state management.`
But you should call RegisterRequiresControlState
Registers a control as one whose control state must be persisted.
I have a Repeater control with a Rating control (from the latest AJAX Control Toolkit) inside:
<asp:Repeater ID="repStudents" runat="server" onitemcommand="repStudents_ItemCommand">
<ItemTemplate>
<%# Eval("FirstName") %>
<asp:Rating ID="warnings" runat="server" Direction="NotSet" MaxRating="3" StarCssClass="star" EmptyStarCssClass="em" FilledStarCssClass="gr" WaitingStarCssClass="gr" AutoPostBack="True" CommandArgument='<%# Eval("Id") %>' CommandName="warn"></asp:Rating>
<br />
</ItemTemplate>
</asp:Repeater>
In the code behind, I have:
protected void repStudents_ItemCommand(object source, System.Web.UI.WebControls.RepeaterCommandEventArgs e)
{
//Custom log function
Log.Append(e.CommandName + " " + e.CommandArgument);
}
All renders fine. However, when I click on a rating the page posts back but repStudents_ItemCommand is not fired. How can I fix this?
Note that if I put a Button in the same Repeater, repStudents_ItemCommand fires correctly when I click the button.
The Rating control does not support CommandName/CommandArgument properties. But you can extend it as follows:
public class ExRating : Rating {
[Category("Behavior")]
public string CommandName {
get { return (string)ViewState["CommandName"] ?? string.Empty; }
set { ViewState["CommandName"] = value; }
}
[Category("Behavior")]
public string CommandArgument {
get { return (string)ViewState["CommandArgument"] ?? string.Empty; }
set { ViewState["CommandArgument"] = value; }
}
protected override void OnChanged(RatingEventArgs e) {
base.OnChanged(e);
RaiseBubbleEvent(this, new CommandEventArgs(CommandName, CommandArgument));
}
}
Then replace the old control with new one:
<pages>
<tagMapping>
<add tagType="AjaxControlToolkit.Rating, AjaxControlToolkit" mappedTagType="Sample.ExRating, Sample"/>
</tagMapping>
</pages>
i would like to use ajax UpdatePanel and ICallbackEventHandler on one page. Each will handle individual part of page, they are not related to each one.
If I remove UpdatePanel, ICallbackEventHandler is working. If I remove ICallbackEventHandler, update panel is working, but they are not working altogether :(
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default"
EnableEventValidation="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
</head>
<body>
<script type="text/ecmascript">
function BootX() {
XUpdate();
}
function XUpdate() {
X_CallServer("", "");
}
function X_ReceiveServerData(rValue) {
$("#a").html(rValue);
}
</script>
<form id="form1" runat="server">
<div id="a">
</div>
<asp:ScriptManager runat="server" ID="ScriptManager1" />
<div>
<asp:Timer ID="Timer1" runat="server" Interval="100" OnTick="Timer1_Tick">
</asp:Timer>
<asp:UpdatePanel runat="server" ID="UpdatePanel1" UpdateMode="Conditional">
<ContentTemplate>
<asp:DropDownList runat="server" ID="ddl">
</asp:DropDownList>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
<script type="text/ecmascript">
BootX();
</script>
</body>
</html>
And code behind:
public partial class _Default : System.Web.UI.Page, ICallbackEventHandler
{
protected void Page_Load(object sender, EventArgs e)
{
String cbReference = Page.ClientScript.GetCallbackEventReference(this, "arg", "X_ReceiveServerData", "context", true);
String callbackScript = string.Format("function X_CallServer(arg, context)" + "{{ {0} ;}}", cbReference);
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "X_CallServer", callbackScript, true);
ScriptManager1.RegisterAsyncPostBackControl(Timer1);
}
private string result = "";
public void RaiseCallbackEvent(string eventArgument)
{
// pretending some time consuming work
for (int i = 0; i < 1000000; i++) for (int j = 0; j < 100; j++) ;
result = "aaaaaaaaaaaaaaaaaaaaaaaaaaaa";
}
public string GetCallbackResult()
{
return result;
}
protected void Timer1_Tick(object sender, EventArgs e)
{
UpdatePanel1.Update();
Timer1.Enabled = false;
// pretending some time consuming work
for (int i = 0; i < 1000000; i++) for (int j = 0; j < 1000; j++) ;
ddl.Items.Add("Item 1");
ddl.Items.Add("Item 2");
ddl.Items.Add("Item 3");
}
}
The code above is just concept. I already have 5 control using ICallbackEventhandler on one page and they are working perfectly, now I need to add one new control to page which is using updatepanel, and it broke all of my other "ICallbackEventhandler" controls.
After few hours I found solution.
Instead of
<script type="text/ecmascript"> BootX();</script>
I used
function pageLoad(sender, args) {setTimeout("BootX()", 25); }
And it's working. The solution was quite easy :) UpdatePanel uses Postback, everytime when postback is made pageLoad method is called and here we call callback func.
Is it possible filtering a list in a content page by a value set in its master page? I mean when a user clicks on a link button in master page a variable is set and then based on the value that variable, when loading a content page, a list in it is filtered?
Yes, definitely. The only trick is to cast Page.Master to the type of master page that you're using.
Here's a quick example I whipped up:
MasterPage.master
<asp:Button runat="server" ID="btnToggleEvensOnly" Text="Toggle Even Number Filtering" OnClick="btnToggleEvensOnly_Click" />
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
MasterPage.master.cs
public bool IsFiltered
{
get
{
return ViewState["isFiltered"] == null ? false : (bool) ViewState["isFiltered"];
}
set
{
ViewState["isFiltered"] = value;
}
}
protected void btnToggleEvensOnly_Click(object sender, EventArgs e)
{
IsFiltered = !IsFiltered;
}
Default.aspx
<asp:Content ContentPlaceHolderID="ContentPlaceHolder1" runat="server" >
<asp:Repeater runat="server" ID="rptList">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr><td><%# Eval("i") %></td></tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</asp:Content>
Default.aspx.cs
protected override void OnPreRender(EventArgs e)
{
List<MyItem> items = new List<MyItem>();
for (int i = 0; i < 10; i++)
{
items.Add(new MyItem { i = i });
}
var query = items.AsEnumerable();
if (((MasterPage)Master).IsFiltered)
{
query = query.Where(mi => mi.i % 2 == 0);
}
rptList.DataSource = query;
rptList.DataBind();
base.OnPreRender(e);
}
public class MyItem
{
public int i { get; set; }
}
I've got an angry boss that will beat me down if I waste another day on this :-P Many karma points to the ajax guru who can solve my dilemma.
But more detail: I want to have an AccordionPane that grabs a bunch of links from an XML source and populate itself from said source.
There might be a sexier way, but this works. Populate your data source however you wish. This was just for demo purposes. Ditto for PrettyTitle() Key is to remember there are two item types in the accordion.
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Accordion Binding</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="AjaxScriptManager" runat="server">
</asp:ScriptManager>
<div>
<cc1:Accordion ID="AccordionControl" runat="server"
onitemdatabound="AccordionControl_ItemDataBound">
<Panes></Panes>
<HeaderTemplate>
<asp:Label ID="HeaderLabel" runat="server" />
</HeaderTemplate>
<ContentTemplate>
<asp:Literal ID="ContentLiteral" runat="server" />
<asp:HyperLink ID="ContentLink" runat="server" />
</ContentTemplate>
</cc1:Accordion><asp:xmldatasource runat="server" ID="RockNUGTwitter" ></asp:xmldatasource>
</div>
</form>
</body>
</html>
And codebehind is :
Using System;
using System.Web.UI.WebControls;
using System.Xml;
namespace Ajaxy
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Fill();
}
private void Fill()
{
PopulateDataSource();
AccordionControl.DataSource = RockNUGTwitter.GetXmlDocument().SelectNodes("//item");
AccordionControl.DataBind();
}
private void PopulateDataSource()
{
XmlDocument RockNugTwitterRSSDocument = new XmlDocument();
RockNugTwitterRSSDocument.Load("http://twitter.com/statuses/user_timeline/15912811.rss");
RockNUGTwitter.Data = RockNugTwitterRSSDocument.OuterXml;
}
protected void AccordionControl_ItemDataBound(object sender, AjaxControlToolkit.AccordionItemEventArgs e)
{
XmlNode ItemNode = (XmlNode)e.AccordionItem.DataItem;
if(e.AccordionItem.ItemType == AjaxControlToolkit.AccordionItemType.Content)
{
HyperLink ContentLink = (HyperLink)e.AccordionItem.FindControl("ContentLink");
ContentLink.NavigateUrl = ItemNode.SelectSingleNode("link").InnerText;
Literal ContentLiteral = (Literal)e.AccordionItem.FindControl("ContentLiteral");
ContentLiteral.Text = ItemNode.SelectSingleNode("title").InnerText;
ContentLink.Text = "Link";
}
else if(e.AccordionItem.ItemType == AjaxControlToolkit.AccordionItemType.Header)
{
Label HeaderLabel = (Label) e.AccordionItem.FindControl("HeaderLabel");
HeaderLabel.Text = PrettyTitle(ItemNode.SelectSingleNode("title").InnerText);
}
}
private string PrettyTitle(string FullItem)
{
string PrettyString = FullItem.Replace("RockNUG: ", "");
string[] Words = PrettyString.Split(' ');
const int MAX_WORDS_TOSHOW = 4;
int WordsToShow = MAX_WORDS_TOSHOW;
if(Words.Length < MAX_WORDS_TOSHOW)
{
WordsToShow = Words.Length;
}
PrettyString = String.Join(" ", Words, 0, WordsToShow);
if (Words.Length > WordsToShow)
{
PrettyString += "...";
}
return PrettyString;
}
}
}