我正在构建一个基于Windows窗体的应用程序,它监听端口,当它接收到特定的命令时,它会打开一个请求输入的窗口(表单)。
我的问题是,即使我使用委托的方法打开窗口,但只绘制了窗口家具/边框。表单的内容根本不呈现。
在搜索S.O.的其他答案时,似乎有两个原因:
InitializeComponent()不被称为打开窗口。
看来2号是我的问题。当我比较表单构造函数中的ManagedThreadId和回调委托时,它们是不同的。
从文档中可以看出,委托应该确保回调在UI线程上运行。,有人能告诉我为什么不是吗?
下面是我的代码的简化版本。
表单
public wPrompt(bool silent, bool listen)
{
InitializeComponent();
// Thread.CurrentThread.ManagedThreadId returns 1
// Register a handler for scan requests received via the network
SocketListener.OpenFormRequest += OpenFormCommandHandler;
// Class that contains the code to open a socket and listen for commands
SocketListener.StartListening();
}
private void wPrompt_Load(object sender, System.EventArgs e)
{
// Thread.CurrentThread.ManagedThreadId returns 3
}
// The callback that gets called by the delegate
private void OpenFormCommandHandler()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Open the form
Visible = true;
}SocketListener类
class SocketListener
{
public delegate void OpenFormRequestEventHandler();
public static event OpenFormRequestEventHandler OpenFormRequest;
public static void StartListening()
{
// Thread.CurrentThread.ManagedThreadId returns 1
// Initialise and start worker thread
workerThread = new Thread(new ThreadStart(ListenThread));
workerThread.Start();
}
// A slightly modified version of the Synchronous Server Socket Example at
// https://learn.microsoft.com/en-us/dotnet/framework/network-programming/synchronous-server-socket-example
private static void ListenThread()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Opens socket and listens for command
if (/* command received */) {
OpenFormRequest?.Invoke();
}
}
}委托回调函数运行在与套接字侦听器相同的线程上。不是预期的UI线程。
有人能解释一下发生了什么吗?我不做太多的.net开发,所以我很难解决这个问题。
发布于 2021-05-02 22:31:02
就我从文档中可以看出的
而言,委托应该确保回调在UI线程上运行。有人能告诉我为什么不是吗?
没有任何文档可以说明这一点。也就是说,仅仅使用委托是不够的。您必须使用一种将调用移动到UI线程上的机制来调用委托,并且在上面发布的代码中没有类似的内容。不管你读到什么,你似乎都误解了。
代码的问题是,您似乎混淆了委托的编译器生成的Invoke()方法和框架提供的Control.Invoke()方法。您的代码调用前者,而您应该调用后者。前者所做的只是实际调用委托;后者是处理将委托的执行封送到UI线程上以便在那里执行的方法。
坦率地说,与套接字相关的代码试图解决这个问题是错误的。在ListenThread()方法中,只需正常地引发事件(具有讽刺意味的是,这是您正在使用的语法,因此实际上您不需要在那里更改任何内容)。在您的OpenFormCommandHandler()方法中,您应该调用Control.Invoke()方法来执行需要在那里执行的任何代码,例如创建和显示一个新表单。
根据您最近的编辑,理论上您将如何更改事件处理程序:
private void OpenFormCommandHandler()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Open the form
this.Invoke((MethodInvoker)(() => Visible = true;));
}但是我从您的问题描述中推断,表单实际上还没有显示一次,这意味着它还没有绑定到主线程,因此Control.Invoke()不太可能工作(您可能会得到一个异常,报告窗口句柄尚未创建…)。我忘记了确切的措辞,这对我来说还不够重要,我现在就去查。
假设是这样的话,您需要从其他地方获得同步上下文。不幸的是,这个问题仍然缺乏能够给出一个更明确的答案的具体细节,从而准确地说明如何做到这一点。但取决于您的程序中还发生了什么,您可以:
Form实例传递给wPrompt构造函数,并在调用Invoke()时使用该实例。或者,SynchronizationContext.Current传递给wPrompt构造函数,并调用对象的Send()或Post()方法(等效于Control.Invoke()和Control.BeginInvoke(),Post())您还可以使用其他机制来捕获并使用同步上下文,但根据您问题中的详细信息,这两种机制中的一种会比您更好。
https://stackoverflow.com/questions/67360836
复制相似问题