C++/CLI 将附加参数传递给事件处理方法
C++/CLI passing additional arguments to event handling method
我正在尝试编写一个在运行时创建大量按钮的 C++/CLI 表单应用程序:我有一个字符串向量,并且为每个字符串创建一个按钮:
std::vector<std::string> strings;
/*
string being initialized with values from file
*/
for ( std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it ) {
Button ^ button = gcnew Button;
/*
button being customized depending on the string
*/
buttonPannel->Controls->Add(button);
}
现在我想要做的是为每个按钮添加一个事件处理程序,将用于自定义按钮的字符串传递给处理方法。
在 c# 中我会写类似
button->Click += new EventHandler((sender, args) => button_Click(s, e, *it));
如何在 C++/CLI 中实现这一点?
您可以执行与 C# 代码完全相同的操作,但我宁愿使用 Button
class 上的现有 属性 来保存您需要的额外数据.
在这种情况下,Tag
属性 似乎是合适的:它的目的是保存您需要的与控件密切相关的任何额外数据,因此这似乎 on-point驱动程序逻辑的字符串。 (您可能需要将其设为托管 String^
对象,而不是 std::string
,但这是一个简单的转换。)
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
button->Tag = marshal_as<String^>(*it);
button->Click += gcnew EventHandler(this, &Form1::button_Click);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e)
{
Control^ senderControl = dynamic_cast<Control^>(sender);
String^ heroName = nullptr;
if(senderControl != nullptr)
heroName = dynamic_cast<String^>(senderControl->Tag);
if(heroName == nullptr)
{
// Something went wrong. Bail out.
return;
}
// ...
}
如果您真的想执行与 C# 代码等效的操作:您的 C# lambda 正在对 it
变量执行变量捕获。我们可以在 C++/CLI 中进行变量捕获,只是需要更多的手动操作。
(注意:您的 C# 示例正在捕获迭代器,而不是字符串,不确定这是否是预期的。我写这个是为了捕获字符串对象。)
ref class EventHandlerStringCapture
{
public:
EventHandlerStringCapture(std::string str,
Action<Object^, EventArgs^, std::string>^ handler)
{
this->str = str;
this->handler = handler;
}
void eventHandler(Object^ sender, EventArgs^ e)
{
this->handler(sender, e, this->str);
}
private:
std::string str;
Func<Object^, EventArgs^, std::string>^ handler;
}
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
// The variable to capture.
std::string str = *it;
// The actual event handler: a method in the current class.
Action<Object^, EventArgs^, std::string>^ actualHandler =
gcnew Action<Object^, EventArgs^, std::string>(this, &Form1::button_Click);
// Pass both the variable to capture and the
// actual event handler to a helper object.
EventHandlerStringCapture^ ehsc =
gcnew EventHandlerStringCapture(str, actualHandler);
// Grab the two-parameter event handler from the helper object,
// and make that the click handler.
button->Click +=
gcnew EventHandler(ehsc, &EventHandlerStringCapture::eventHandler);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e, std::string heroName)
{
// ...
}
(注意:我不是编译器,所以可能会有语法错误。)
显然,在按钮对象上使用现有的 属性 更简单,但 C++/CLI 等同于 C# 编译器在幕后所做的工作。
我正在尝试编写一个在运行时创建大量按钮的 C++/CLI 表单应用程序:我有一个字符串向量,并且为每个字符串创建一个按钮:
std::vector<std::string> strings;
/*
string being initialized with values from file
*/
for ( std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it ) {
Button ^ button = gcnew Button;
/*
button being customized depending on the string
*/
buttonPannel->Controls->Add(button);
}
现在我想要做的是为每个按钮添加一个事件处理程序,将用于自定义按钮的字符串传递给处理方法。
在 c# 中我会写类似
button->Click += new EventHandler((sender, args) => button_Click(s, e, *it));
如何在 C++/CLI 中实现这一点?
您可以执行与 C# 代码完全相同的操作,但我宁愿使用 Button
class 上的现有 属性 来保存您需要的额外数据.
在这种情况下,Tag
属性 似乎是合适的:它的目的是保存您需要的与控件密切相关的任何额外数据,因此这似乎 on-point驱动程序逻辑的字符串。 (您可能需要将其设为托管 String^
对象,而不是 std::string
,但这是一个简单的转换。)
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
button->Tag = marshal_as<String^>(*it);
button->Click += gcnew EventHandler(this, &Form1::button_Click);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e)
{
Control^ senderControl = dynamic_cast<Control^>(sender);
String^ heroName = nullptr;
if(senderControl != nullptr)
heroName = dynamic_cast<String^>(senderControl->Tag);
if(heroName == nullptr)
{
// Something went wrong. Bail out.
return;
}
// ...
}
如果您真的想执行与 C# 代码等效的操作:您的 C# lambda 正在对 it
变量执行变量捕获。我们可以在 C++/CLI 中进行变量捕获,只是需要更多的手动操作。
(注意:您的 C# 示例正在捕获迭代器,而不是字符串,不确定这是否是预期的。我写这个是为了捕获字符串对象。)
ref class EventHandlerStringCapture
{
public:
EventHandlerStringCapture(std::string str,
Action<Object^, EventArgs^, std::string>^ handler)
{
this->str = str;
this->handler = handler;
}
void eventHandler(Object^ sender, EventArgs^ e)
{
this->handler(sender, e, this->str);
}
private:
std::string str;
Func<Object^, EventArgs^, std::string>^ handler;
}
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
// The variable to capture.
std::string str = *it;
// The actual event handler: a method in the current class.
Action<Object^, EventArgs^, std::string>^ actualHandler =
gcnew Action<Object^, EventArgs^, std::string>(this, &Form1::button_Click);
// Pass both the variable to capture and the
// actual event handler to a helper object.
EventHandlerStringCapture^ ehsc =
gcnew EventHandlerStringCapture(str, actualHandler);
// Grab the two-parameter event handler from the helper object,
// and make that the click handler.
button->Click +=
gcnew EventHandler(ehsc, &EventHandlerStringCapture::eventHandler);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e, std::string heroName)
{
// ...
}
(注意:我不是编译器,所以可能会有语法错误。)
显然,在按钮对象上使用现有的 属性 更简单,但 C++/CLI 等同于 C# 编译器在幕后所做的工作。