在 ASP.NET 4.0 "Templated Control" 中重新绑定事件处理程序?
Re-Binding Event-Handlers in ASP.NET 4.0 "Templated Control"?
编辑:可悲的是,似乎没有人知道。也许这将有助于澄清我的困境:我正在尝试实现我自己的支持从 ItemTemplate 切换到 EditItemTemplate 的 DataList 类型的控件。单击 EditItemTemplate 内的按钮时会出现问题 - 它不会触发处理程序,除非您再次单击!
抱歉冗长 post。代码已完成,但希望没有任何干扰。
我正在尝试创建我自己的接受多个模板的用户控件。我部分遵循 Manning "ASP.NET 4.0 in Practice" 中的技术 39 和 40。它似乎工作正常,除了模板内的按钮直到第二次单击(在额外 post 返回之后)才绑定到处理程序。
涉及四个文件。 Default.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Src="~/TheTemplate.ascx" TagPrefix="TT" TagName="TheTemplate" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<TT:TheTemplate ID="tt" runat="server">
<ATemplate>
<p>This is template A</p>
<asp:Button ID="TemplateAButton" OnClick="TemplateAButton_Click" runat="server" Text="Template A Button" />
</ATemplate>
<BTemplate>
<p>This is template B</p>
<asp:Button ID="TemplateBButton" OnClick="TemplateBButton_Click" runat="server" Text="Template B Button" />
</BTemplate>
</TT:TheTemplate>
<br />
<asp:Button ID="ToggleTemplate" Text="Toggle Template" OnClick="ToggleTemplate_Click" runat="server" />
</div>
</form>
</body>
</html>
Default.aspx.cs:
using System;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Trace.IsEnabled = true;
tt.DataBind();
}
protected void ToggleTemplate_Click(object sender, EventArgs e)
{
tt.TemplateName = (tt.TemplateName == "A") ? "B" : "A";
tt.DataBind();
}
public void TemplateAButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateAButton_Click");
}
public void TemplateBButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateBButton_Click");
}
}
和用户控件,TheTemplate.ascx:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template <asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
<asp:Placeholder runat="server" ID="PlaceHolder1" />
最后,TheTemplate.ascx.cs:
using System;
using System.Web.UI;
[ParseChildren(true)]
public class TheTemplateContainer : Control, INamingContainer
{
private TheTemplate parent;
public TheTemplateContainer(TheTemplate parent)
{
this.parent = parent;
}
}
public partial class TheTemplate : System.Web.UI.UserControl, INamingContainer
{
public string TemplateName
{
get { return (string)(ViewState["TemplateName"] ?? "A"); }
set { ViewState["TemplateName"] = value; }
}
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ATemplate { get; set; }
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate BTemplate { get; set; }
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
EnsureChildControls();
base.OnDataBinding(e);
}
}
当您第一次 运行 它时,您会看到正在使用 ATemplate:
如果您单击“切换模板”按钮,所有文本都会正确呈现:
但是单击 "Template A Button" 或 "Template B Button" 不会在第一次尝试时触发 OnClick 处理程序:
它将在第二次点击时起作用:
问题是否与调用 .DataBind() 的位置有关?
我不太确定我是否理解它,但问题与新添加的控件如何处理 "catch-up" 事件有关。删除 PlaceHolder1 并以编程方式添加它可以解决问题。 TheTemplate.ascx 变为:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template
<asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
... 在 TheTemplate.ascx.cs 中,像这样替换 OnDataBinding:
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
System.Web.UI.WebControls.PlaceHolder PlaceHolder1 = new System.Web.UI.WebControls.PlaceHolder();
//PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
this.Controls.Clear();
this.Controls.Add(PlaceHolder1);
EnsureChildControls();
base.OnDataBinding(e);
}
以后,如果我觉得需要动态添加控件,我也会动态创建一个 PlaceHolder 并将其用作根。填充 PlaceHolder 后,我会将其添加到页面。
编辑:可悲的是,似乎没有人知道。也许这将有助于澄清我的困境:我正在尝试实现我自己的支持从 ItemTemplate 切换到 EditItemTemplate 的 DataList 类型的控件。单击 EditItemTemplate 内的按钮时会出现问题 - 它不会触发处理程序,除非您再次单击!
抱歉冗长 post。代码已完成,但希望没有任何干扰。
我正在尝试创建我自己的接受多个模板的用户控件。我部分遵循 Manning "ASP.NET 4.0 in Practice" 中的技术 39 和 40。它似乎工作正常,除了模板内的按钮直到第二次单击(在额外 post 返回之后)才绑定到处理程序。
涉及四个文件。 Default.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Src="~/TheTemplate.ascx" TagPrefix="TT" TagName="TheTemplate" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<TT:TheTemplate ID="tt" runat="server">
<ATemplate>
<p>This is template A</p>
<asp:Button ID="TemplateAButton" OnClick="TemplateAButton_Click" runat="server" Text="Template A Button" />
</ATemplate>
<BTemplate>
<p>This is template B</p>
<asp:Button ID="TemplateBButton" OnClick="TemplateBButton_Click" runat="server" Text="Template B Button" />
</BTemplate>
</TT:TheTemplate>
<br />
<asp:Button ID="ToggleTemplate" Text="Toggle Template" OnClick="ToggleTemplate_Click" runat="server" />
</div>
</form>
</body>
</html>
Default.aspx.cs:
using System;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Trace.IsEnabled = true;
tt.DataBind();
}
protected void ToggleTemplate_Click(object sender, EventArgs e)
{
tt.TemplateName = (tt.TemplateName == "A") ? "B" : "A";
tt.DataBind();
}
public void TemplateAButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateAButton_Click");
}
public void TemplateBButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateBButton_Click");
}
}
和用户控件,TheTemplate.ascx:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template <asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
<asp:Placeholder runat="server" ID="PlaceHolder1" />
最后,TheTemplate.ascx.cs:
using System;
using System.Web.UI;
[ParseChildren(true)]
public class TheTemplateContainer : Control, INamingContainer
{
private TheTemplate parent;
public TheTemplateContainer(TheTemplate parent)
{
this.parent = parent;
}
}
public partial class TheTemplate : System.Web.UI.UserControl, INamingContainer
{
public string TemplateName
{
get { return (string)(ViewState["TemplateName"] ?? "A"); }
set { ViewState["TemplateName"] = value; }
}
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ATemplate { get; set; }
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate BTemplate { get; set; }
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
EnsureChildControls();
base.OnDataBinding(e);
}
}
当您第一次 运行 它时,您会看到正在使用 ATemplate:
如果您单击“切换模板”按钮,所有文本都会正确呈现:
但是单击 "Template A Button" 或 "Template B Button" 不会在第一次尝试时触发 OnClick 处理程序:
它将在第二次点击时起作用:
问题是否与调用 .DataBind() 的位置有关?
我不太确定我是否理解它,但问题与新添加的控件如何处理 "catch-up" 事件有关。删除 PlaceHolder1 并以编程方式添加它可以解决问题。 TheTemplate.ascx 变为:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template
<asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
... 在 TheTemplate.ascx.cs 中,像这样替换 OnDataBinding:
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
System.Web.UI.WebControls.PlaceHolder PlaceHolder1 = new System.Web.UI.WebControls.PlaceHolder();
//PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
this.Controls.Clear();
this.Controls.Add(PlaceHolder1);
EnsureChildControls();
base.OnDataBinding(e);
}
以后,如果我觉得需要动态添加控件,我也会动态创建一个 PlaceHolder 并将其用作根。填充 PlaceHolder 后,我会将其添加到页面。