WPF 从其他线程访问 GUI
Posted
技术标签:
【中文标题】WPF 从其他线程访问 GUI【英文标题】:WPF access GUI from other thread 【发布时间】:2010-03-02 14:46:39 【问题描述】:我正在处理仅制作 WPF 应用程序单个实例的要求。 但是 - 我必须将命令行传递给第一个实例,然后执行一些 UI 操作。
我正在使用 Mutext 检查已运行的实例,我确实使用 NamedPipes 将命令行传输到已运行的实例。
当然,我不在正确的线程中访问“Window1”。 我尝试在静态类中存储对“Window1”的引用,然后使用 Dispatcher 在“Window1”中调用方法,但是,一旦我尝试访问变量(“Window1”中的类范围),我就会收到“对象引用未设置为对象的实例。”
UI 操作是向 TabControl 添加一个新选项卡 - 在新选项卡的初始化过程中完成了一些工作 - 并且变量被初始化,甚至我想要调用的方法在初始化期间也有效 - 但是当从调度失败。
任何提示,如何做到这一点?我是不是走错了路?
谢谢!
【问题讨论】:
【参考方案1】:这很简单:
void ProcessCommandLine(string commandLine)
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
... code to process the command line here ...
);
您可以从 App.Startup 以及从命名管道接收消息的线程调用它。
这里的主要考虑因素是:
-
使用 BeginInvoke 代替 Invoke 来防止调用线程等待
使用 DispatcherPriority.ApplicationIdle 来保证应用程序在处理命令行之前完成初始化
在 Window1 尚未初始化的情况下使用 Application.Current.Dispatcher 而不是 Window1.Dispatcher
【讨论】:
伟大的信息射线。我不久前发布的解决方案是我遇到的解决我遇到的问题并简单地转发该信息的东西。我做了更多的研究,发现了你发布的同样的东西(BeginInvoke 可能会解决 OP 的问题)。我对有关使用 lambda 表达式而不是回调的差异的更多信息感兴趣。我使用我在几个位置发布的代码,如果有更好的方法,我正在考虑更改它。您是否有任何链接可以进行更深入的解释?再次感谢! @Scott:lambda 表达式实际上只是语法糖。我在其他评论中提到的性能差异的原因是跳过了对 Dispatcher.CheckAccess 的调用。 Dispatcher.Invoke 已经在内部执行了 Dispatcher.CheckAccess(),因此在调用 Dispatcher.Invoke 之前执行此操作会重复工作并使代码混乱。当您在同一个线程上时,性能是等效的,但当您不在时,如果我们手动调用 CheckAccess(),它会被调用三次(一次在 Invoke 之前,一次来自 Invoke 代码,一次在递归调用中)。 再次感谢您的回复!这很有趣!我发现的所有文章(包括 msdn)都建议添加 Dispatcher.CheckAccess 以防止不必要的调用(请参阅链接)。我不打算听起来怀疑,但我希望你能指出一些文件。感谢您分享您的知识!链接:msdn.microsoft.com/en-us/library/…social.msdn.microsoft.com/Forums/en-US/wpf/thread/…blog.decarufel.net/2009/03/… @Scott:这三个链接都不适用: 1. CheckAccess 文档中的示例用于在直接调用和 BeginInvoke(不是 Invoke)之间做出决定,因此它实际上改变了时间。 2. 论坛帖子仅在 DispatcherPriority 值较低时有效:在 DispatcherPriority.Send 中,Invoke 进行直接调用,不涉及队列。您可以通过在 UI 线程上尝试 BeginInvoke 和 Invoke 来验证这一点。 Invoke 将首先执行。 3. Eric De C# 误读了他引用的材料,特别是他将“VerifyAccess”读为“CheckAccess”,然后继续由此产生的误解。 再次感谢!感谢您抽出宝贵时间回答我的问题!【参考方案2】:这不对,您确定互斥锁正确地将控制权传递给您当前正在运行的应用程序实例吗?
如果是线程 UI 访问问题,您应该收到此错误:调用线程无法访问此对象,因为不同的线程拥有它。
您得到“对象引用未设置为对象的实例”这一事实。错误消息表示您尚未将该对象实例化为新对象。
【讨论】:
我同意这个评估。我也相信这里还有其他问题。我会尝试调试并找到您尝试访问的 null 对象。 似乎我进入了某种竞争状态,因为我是一步一步构建的,所以我非常关注为什么这个该死的对象没有被初始化。我目前的工作解决方案是从 WinForms 借来的糟糕的旧 DoEvents()。几项测试表明它现在可以正常工作,但如果有人有比 DoEvents() 更好的主意,我会很高兴,....谢谢!! +1 表示它不是 WPF 的调度程序在抱怨以上是关于WPF 从其他线程访问 GUI的主要内容,如果未能解决你的问题,请参考以下文章
Wpf中“由于其他线程拥有此对象,因此调用线程无法对其进行访问”
Wpf中“由于其他线程拥有此对象,因此调用线程无法对其进行访问”的问题