简体   繁体   English

c#从另一个类中的另一个静态事件引发事件

[英]c# raise event from another static event in another class

Need help calling event from another class. 需要帮助另一个班级的活动。 I have class with declared event: 我有宣告事件的课程:

     public class MxPBaseGridView : GridView
    {

        public event AddNewItemsToPopUpMenuEventHandler AddNewItemsToPopUpMenu;
          ...
    }

Another class from which i need to call event has methods and "AddNewItemsToPopUpMenuEventHandler " delegate 我需要从中调用事件的另一个类具有方法和“ AddNewItemsToPopUpMenuEventHandler”委托

    public delegate void AddNewItemsToPopUpMenuEventHandler(PopupMenuShowingEventArgs e);
    public static class GridViewUtils
{
public static void gridView_PopupMenuShowing(object sender, PopupMenuShowingEventArgs e)
    {                     
        if (e.MenuType != DevExpress.XtraGrid.Views.Grid.GridMenuType.Row)
        {
           if (menu != null)
            {               
                if (sender is MxPBaseAdvBandedGridView)
                {
                    MxPBaseAdvBandedGridView currentGrid = sender as MxPBaseAdvBandedGridView;

...

                    currentGrid.AddNewItemsToPopUpMenu();
                    if (currentGrid.AddNewItemsToPopUpMenu != null) //there i need to call event
                        currentGrid.AddNewItemsToPopUpMenu(e); // how you understand it doesn't work 
                }

so what is the right way to do the same job? 那么做同样工作的正确方法是什么?

Events in C# are kind of funny things. C#中的事件是一种有趣的事情。 They're very much like automatic properties, but with a private get method and a public (or whatever access you choose) set method. 它们非常类似于自动属性,但是具有私有的get方法和公共的(或您选择的任何访问方式)set方法。

Allow me to demonstrate. 请允许我示范。 Let's create a hypothetical class with a hypothetical event. 让我们用一个假设事件创建一个假设类。

class SomeObject{
    public event EventHandler SomeEvent;

    public void DoSomeStuff(){
        OnSomeEvent(EventArgs.Empty);
    )

    protected virtual void OnSomeEvent(EventArgs e){
        var handler = SomeEvent;
        if(handler != null)
            handler(this, e);
    }
}

This class follows the typical pattern for a class that exposes an event. 此类遵循暴露事件的类的典型模式。 It exposes the event publicly, but has a protected virtual "On..." method that, by default, simply calls the event (if it has any subscibers). 它公开地公开事件,但是具有受保护的虚拟“ On ...”方法,默认情况下,该方法仅简单地调用事件(如果它具有任何子用户)。 This protected virtual method not only encapsulates the logic of actually calling the event, but provides a way for derived classes to: 这种受保护的虚拟方法不仅封装了实际调用事件的逻辑,而且还为派生类提供了一种方法:

  • conveniently handle the event with less overhead, 方便地以较少的开销处理事件,
  • perform some processing before or after all external subscribers receive the event, 在所有外部订户收到事件之前或之后执行一些处理,
  • call an entirely different event, or 呼叫完全不同的事件,或者
  • suppress the event altogether. 完全抑制该事件。

But what is this "event" object called SomeEvent? 但是,这个名为“ SomeEvent”的“事件”对象是什么? In C#, we're familiar with fields, properties, and methods, but what exactly is an event? 在C#中,我们熟悉字段,属性和方法,但是事件究竟是什么?

Before we get into that, it helps to realize that there are really only two types of class members in C#: fields and methods. 在开始讨论之前,它有助于认识到C#中实际上只有两种类型的类成员:字段和方法。 Properties and events are more or less just syntactic sugar on top of those. 属性和事件或多或少只是这些之上的语法糖。

A property is really either one or two methods, and a name stored in metadata that the C# compiler allows you to use to refer to one of those two methods. 属性实际上是一个或两个方法,并且存储在元数据中的名称可供C#编译器用来引用这两个方法之一。 That is, when you define a property like this one: 也就是说,当您定义这样的属性时:

public string SomeProperty{
    get{return "I like pie!";}
    set{
        if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
            Console.WriteLine("Pie is yummy!");
        else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
    }
}

the compiler writes two methods for you: 编译器为您编写了两种方法:

public string get_SomeProperty(){return "I like pie!";}
public void   set_SomeProperty(string value){
    if(string.Compare(value, "pie", StringComparison.OrdinalIgnoreCase) == 0)
        Console.WriteLine("Pie is yummy!");
    else Console.WriteLine("\"{0}\" isn't pie!", value ?? "<null>");
}

I don't mean this obliquely. 我不是这个意思。 These two methods literally become part of your compiled class along with a chunk of metadata about the property, which tells the compiler next time which methods to call when the property is read from (get) or written to (set). 从字面上看,这两个方法与有关属性的大量元数据一起成为编译类的一部分,当从(获取)或写入(设置)属性时,这将告诉编译器下次调用哪个方法。 So when you write code like this: 因此,当您编写这样的代码时:

var foo = someObject.SomeProperty;
someObject.SomeProperty = foo;

The compiler finds the getter and setter methods assigned to SomeProperty , and turns your code into: 编译器找到分配给SomeProperty的getter和setter方法,并将您的代码转换为:

string foo = someObject.get_SomeProperty();
someObject.set_SomeProperty(foo);

This is why if you define a class with a public field, but later decide to change it to a property so that you can do something interesting when it is read from or written to, you have to recompile any external assemblies that contain references to this member, because what was a field access instruction needs to become a method call instruction, instead. 这就是为什么如果您定义一个具有公共字段的类,但是后来决定将其更改为属性,以便在读取或写入该类时可以做一些有趣的事情,则必须重新编译包含对此引用的所有外部程序集成员,因为原来是字段访问指令,而必须变成方法调用指令。

Now this property was somewhat abnormal, in that it didn't rely on any backing field. 现在,此属性有些异常,因为它不依赖任何后备字段。 Its getter returned a constant value, and its setter didn't store its value anywhere. 它的getter返回一个恒定值,而它的setter没有将其值存储在任何地方。 To be clear, that's perfectly valid, but most of the time, we define properties more like this: 需要明确的是,这是完全正确的,但是在大多数情况下,我们更像这样定义属性:

string someProperty;

public string SomeProperty{get{return someProperty;}set{someProperty = value;}}

This property doesn't do anything other than read and write to a field. 该属性除了读取和写入字段外不执行其他任何操作。 It's pretty much the same as a public field named SomeProperty , except that you could add logic to that getter and setter at a later date, without making consumers of your class recompile. 它与名为SomeProperty的公共字段几乎相同,不同之处在于,您可以稍后在该getter和setter中添加逻辑,而无需重新编译类的使用者。 But this pattern is so common, that C# 3 added "automatic properties" to achieve the same effect: 但是这种模式非常普遍,以至于C#3添加了“自动属性”以达到相同的效果:

public string SomeProperty{get;set;}

The compiler turns this into the same code as we wrote above, except that the backing field has a super secret name that only the compiler knows, so we can only refer to the property in our code, even within the class itself. 编译器将其转换为与上面编写的代码相同的代码,不同的是后备字段具有仅编译器才知道的超级秘密名称,因此即使在类本身内,我们也只能引用代码中的属性。

Because the backing field is inaccessible to us, while you might have read-only properties like this: 由于我们无法访问后备字段,因此您可能具有以下只读属性:

string someProperty;

public string SomeProperty{get{return someProperty;}}

you'll almost never see read-only automatic properties (the compiler lets you write them, but you'll find very little use for them): 您几乎永远不会看到只读的自动属性(编译器允许您编写它们,但几乎找不到它们的用法):

public string SomeProperty{get;} // legal, but not very useful unless you always want SomeProperty to be null

Instead, what you'll usually see is this: 相反,您通常会看到的是:

public string SomeProperty{get;private set;}

The private access modifier attached to set makes it possible for methods within the class to set the property, but the property still appears read-only outside the class. 附加到setprivate access修饰符使类内的方法可以设置属性,但是该属性在类外仍然显示为只读。

"Now what does any of this have to do with events?" “现在,这与事件有什么关系?” you may ask. 你可能会问。 Well, as a matter of fact, an event is very much like an automatic property. 嗯,事实上,事件非常类似于自动属性。 Normally, when you declare an event, the compiler generates a super secret backing field and a pair of methods. 通常,当您声明事件时,编译器会生成一个超级秘密后备字段和一对方法。 Except that the backing field isn't quite as super secret, and the pair of methods aren't "get" and "set", they're "add" and "remove". 除了背景字段不是超级秘密,并且这两种方法不是“获取”和“设置”之外,它们都是“添加”和“删除”。 Let me demonstrate. 让我示范一下。

When you write an event like this: 当您编写这样的事件时:

public event EventHandler SomeEvent;

what the compiler writes is this: 编译器写的是这样的:

EventHandler SomeEvent;

public void add_SomeEvent(EventHandler value){
    SomeEvent = (EventHandler)Delegate.Combine(SomeEvent, value);
}
public void remove_SomeEvent(EventHandler value){
    SomeEvent = (EventHandler)Delegate.Remove(SomeEvent, value);
}

It also adds some metadata glue so that later, when you write code like this: 它还添加了一些元数据胶水,以便稍后在您编写如下代码时:

void Awe_SomeEventHandler(object sender, EventArgs e){}

void SomeMethod(SomeObject Awe){
    Awe.SomeEvent += Awe_SomeEventHandler
    Awe.SomeEvent -= Awe_SomeEventHandler
}

the compiler rewrites it as (only the interesting lines): 编译器将其重写为(仅有趣的行):

Awe.add_SomeEvent(Awe_SomeEventHandler);
Awe.remove_SomeEvent(Awe_SomeEventHandler);

What's important to take note of here is that the only publicly accessible members related to SomeEvent are those add and remove methods, and those are called when you use the += and -= operators. 这里需要注意的重要一点是,与SomeEvent相关的唯一可公开访问的成员是那些add和remove方法,并且在使用+=-=运算符时会调用它们。 The backing field, that delegate object named SomeEvent that holds the event's subscribers, is a private field that only members of the declaring class can access. 支持字段(该代理对象名为SomeEvent,用于保存事件的订阅者)是一个私有字段,只有声明类的成员才能访问。

However, much like the way automatic properties are only a shortcut for writing the backing field and getter and setter by hand, you can explicitly declare your delegate and add and remove methods as well: 但是,就像自动属性只是手动编写支持字段以及getter和setter的快捷方式一样,您可以显式声明委托以及添加和删除方法:

internal EventHandler someEvent;

public event EventHandler SomeEvent{
    add{someEvent = (EventHandler)Delegate.Combine(someEvent, value);}
    remove{someEvent = (EventHandler)Delegate.Remove(someEvent, value);}
}

Then, other classes within your assembly can trigger your event: 然后,程序集中的其他类可以触发您的事件:

var handler = Awe.someEvent;
if(handler != null)
    handler(Awe, EventArgs.Empty);

However, it's easier and more idiomatic to define your event the normal (automatic) way, and just expose a "Raise" method: 但是,以常规(自动)方式定义事件,并只公开“提高”方法会更容易且更惯用:

internal void RaiseSomeEvent(){OnSomeEvent(EventArgs.Empty);}

But now you hopefully understand why you have to do it this way, and what's going on in the background. 但是现在您希望能理解为什么必须这样做,以及背景情况。

You can only invoke an event in the class where you have defined the event. 您只能在已定义事件的类中调用事件。 What is common is to use a specific method to fire the event, which you have to add in the class where you define the event. 常见的是使用特定的方法来触发事件,您必须在定义事件的类中添加该方法。 In your case, in the class MxPBaseGridView. 对于您的情况,在类MxPBaseGridView中。 Add the following: 添加以下内容:

public void OnAddNewItemsToPopUpMenu(<eventargstype> e) {
    var addNewItemsToPopUpMenu = AddNewItemsToPopUpMenu;
    if (addNewItemsToPopUpMenu != null)
        addNewItemsToPopUpMenu(this, e);
}

Note: I'm not sure what the eventargs-type is, so I've left it open. 注意:我不确定eventargs-type是什么,所以我将其保持打开状态。

Then you can call this method from your static method. 然后,您可以从静态方法中调用此方法。

Note: normally I define the On... methods as private, if necessary as protected. 注意:通常,我将On ...方法定义为私有方法,必要时将其定义为受保护的方法。 In this case I've defined it public since you need to call it from outside your class. 在这种情况下,我将其定义为public,因为您需要从课堂之外调用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM