gpt4 book ai didi

winforms - 从另一个线程更新 WinForms(非 WPF)UI

转载 作者:行者123 更新时间:2023-12-02 19:37:41 25 4
gpt4 key购买 nike

编辑:示例代码与最初的目标相去甚远,但我实际提出的问题有一个合理的答案,因此我编辑了问题和标题。

我正在使用 WinForms GUI 编写多线程 PowerShell 脚本,并且希望从脚本中的其他线程之一更新 GUI。我根本没有运气做这件事。事实上,我什至无法让 BeginInvoke 工作。

这是一个非常精简的示例。这是示例代码,而不是生产代码,因此它并不漂亮,但它复制了问题:

$script:SynchronizedUIHash = [Hashtable]::Synchronized(@{});

[reflection.assembly]::LoadWithPartialName( "System.Windows.Forms")
$form= New-Object Windows.Forms.Form
$label = New-Object Windows.Forms.Label
$label.Text = 'original'
$script:SynchronizedUIHash.label = $label

$form.Controls.add($label)
$form.show()
[System.Windows.Forms.Application]::DoEvents()

Read-Host

$label.BeginInvoke(
[Action[string]] {
param($Message)
[Console]::Beep(500,300)
$l = $SynchronizedUIHash.label
$l.Text = $Message
[System.Windows.Forms.Application]::DoEvents()
},
'changed'
)

Read-Host

$form.close()

我什至没有听到控制台的蜂鸣声,所以就好像 BeginInvoke 代码根本没有被执行。

如果我在 Read-Host 之后添加 [System.Windows.Forms.Application]::DoEvents() ,那么我会看到更改,所以看起来是一个计时问题,但我不想进入旋转循环或类似的愚蠢的事情 - 我想触发并忘记 BeginInvoke 并让结果在适当的情况下可见。我看到其他答案,例如 PowerShell: Job Event Action with Form not executed 的答案但这需要处于检查、 sleep 、检查循环中。

一定有一种我缺少的更简单的方法......它是什么?

这里正确的解决方案是什么?

编辑:Keith 的评论让我意识到我错过了从生产到剥离示例代码的一个关键点 - 启 Action 业确实是多进程的,但示例代码不是,因此示例并不反射(reflect)现实生活,我将不得不重新考虑整个事情。总而言之 - 示例代码不使用作业,并且它也不按我的预期工作,所以我想了解原因,即使这在这种特定情况下对我没有帮助。

最佳答案

您实际上无法让控制台和 Windows 在同一线程上运行。我建议您创建另一个线程来托管窗口,如下所示(这是一个 C# 控制台应用程序,但应该很容易翻译):

class Program
{
static void Main(string[] args)
{
Form form = new Form();
Label label = new Label();
label.Text = "original";
form.Controls.Add(label);

// run this form on its own thread, so it can work properly
Thread t = new Thread((state) => Application.Run((Form)state));
t.Start(form);

Console.ReadLine();

// note you *could* do label.Text = "other" here, but BeginInvoke is always recommended
label.BeginInvoke((Action)delegate() { label.Text = "other"; });

Console.ReadLine();
Application.Exit();
}
}

编辑:我花了一段时间才制作了一个 Powershell 版本,但它就是这样。诀窍是:您无法从 Powershell 环境创建标准 .NET 线程。时期。一旦你尝试这样做,Powershell 就会崩溃。导致崩溃的根本错误是这样的:

System.Management.Automation.PSInvalidOperationException: There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type

并且,您不能真正尝试从另一个线程设置此 DefaultRunspace 变量,因为它被标记为 threadstatic(每个线程一个副本)!现在,如果您在 google 上搜索多线程 Powershell,您会发现许多有关 Powershell 作业的文章。但这些允许多任务处理,但它不是真正的多线程,它更重量级。

所以...事实证明,解决方案实际上是使用 Powershell 功能,即:Powershell 运行空间。这是:

[reflection.assembly]::LoadWithPartialName( "System.Windows.Forms")
$form = New-Object System.Windows.Forms.Form
$label = New-Object System.Windows.Forms.Label
$label.Text = 'original'
$form.Controls.add($label)

$ps = [powershell]::create()
$ps.AddScript(
{
[System.Windows.Forms.Application]::Run($form)
})
$ps.Runspace.SessionStateProxy.SetVariable("form", $form)
$ps.BeginInvoke()

Read-Host

$label.BeginInvoke(
[Action[string]] {
param($Message)
$label.Text = $Message
},
'changed'
)

Read-Host

[System.Windows.Forms.Application]::Exit()
$ps.Dispose()

关于winforms - 从另一个线程更新 WinForms(非 WPF)UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14401704/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com