在 C++/CLI 中对 EventHandler 进行类型转换

Typecasting EventHandler in C++/CLI

我有一个嵌入了 TabControl 的表单(我称之为 MainForm)。每次用户创建一个新选项卡时,它都会填充一个包含许多控件的预建面板实例(我将其称为 MyPanel)。

我的 MyPanel class 有一个私有变量 bool save_state 每次编辑一个(可编辑的)控件时设置为 false 并设置为 true 时用户 "saves" 面板状态。

我想要一个视觉标志来跟踪具有未保存更改的选项卡(例如,如果选项卡 "Tab1" 具有未保存的更改,它将改为显示文本 "Tab1 *")。所以我想在我的 MainForm 中设置事件处理程序,它可以调用 MyPanel 中的方法来将处理程序添加到每个控件。

由于不是我所有的控件都使用相同的EventHandler类型(例如,我还需要跟踪DataGridViewRowsAddedEvent等),我目前有几种方法将适当的处理程序添加到相应的控件(每种类型的事件处理程序一个),每个都是 运行 相同的代码(即将 save_state 位设置为 false 并将“*”附加到选项卡文本。

例如,在 MainForm.cpp 我有:

#include "MyPanel.h"

void markUnsaved(void) {
  // set panel bit to false
  // append " *" to tab text if we haven't already
}

void MainForm::handler1(Object ^sender, EventArgs ^e) {
  markUnsaved();
}

void MainForm::handler2(Object ^sender, DataGridViewRowsAddedEventArgs ^e) {
  markUnsaved();
}

void Main::FormaddNewPanelToTab(int tab_index) {
  // check index is valid ...


  // make the new panel
  MyPanel ^new_panel = gcnew MyPanel();
  new_panel->addEventHandlerToControls(gcnew EventHandler(this, &MainForm::handler1));
  new_panel->addDgvEventHandlerToControls(gcnew DataGridViewRowsAddedEventHandler(this, &MainForm::handler2));

  // rest of code...
}

虽然这目前按预期工作,但这(以及我必须管理的其他几种事件处理程序类型)使我的代码看起来非常愚蠢。

我希望能够在 MainForm 中有一个事件处理程序,在 MyPanel 中有一个方法,它对传递的事件处理程序进行类型转换并将其添加到所有控件中合适的类型。

我试过做一些简单的转换,例如:

void MyPanel::addHandlerToControls(EventHandler ^handler) {
  control_NUD->ValueChanged += handler; // this works because ValueChanged is of type EventHandler
  control_DGV->RowsAdded += (DataGridViewRowsAddedEventHandler ^)handler; // this compiles but throws an exception

  // rest of the code...
}

无济于事。

如有任何帮助,我们将不胜感激!

我知道这可能有点晚了,但我想展示我将如何解决这个问题。

首先,我建议摆脱铸造事件处理程序的想法。这种方法可能适用于 C#(经过一些调整),但据我所知,这在 C++ /CLI.

中是不可能的

我会向 MyPanel class 添加新事件,每次面板上的数据更改时都会调用该事件。但是为了避免向 MyPanel class 中的控件事件添加大量不同的处理程序,最好创建一个通用方法来处理所有必要的控件事件并触发新事件。也许这听起来很乱,让我展示一下代码:

public ref class MyPanel 
{
    // Add a new event
    public: 
        event EventHandler^ DataChanged;

    // Add a method that will fire new event 
    // this methid will be invoked on every control's event that you'll subscribe
    private: 
        generic <typename T>
        void DataChangedHandler(System::Object^ sender, T e)
        {
            // Fire the event
            DataChanged(this, EventArgs::Empty);
        }

    // Once the controls are initialized you may add the event handlers
    // I put it in a constructor only for example
    MyPanel()
    {
        control_NUD->ValueChanged += gcnew EventHandler(this, &MyPanel::DataChangedHandler<EventArgs^>);
        control_DGV->RowsAdded += gcnew DataGridViewRowsAddedEventHandler(this, &MyPanel::DataChangedHandler<DataGridViewRowsAddedEventArgs^>);
        // and so on...
    }
}


/// And now in a main form we only need to subscribe to a DataChanged event
public ref class MainForm
{
    //...

    // the handler
    void MyHandler(Object^ sender, EventArgs^ e) 
    {
        markUnsaved();
    }

    void FormaddNewPanelToTab(int tab_index) 
    {
        // make the new panel
        MyPanel ^new_panel = gcnew MyPanel();
        new_panel->DataChanged += gcnew EventHandler(this, &MainForm::MyHandler);
    }

    //...
}

希望这对您有所帮助。