如何在C#中手动调用事件?

如何在C#中手动调用事件?,c#,winforms,visual-studio-2010,C#,Winforms,Visual Studio 2010,我有一个USerControll,其中有一个文本框。我在表单中使用usercontrol,当有人按下文本框上的enter键时,我想做一些事情。我怎么做? 如果您告诉我如何手动调用事件,我可以在textbox.keydown中调用usercontrol.keydown。首先,只能从声明事件的控件内的代码引发事件。因此,您的用户控件必须声明自定义事件KeyDown才能引发它。例如,您不能在用户控件包含的文本框上提高KeyDown。但是,您可以声明自己的KeyDown,并将处理程序附加到文本框的Key

我有一个USerControll,其中有一个文本框。我在表单中使用usercontrol,当有人按下文本框上的enter键时,我想做一些事情。我怎么做?
如果您告诉我如何手动调用事件,我可以在textbox.keydown中调用usercontrol.keydown。

首先,只能从声明事件的控件内的代码引发事件。因此,您的用户控件必须声明自定义事件KeyDown才能引发它。例如,您不能在用户控件包含的文本框上提高KeyDown。但是,您可以声明自己的KeyDown,并将处理程序附加到文本框的KeyDown,该处理程序将引发您自己的KeyDown

鉴于此限制,引发事件很容易:

public delegate void MyEventHandler(object sender, MyEventArgs e)

public event MyEventHandler MyEvent;

public void RaisesMyEvent()
{
   ...

   if(MyEvent != null) //required in C# to ensure a handler is attached
      MyEvent(this, new MyEventArgs(/*any info you want handlers to have*/));
}

提出一个事件看起来很像一种方法,因为本质上这就是你正在做的;您正在调用一个或多个在事件幕后分配给多播代理的方法代理。可以将其视为将一个方法分配给一个普通的命名委托(例如,如果您在定义中省略了“event”关键字),并从代码内部调用它。真实事件与真实事件之间的唯一区别在于,一个事件可以附加多个处理程序委托,并在引发时调用所有这些委托。

您所描述的称为事件冒泡。下面是一个例子:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyUserControl.ascx.cs" Inherits="MyUserControl" %>

<asp:TextBox ID="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" />


通常情况下,事件调用被包装在一个名为“On[EventName]”的方法中,该方法验证delgate是否有一个或多个目标(事件不为null),然后使用发送方和任何适用的参数调用它……因此,类似这样的典型模式是:

public event EventHandler SomethingHappened;
protected void OnSomethingHappend(EventArgs e)
{
    if (SomethingHappened != null)
        SomethingHappened(this, e);
}
任何需要引发该事件的事件都会调用该方法(假定其可访问)


如果您只是想传递事件,那么作为一个UserControl,您可能只需要调用基本的“On[event]”方法,该方法很可能是公开的。您也可以连接事件处理程序,将子控件中的事件作为父控件的事件直接传递…以便txtFoo.KeyPress仅调用父控件的OnKeyPress方法。

如果您使用WPF,则可以使用RaiseEvent:

但这对于你想做的事情来说是错误的

你应该把这件事搞砸

class MyControl : UserControl {
    public KeyDownEventHandler KeyDown;

    private void OnTextBoxKeyDown(object sender, EventArgs e){ KeyDown.Invoke(sender, e); }
}

然后从你的表格中听KeyDown。请原谅在命名各种元素/事件时出现的错误。

我正在为这个问题寻找答案

就这么做吧

例:

//this is the call to trigger the event:

 **lst_ListaDirectorios_SelectedIndexChanged(this, new EventArgs());**

//do that if you have the method signature in the same class as I do. (something like this below)
private void lst_ListaDirectorios_SelectedIndexChanged(object sender, EventArgs e)
        {
          //do something
         }

我希望这对您有用。

