2

需要帮助从另一个班级调用事件。我有声明事件的类:

     public class MxPBaseGridView : GridView
    {

        public event AddNewItemsToPopUpMenuEventHandler AddNewItemsToPopUpMenu;
          ...
    }

我需要调用事件的另一个类有方法和“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 
                }

那么做同样工作的正确方法是什么?

4

2 回答 2

5

C# 中的事件是一种有趣的事情。它们非常类似于自动属性,但具有私有 get 方法和公共(或您选择的任何访问权限)set 方法。

请允许我演示一下。让我们创建一个带有假设事件的假设类。

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);
    }
}

此类遵循公开事件的类的典型模式。它公开事件,但有一个受保护的虚拟“On...”方法,默认情况下,它只是调用事件(如果它有任何订​​阅者)。这个受保护的虚方法不仅封装了实际调用事件的逻辑,还为派生类提供了一种方法:

  • 以更少的开销方便地处理事件,
  • 在所有外部订阅者收到事件之前或之后执行一些处理,
  • 调用一个完全不同的事件,或
  • 完全压制事件。

但是这个名为 SomeEvent 的“事件”对象是什么?在 C# 中,我们熟悉字段、属性和方法,但究竟什么是事件?

在我们开始之前,它有助于意识到 C# 中实际上只有两种类型的类成员:字段和方法。属性和事件或多或少只是它们之上的语法糖。

属性实际上是一种或两种方法,并且是存储在元数据中的名称,C# 编译器允许您使用它来引用这两种方法之一。也就是说,当您定义这样的属性时:

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>");
    }
}

编译器为您编写了两种方法:

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>");
}

我不是这个意思。这两个方法与一大块关于属性的元数据一起成为编译类的一部分,它告诉编译器下次从(get)或写入(set)属性时调用哪些方法。所以当你写这样的代码时:

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

编译器找到分配给 的 getter 和 setter 方法SomeProperty,并将您的代码转换为:

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

这就是为什么如果您定义一个具有公共字段的类,但后来决定将其更改为一个属性,以便在读取或写入它时可以做一些有趣的事情,您必须重新编译任何包含对它的引用的外部程序集成员,因为字段访问指令需要变成方法调用指令。

现在这个属性有点不正常,因为它不依赖任何支持字段。它的 getter 返回一个常量值,它的 setter 没有在任何地方存储它的值。需要明确的是,这是完全有效的,但大多数时候,我们定义的属性更像这样:

string someProperty;

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

除了对字段进行读取和写入之外,此属性不执行任何操作。它与名为 的公共字段几乎相同SomeProperty,只是您可以在以后向该 getter 和 setter 添加逻辑,而无需重新编译您的类的使用者。但是这种模式非常普遍,以至于 C# 3 添加了“自动属性”来达到相同的效果:

public string SomeProperty{get;set;}

编译器将其转换为与我们上面编写的代码相同的代码,除了支持字段有一个只有编译器知道的超级机密名称,因此我们只能在代码中引用该属性,即使在类本身中也是如此。

因为我们无法访问支持字段,而您可能具有如下只读属性:

string someProperty;

public string SomeProperty{get{return someProperty;}}

您几乎永远不会看到只读的自动属性(编译器允许您编写它们,但您会发现它们几乎没有用处):

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

相反,你通常会看到的是:

public string SomeProperty{get;private set;}

附加的private访问修饰符set使类内的方法可以设置属性,但该属性在类外仍然显示为只读。

“现在这与事件有什么关系?” 你可能会问。嗯,事实上,一个事件很像一个自动属性。通常,当您声明一个事件时,编译器会生成一个超级机密的支持字段和一对方法。除了支持字段不是超级机密,而且这对方法不是“get”和“set”,它们是“add”和“remove”。让我演示一下。

当您编写这样的事件时:

public event EventHandler SomeEvent;

编译器写的是这样的:

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);
}

它还添加了一些元数据粘合,以便以后在编写如下代码时:

void Awe_SomeEventHandler(object sender, EventArgs e){}

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

编译器将其重写为(仅有趣的行):

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

这里需要注意的重要一点是,与这些相关的唯一可公开访问的成员SomeEvent是那些 add 和 remove 方法,当您使用+=and-=运算符时会调用这些方法。支持字段,即名为 SomeEvent 的包含事件订阅者的委托对象,是一个私有字段,只有声明类的成员才能访问。

但是,就像自动属性只是手动编写支持字段和 getter 和 setter 的捷径一样,您也可以显式声明您的委托并添加和删除方法:

internal EventHandler someEvent;

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

然后,程序集中的其他类可以触发您的事件:

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

但是,以正常(自动)方式定义您的事件更容易也更惯用,并且只需公开一个“Raise”方法:

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.

于 2012-08-17T05:02:07.880 回答
3

您只能在定义了事件的类中调用事件。常见的是使用特定方法来触发事件,您必须将其添加到定义事件的类中。在您的情况下,在 MxPBaseGridView 类中。添加以下内容:

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

注意:我不确定 eventargs-type 是什么,所以我把它打开了。

然后你可以从你的静态方法中调用这个方法。

注意:通常我将 On... 方法定义为私有的,如果需要的话,定义为受保护的。在这种情况下,我将其定义为 public,因为您需要从班级外部调用它。

于 2012-08-13T12:59:13.350 回答