跨回发保留用户控制 (ASCX) 事件处理程序对象

Persist User Control(ASCX) EventHandler Object Across Postbacks

假设您有一个用户控件 (ASCX):

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Test.ascx.cs" 
    Inherits="WebApplication1.Test" %>
<asp:Button ID="test" runat="server" OnClick="test_Click" Text="test" />

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class Test : System.Web.UI.UserControl
    {        
        public event EventHandler Method1;

        public EventHandler Method2
        {
            get { return (EventHandler)Session[this.ClientID + "|Method2"]; }
            set { Session[this.ClientID + "|Method2"] = value; }
        }

        public EventHandler Method3
        {
            get { return (EventHandler)ViewState["Method3"]; }
            set { ViewState["Method3"] = value; }
        }

        protected void test_Click(object sender, EventArgs e)
        {
            if (Method1 != null)
                Method1(this, e);

            if (Method2 != null)
                Method2(this, e);

            if (Method3 != null)
                Method3(this, e);
        }
    }
}

并且您有一个放置 UserControl 的页面:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" 
    AutoEventWireup="true" CodeBehind="Default.aspx.cs" 
    Inherits="WebApplication1._Default" %>
<%@ Register Src="~/Test.ascx" TagName="Test" TagPrefix="uc1" %>

<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
    <uc1:Test ID="TestControl" runat="server" />
    <asp:Label ID="Result" runat="server" Text="Test" />
</asp:Content>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication1
{
    public partial class _Default : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //Uncomment each one to observe their behavior

            //Will never trigger(unless reset on each postback)
            //if (!IsPostBack) TestControl.Method1 += new EventHandler(Test);

            //Will trigger(the code will run) but makes no change to the parent page
            //if (!IsPostBack) TestControl.Method2 += new EventHandler(Test);

            /*
            Will fail because it can't serialize the "EventHandler" object
            You can play around by changing this to a delegate type 
            and making that serializable, but that fails as well  
            */
            //if (!IsPostBack) TestControl.Method3 += new EventHandler(Test);
        }
        protected void Test(object sender, EventArgs e)
        {
            Result.Text = Guid.NewGuid().ToString();
        }
    }
}

但是对于这个给定的用户控件,我们想要定义一个事件来捕获按钮的点击并触发用户控件父级选择的事件。同样由于超出本示例范围的要求,假设我们只想设置一次 "MethodX" EventHandler 的值,即不在每个回传上重新创建 control/property(在本示例中我通过仅在页面加载时设置一次事件来演示此概念)。

如何实现?我尝试过的所有三种方法都以其独特的方式失败了……到目前为止,我还没有发现与事件持久性相关的类似问题,大多数帖子都处理 variable/object 持久性("Use the ViewState"或各种其他持久化非事件对象的方法,请参阅 link) or simply direct the user to "recreate the control on every Postback(including related properties)", see link

任何反馈或见解都会有所帮助,尤其让我感到困惑的是 "Method2" 的行为,我认为我对页面生命周期没有足够全面的了解,无法理解为什么会发生这种情况。

您需要在加载事件之前注册事件(尝试初始化)。点击事件触发发生在init和load事件之间。

更新:抱歉,您在 Load 和 LoadComplete 之间引发了右键单击事件。

更新:我想我找到了你需要的东西,令人失望的是,你想要什么是不可能的。非静态方法的实例委托强烈依赖于方法 class 中的实例。每个请求都会创建页面实例,并在请求结束时将其释放。页面不是单例实例。这就是为什么即使您将处理程序存储在会话中也无法重用它的原因,因为它试图从已处置的实例中调用方法。

这是一篇关于事件和委托如何工作的好文章:http://csharpindepth.com/Articles/Chapter2/Events.aspx

最重要的部分是:

[..] the key points of data in any particular delegate instance are the method the delegate refers to, and a reference to call the method on (the target). For static methods, no target is required.

target 是您第一次创建页面的 page/usercontrol 实例。当您执行回发时,page/usercontrol 实例已更改,但您持久处理程序的 target 未更改。