C# 将委托传递给不同的形式
C# pass delegates to different forms
我有一个包含多个表单的 WinForms 应用程序。
我想要实现的是将事件处理程序从第二种形式传递到第三种形式,但我无法实现。我遇到了一个我不知道如何克服的铸造错误。
我将不胜感激帮助:
代码+下面的进一步解释:
这是应该发生的事情的粗略图像:
Form1 可以创建多个表单(它还包含我想要传递的方法)- 我可以在创建子表单时成功传递。
当我从 form2 中创建 form3 时,问题就开始了:我尝试传递事件处理程序,但我得到错误 CS0029/CS0030(转换错误)
我做错了什么以及如何解决?
编辑:
需要发生什么? -- Form3需要控制(传回数据)到Form1中放置的一个Gui控件
代码:
Form1:
public delegate void sendMessageToConsoleDelegate(string value);
public sendMessageToConsoleDelegate sendMessageToConsoleCallback;
public delegate void SetPlaceHolderDelegate(TextBox tb);
public SetPlaceHolderDelegate SetPlaceHolderCallback;
private void SetPlaceHolder(TextBox tb)
{
if (!tb.InvokeRequired)
{
if (!tb.Focused)
{
if (string.IsNullOrWhiteSpace(tb.Text))
tb.Text = tb.Tag.ToString();
return;
}
if (tb.Text == tb.Tag.ToString())
tb.Text = "";
return;
}
SetPlaceHolderDelegate call = new SetPlaceHolderDelegate(SetPlaceHolder);
tb.BeginInvoke(call, tb);
}
private void SendMessageToConsole(string msg)
{
if (!textBoxConsole.InvokeRequired)
{
textBoxConsole.AppendText(msg);
return;
}
sendMessageToConsoleDelegate call = new sendMessageToConsoleDelegate(SendMessageToConsole);
textBoxConsole.BeginInvoke(call, msg);
}
private void AddNewDeviceForm()
{
frmAddDevice add_device = new frmAddDevice(devicesDBPath);
add_device.sendMessageToConsole += SendMessageToConsole;
add_device.Show();
}
private void StartEdit()
{
frmEditDBs editdb = new frmEditDBs(devicesDBPath, commandsDBPath);
editdb.sendMessageToConsole += SendMessageToConsole;
editdb.SetPlaceHolder += SetPlaceHolder;
editdb.Show();
}
Form2 (frmEditDBs)
public delegate void EventHandler_sendMessageToConsole(string msg);
public event EventHandler_sendMessageToConsole sendMessageToConsole = delegate { };
public delegate void EventHandler_SetPlaceHolder(TextBox tb);
public event EventHandler_SetPlaceHolder SetPlaceHolder = delegate { };
private void EditDevice()
{
frmAddDevice edit_device = new frmAddDevice(devicesDBpath, current_device);
edit_device.sendMessageToConsole += sendMessageToConsole; ****<== This is the issue (same for the placeholder)****
edit_device.Show();
}
我收到错误 CS0029
如何将同一个委托传递给其他子表单(例如 frmAddDevice)?
我认为您不理解的主要概念是 delegate
与 class
、enum
、struct
等“同一级别”。您需要在一些共享范围内声明它以使其以两种形式访问。
namespace ConsoleApp6
{
public delegate void TestDelegate();
public class ClassA
{
public TestDelegate delegateA;
}
public class ClassB
{
public TestDelegate delegateB;
}
internal class Program
{
static void Main(string[] args)
{
TestDelegate del = () => { };
var classA = new ClassA()
{
delegateA = del,
};
var classB = new ClassB()
{
delegateB = classA.delegateA
};
}
}
}
或者,如果您想将其保留在表单中,则需要使用 class 名称引用它,就像使用类型一样。
namespace ConsoleApp6
{
public class ClassA
{
public delegate void TestDelegate();
public TestDelegate delegateA;
}
public class ClassB
{
public ClassA.TestDelegate delegateB;
}
internal class Program
{
static void Main(string[] args)
{
ClassA.TestDelegate del = () => { };
var classA = new ClassA()
{
delegateA = del,
};
var classB = new ClassB()
{
delegateB = classA.delegateA
};
}
}
}
您的问题是如何 C# 将委托传递给不同的表单 以便您可以(例如)从其他表单 sendMessageToConsole
到您的 MainForm
.在您的代码中,您声明这是问题所在:
// This is the issue (same for the placeholder)****
edit_device.sendMessageToConsole += sendMessageToConsole;
当我查看您的代码时,本质上您是在尝试实现您自己的 Event Pattern 版本。您的问题的一种解决方案是使用标准事件模式。然后 intellisense 将以标准方式识别您的自定义 event
delegate
:
FIRST 你需要让delegate
和继承的EventArgs
class在之外你的 MainForm
class:
namespace pass_delegates
{
public partial class MainForm : Form
{
}
// Make sure these are outside of any other class.
public delegate void SendMessageToConsoleEventHandler(object sender, SendMessageToConsoleEventArgs e);
public class SendMessageToConsoleEventArgs : EventArgs
{
public string Message { get; }
public SendMessageToConsoleEventArgs(string message)
{
Message = message;
}
}
}
您的 frmAddDevice
(此处以最小格式显示)使用 event
关键字声明委托。您的其他表格 frmEditDBs
做完全相同的事情。
public partial class frmAddDevice : Form
{
public event SendMessageToConsoleEventHandler SendMessageToConsole;
public frmAddDevice(string devicesDBpath)
{
InitializeComponent();
}
protected virtual void OnSendMessageToConsole(SendMessageToConsoleEventArgs e)
{
SendMessageToConsole?.Invoke(this, e);
}
// Clicking the button will call this as a test.
private void btnSendTestMessage_Click(object sender, EventArgs e)
{
OnSendMessageToConsole(new SendMessageToConsoleEventArgs("Message received from 'Add Device Form'"));
}
}
MainForm
代码中的一个按钮创建了一个新的 frmAddDevice
,如下所示:
frmAddDevice frmAddDevice = null;
// This handler in the Main Form creates the frmAddDevice form
private void btnFrmAddDevice_Click(object sender, EventArgs e)
{
if (frmAddDevice == null)
{
frmAddDevice = new frmAddDevice(devicesDBpath: "Some path");
// This was the problem. Not anymore ****
frmAddDevice.SendMessageToConsole += outputMessageToConsole;
}
frmAddDevice.Show();
}
private void outputMessageToConsole(object sender, SendMessageToConsoleEventArgs e)
{
textBoxConsole.AppendText(e.Message + Environment.NewLine);
}
如果您执行这些操作,您将实现您的代码试图执行的 sendMessageToConsole
的功能。从 GitHub.
下载我的示例进行试用
如前所述,您的“代表”应该在项目的命名空间中进行一般声明,而不是在特定的 class 中声明,以便它们在整个应用程序中可见。为此,可以在您的项目中为“MyDelegates”创建一个单独的文件,其内容可能类似于:
使用 System.Windows.Forms;
namespace WinHelp1
{
// Create your own delegates outside of your classes that need to be publicly
// visible within your app or even protected if so needed.
public delegate void EventHandler_SendMessageToConsole(string msg);
public delegate void EventHandler_SetPlaceHolder(TextBox tb);
}
现在,在您要定义要做什么的表单 1 中,根据适当匹配的签名进行定义
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void DoThisForConsole(string msg)
{
// whatever to do with string
}
public void DoThisForTextBox(TextBox tb)
{
// whatever to do with textbox
}
private void Btn2_Click(object sender, System.EventArgs e)
{
var f2 = new Form2();
f2.SendMessageToConsole += DoThisForConsole;
f2.SetPlaceHolder += DoThisForTextBox;
f2.ShowDialog();
// OR, if using the PARAMETERIZED for pass-through to call
// when form2 calls form 3
var f2b = new Form2( DoThisForConsole, DoThisForTextBox );
f2b.ShowDialog();
}
private void Btn3_Click(object sender, System.EventArgs e)
{
var f3 = new Form3();
f3.SendMessageToConsole += DoThisForConsole;
f3.SetPlaceHolder += DoThisForTextBox;
f3.ShowDialog();
}
}
}
首先是 form3,因为它只有直接的事件处理程序,但是您可以在 form 3 中调用
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form3 : Form
{
// now, for each form you want to USE them on...
public event EventHandler_SendMessageToConsole SendMessageToConsole;
public event EventHandler_SetPlaceHolder SetPlaceHolder;
public Form3()
{
InitializeComponent();
}
}
}
现在,您的表格 2 有点不同。由于您想让 form2 可以使用相同的事件处理程序调用 form3,只需将这些事件处理程序作为参数添加到构造函数 class。然后你可以以那种形式保存它们,但同时,self-register 它们就像在第二个按钮单击事件的 var f2b = new Form2
中一样。然后当 form2 需要调用 form3
时使用那些保留的值
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form2 : Form
{
// now, for each form you want to USE them on...
public event EventHandler_SendMessageToConsole SendMessageToConsole;
public event EventHandler_SetPlaceHolder SetPlaceHolder;
// now, for each form you want to USE them on...
public EventHandler_SendMessageToConsole passThroughForMessage;
public EventHandler_SetPlaceHolder passThroughForTextBox;
public Form2()
{
InitializeComponent();
}
public Form2(EventHandler_SendMessageToConsole forSendMsg, EventHandler_SetPlaceHolder forPlaceHolder ) : this()
{
// preserve into properties in-case you need to call form 3
passThroughForMessage = forSendMsg;
passThroughForTextBox = forPlaceHolder;
// and the constructor can auto-set for itself so IT can notify as well
if( forSendMsg != null )
SendMessageToConsole += forSendMsg;
if( forPlaceHolder != null )
SetPlaceHolder += forPlaceHolder;
}
private void Btn3_Click(object sender, System.EventArgs e)
{
var f3 = new Form3();
// and the constructor can auto-set for itself so IT can notify as well
if (passThroughForMessage != null)
f3.SendMessageToConsole += passThroughForMessage;
if (passThroughForTextBox != null)
f3.SetPlaceHolder += passThroughForTextBox;
f3.ShowDialog();
}
}
}
请记住,参数几乎可以是任何东西,您可以像其他任何东西一样将变量存储在 属性 中......只要它与相应的类型相匹配。
然后,从 form3 开始,任一实例都将调用回根实例方法。
我有一个包含多个表单的 WinForms 应用程序。
我想要实现的是将事件处理程序从第二种形式传递到第三种形式,但我无法实现。我遇到了一个我不知道如何克服的铸造错误。 我将不胜感激帮助:
代码+下面的进一步解释:
这是应该发生的事情的粗略图像:
Form1 可以创建多个表单(它还包含我想要传递的方法)- 我可以在创建子表单时成功传递。
当我从 form2 中创建 form3 时,问题就开始了:我尝试传递事件处理程序,但我得到错误 CS0029/CS0030(转换错误)
我做错了什么以及如何解决?
编辑: 需要发生什么? -- Form3需要控制(传回数据)到Form1中放置的一个Gui控件
代码:
Form1:
public delegate void sendMessageToConsoleDelegate(string value);
public sendMessageToConsoleDelegate sendMessageToConsoleCallback;
public delegate void SetPlaceHolderDelegate(TextBox tb);
public SetPlaceHolderDelegate SetPlaceHolderCallback;
private void SetPlaceHolder(TextBox tb)
{
if (!tb.InvokeRequired)
{
if (!tb.Focused)
{
if (string.IsNullOrWhiteSpace(tb.Text))
tb.Text = tb.Tag.ToString();
return;
}
if (tb.Text == tb.Tag.ToString())
tb.Text = "";
return;
}
SetPlaceHolderDelegate call = new SetPlaceHolderDelegate(SetPlaceHolder);
tb.BeginInvoke(call, tb);
}
private void SendMessageToConsole(string msg)
{
if (!textBoxConsole.InvokeRequired)
{
textBoxConsole.AppendText(msg);
return;
}
sendMessageToConsoleDelegate call = new sendMessageToConsoleDelegate(SendMessageToConsole);
textBoxConsole.BeginInvoke(call, msg);
}
private void AddNewDeviceForm()
{
frmAddDevice add_device = new frmAddDevice(devicesDBPath);
add_device.sendMessageToConsole += SendMessageToConsole;
add_device.Show();
}
private void StartEdit()
{
frmEditDBs editdb = new frmEditDBs(devicesDBPath, commandsDBPath);
editdb.sendMessageToConsole += SendMessageToConsole;
editdb.SetPlaceHolder += SetPlaceHolder;
editdb.Show();
}
Form2 (frmEditDBs)
public delegate void EventHandler_sendMessageToConsole(string msg);
public event EventHandler_sendMessageToConsole sendMessageToConsole = delegate { };
public delegate void EventHandler_SetPlaceHolder(TextBox tb);
public event EventHandler_SetPlaceHolder SetPlaceHolder = delegate { };
private void EditDevice()
{
frmAddDevice edit_device = new frmAddDevice(devicesDBpath, current_device);
edit_device.sendMessageToConsole += sendMessageToConsole; ****<== This is the issue (same for the placeholder)****
edit_device.Show();
}
我收到错误 CS0029
如何将同一个委托传递给其他子表单(例如 frmAddDevice)?
我认为您不理解的主要概念是 delegate
与 class
、enum
、struct
等“同一级别”。您需要在一些共享范围内声明它以使其以两种形式访问。
namespace ConsoleApp6
{
public delegate void TestDelegate();
public class ClassA
{
public TestDelegate delegateA;
}
public class ClassB
{
public TestDelegate delegateB;
}
internal class Program
{
static void Main(string[] args)
{
TestDelegate del = () => { };
var classA = new ClassA()
{
delegateA = del,
};
var classB = new ClassB()
{
delegateB = classA.delegateA
};
}
}
}
或者,如果您想将其保留在表单中,则需要使用 class 名称引用它,就像使用类型一样。
namespace ConsoleApp6
{
public class ClassA
{
public delegate void TestDelegate();
public TestDelegate delegateA;
}
public class ClassB
{
public ClassA.TestDelegate delegateB;
}
internal class Program
{
static void Main(string[] args)
{
ClassA.TestDelegate del = () => { };
var classA = new ClassA()
{
delegateA = del,
};
var classB = new ClassB()
{
delegateB = classA.delegateA
};
}
}
}
您的问题是如何 C# 将委托传递给不同的表单 以便您可以(例如)从其他表单 sendMessageToConsole
到您的 MainForm
.在您的代码中,您声明这是问题所在:
// This is the issue (same for the placeholder)****
edit_device.sendMessageToConsole += sendMessageToConsole;
当我查看您的代码时,本质上您是在尝试实现您自己的 Event Pattern 版本。您的问题的一种解决方案是使用标准事件模式。然后 intellisense 将以标准方式识别您的自定义 event
delegate
:
FIRST 你需要让delegate
和继承的EventArgs
class在之外你的 MainForm
class:
namespace pass_delegates
{
public partial class MainForm : Form
{
}
// Make sure these are outside of any other class.
public delegate void SendMessageToConsoleEventHandler(object sender, SendMessageToConsoleEventArgs e);
public class SendMessageToConsoleEventArgs : EventArgs
{
public string Message { get; }
public SendMessageToConsoleEventArgs(string message)
{
Message = message;
}
}
}
您的 frmAddDevice
(此处以最小格式显示)使用 event
关键字声明委托。您的其他表格 frmEditDBs
做完全相同的事情。
public partial class frmAddDevice : Form
{
public event SendMessageToConsoleEventHandler SendMessageToConsole;
public frmAddDevice(string devicesDBpath)
{
InitializeComponent();
}
protected virtual void OnSendMessageToConsole(SendMessageToConsoleEventArgs e)
{
SendMessageToConsole?.Invoke(this, e);
}
// Clicking the button will call this as a test.
private void btnSendTestMessage_Click(object sender, EventArgs e)
{
OnSendMessageToConsole(new SendMessageToConsoleEventArgs("Message received from 'Add Device Form'"));
}
}
MainForm
代码中的一个按钮创建了一个新的 frmAddDevice
,如下所示:
frmAddDevice frmAddDevice = null;
// This handler in the Main Form creates the frmAddDevice form
private void btnFrmAddDevice_Click(object sender, EventArgs e)
{
if (frmAddDevice == null)
{
frmAddDevice = new frmAddDevice(devicesDBpath: "Some path");
// This was the problem. Not anymore ****
frmAddDevice.SendMessageToConsole += outputMessageToConsole;
}
frmAddDevice.Show();
}
private void outputMessageToConsole(object sender, SendMessageToConsoleEventArgs e)
{
textBoxConsole.AppendText(e.Message + Environment.NewLine);
}
如果您执行这些操作,您将实现您的代码试图执行的 sendMessageToConsole
的功能。从 GitHub.
如前所述,您的“代表”应该在项目的命名空间中进行一般声明,而不是在特定的 class 中声明,以便它们在整个应用程序中可见。为此,可以在您的项目中为“MyDelegates”创建一个单独的文件,其内容可能类似于:
使用 System.Windows.Forms;
namespace WinHelp1
{
// Create your own delegates outside of your classes that need to be publicly
// visible within your app or even protected if so needed.
public delegate void EventHandler_SendMessageToConsole(string msg);
public delegate void EventHandler_SetPlaceHolder(TextBox tb);
}
现在,在您要定义要做什么的表单 1 中,根据适当匹配的签名进行定义
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void DoThisForConsole(string msg)
{
// whatever to do with string
}
public void DoThisForTextBox(TextBox tb)
{
// whatever to do with textbox
}
private void Btn2_Click(object sender, System.EventArgs e)
{
var f2 = new Form2();
f2.SendMessageToConsole += DoThisForConsole;
f2.SetPlaceHolder += DoThisForTextBox;
f2.ShowDialog();
// OR, if using the PARAMETERIZED for pass-through to call
// when form2 calls form 3
var f2b = new Form2( DoThisForConsole, DoThisForTextBox );
f2b.ShowDialog();
}
private void Btn3_Click(object sender, System.EventArgs e)
{
var f3 = new Form3();
f3.SendMessageToConsole += DoThisForConsole;
f3.SetPlaceHolder += DoThisForTextBox;
f3.ShowDialog();
}
}
}
首先是 form3,因为它只有直接的事件处理程序,但是您可以在 form 3 中调用
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form3 : Form
{
// now, for each form you want to USE them on...
public event EventHandler_SendMessageToConsole SendMessageToConsole;
public event EventHandler_SetPlaceHolder SetPlaceHolder;
public Form3()
{
InitializeComponent();
}
}
}
现在,您的表格 2 有点不同。由于您想让 form2 可以使用相同的事件处理程序调用 form3,只需将这些事件处理程序作为参数添加到构造函数 class。然后你可以以那种形式保存它们,但同时,self-register 它们就像在第二个按钮单击事件的 var f2b = new Form2
中一样。然后当 form2 需要调用 form3
using System.Windows.Forms;
namespace WinHelp1
{
public partial class Form2 : Form
{
// now, for each form you want to USE them on...
public event EventHandler_SendMessageToConsole SendMessageToConsole;
public event EventHandler_SetPlaceHolder SetPlaceHolder;
// now, for each form you want to USE them on...
public EventHandler_SendMessageToConsole passThroughForMessage;
public EventHandler_SetPlaceHolder passThroughForTextBox;
public Form2()
{
InitializeComponent();
}
public Form2(EventHandler_SendMessageToConsole forSendMsg, EventHandler_SetPlaceHolder forPlaceHolder ) : this()
{
// preserve into properties in-case you need to call form 3
passThroughForMessage = forSendMsg;
passThroughForTextBox = forPlaceHolder;
// and the constructor can auto-set for itself so IT can notify as well
if( forSendMsg != null )
SendMessageToConsole += forSendMsg;
if( forPlaceHolder != null )
SetPlaceHolder += forPlaceHolder;
}
private void Btn3_Click(object sender, System.EventArgs e)
{
var f3 = new Form3();
// and the constructor can auto-set for itself so IT can notify as well
if (passThroughForMessage != null)
f3.SendMessageToConsole += passThroughForMessage;
if (passThroughForTextBox != null)
f3.SetPlaceHolder += passThroughForTextBox;
f3.ShowDialog();
}
}
}
请记住,参数几乎可以是任何东西,您可以像其他任何东西一样将变量存储在 属性 中......只要它与相应的类型相匹配。
然后,从 form3 开始,任一实例都将调用回根实例方法。