如果您确实需要手动调用事件,您可以获取支持代理,它通常是私有的。使用.NET反编译器(如ILSPY)定位事件的备份字段,然后使用反射获取备份委托

示例:从
BackgroundWorker
获取事件
DoWork

在ILSpy中反编译
BackgroundWorker
类,您会看到:

public event DoWorkEventHandler DoWork
{
    add
    {
        base.Events.AddHandler(doWorkKey, value);
    }
    remove
    {
        base.Events.RemoveHandler(doWorkKey, value);
    }
}
因此,您需要找到
事件
成员和
doWorkKey
字段作为键

事件
是在类
组件
中声明的
事件句柄列表
(公共类)

doWorkKey
是在类
BackgroundWorker
中声明的静态字段

然后使用反射获取代理:

PropertyInfo property = backgroundWorker.GetType().GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
EventHandlerList eventHandlerList = (EventHandlerList)property.GetValue(backgroundWorker, null);
FieldInfo doWorkField = backgroundWorker.GetType().GetField("doWorkKey", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
object doWorkKey = doWorkField.GetValue(null);
DoWorkEventHandler doWork = (DoWorkEventHandler)eventHandlerList[doWorkKey];
现在您有了
DoWork
事件的委托,可以调用它

同样的方法也适用于其他控件


请注意,使用反射获取私有字段可能会在代码的任何新版本出现时中断。

因此,您希望从TextBox.KeyDown事件处理程序中引发UserControl.KeyDown事件,对吗?因此,在用户实际按下按键时响应事件对于您的场景来说是不够的?我想我在你的场景中遗漏了一些东西。@saeed-原则上解决方案是相同的,但这是针对ASP.NET还是WinForms的?@RQDQ:我不明白你遗漏了什么。我只是想澄清一下,因为我可能误解了这个问题。您是专门寻找“回车”还是试图通过文本框捕获提交?不幸的是,此代码不是线程安全的。您需要创建
MyEvent
的本地副本,以防止另一个线程同时删除
if
和事件调用之间的所有事件处理程序时出现
NullReferenceExceptions
。。。。或者在计算if之前锁定(MyEvent()),然后释放它。问题只是关于举办一个活动,这就是我的回答;如果他随意地连接和分离处理程序,这表明他对事件有更深入的了解,不必问这个问题。原则上你是对的,但这不是我的意思:在本地复制事件是一种牢固确立的最佳实践。任何事件引发代码都不应该在没有它的情况下编写,即使是作为一个示例也不应该(因为如果您不希望出现多线程问题,您就不会想到它)。随意附加事件并不是用户控件的代码可以控制的——这取决于用户。作为一个可重用的代码,用户控制代码应该尽可能地健壮。嗯,这是一个教条,但在这里几乎不相关。一个控件只有4个成员,这些成员被记录为可从另一个线程使用(InvokeRequired等)。即使是MSFT也不能始终如一地做到这一点,例如NumericUpDown。但是任何多播代理都不能连接多个代理吗?那么,一个活动有什么不同呢?
//this is the call to trigger the event:

 **lst_ListaDirectorios_SelectedIndexChanged(this, new EventArgs());**

//do that if you have the method signature in the same class as I do. (something like this below)
private void lst_ListaDirectorios_SelectedIndexChanged(object sender, EventArgs e)
        {
          //do something
         }
public event DoWorkEventHandler DoWork
{
    add
    {
        base.Events.AddHandler(doWorkKey, value);
    }
    remove
    {
        base.Events.RemoveHandler(doWorkKey, value);
    }
}
PropertyInfo property = backgroundWorker.GetType().GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
EventHandlerList eventHandlerList = (EventHandlerList)property.GetValue(backgroundWorker, null);
FieldInfo doWorkField = backgroundWorker.GetType().GetField("doWorkKey", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
object doWorkKey = doWorkField.GetValue(null);
DoWorkEventHandler doWork = (DoWorkEventHandler)eventHandlerList[doWorkKey];