我已经痛苦地意识到需要在事件驱动的GUI代码中编写以下代码模式的频率,其中
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
Run Code Online (Sandbox Code Playgroud)
变为:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Run Code Online (Sandbox Code Playgroud)
这是C#中的一个尴尬模式,无论是记忆还是打字.有没有人提出某种捷径或构造,在一定程度上自动化?如果有一种方法可以将函数附加到执行此检查的对象,而不必经历所有这些额外的工作,如object1.InvokeIfNecessary.visible = true
类型快捷方式,那就太酷了.
上一页答案已经讨论的只是打电话的invoke()每次不切实际,甚则调用()语法既效率低下,仍然尴尬应对.
那么,有没有人想出任何捷径?
我知道,当从任何非UI线程操作UI控件时,您必须封送对UI线程的调用以避免问题.一般的共识是您应该使用测试InvokeRequired,如果为true,则使用.Invoke来执行封送处理.
这会导致很多代码看起来像这样:
private void UpdateSummary(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => UpdateSummary(text)));
}
else
{
summary.Text = text;
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:我可以省略InvokeRequired测试并只调用Invoke,如下所示:
private void UpdateSummary(string text)
{
this.Invoke(new Action(() => summary.Text = text));
}
Run Code Online (Sandbox Code Playgroud)
这样做有问题吗?如果是这样,是否有更好的方法来保持InvokeRequired测试,而不必在整个地方复制和粘贴此模式?
我已经看到了交叉线程访问GUI控件的常见设置,如下所述: 将线程安全访问方法写入Windows窗体控件的最短方法
我发现的所有网络点击描述了类似的事情.
但是,为什么我们需要检查InvokeRequired?我们不能直接调用Invoke吗?
我认为答案是否定的,所以我真正的问题是'为什么'?
我在一个Windows forms
应用程序中使用了这个函数
delegate void ParametrizedMethodInvoker5(int arg);
private void log_left_accs(int arg)
{
if (InvokeRequired)
{
Invoke(new ParametrizedMethodInvoker5(log_left_accs), arg);
return;
}
label2.Text = arg.ToString();
}
Run Code Online (Sandbox Code Playgroud)
但在WPF
它不起作用.为什么?
我是一名新手程序员,所以我可能在这里完全弄错了,但是这个问题让我更加困惑.
这实际上是这个问题的后续行动.
接受的答案是,你必须调用InvokeRequired以避免一些开销,因为你有可能已经在UI线程上运行了.
理论上,我同意它可以节省一些时间.经过一些测试后,我发现使用Invoke所需的时间比正常调用操作大两倍(测试如设置标签文本n次,或在RichTextBox中放置一个非常非常大的字符串).
但!然后有练习.
MSDN文档说:
此属性可用于确定是否必须调用invoke方法,如果您不知道哪个线程拥有控件,这可能很有用.
在大多数情况下,您确实知道何时尝试从另一个线程访问控件.实际上,我能想到的唯一情况是,从一个可以被线程X调用的方法以及所有者线程访问控件时.对我来说这是一个非常不可能的情况.
即使你真的不知道哪个线程试图操纵控件,也有一个事实是UI线程不必经常更新.对于您的GUI,25-30 fps之间的任何内容都应该没问题.并且在UI控件中进行的大多数更改所花费的时间远远少于毫秒.
因此,如果我理解正确,那么您必须检查是否需要调用的唯一情况是您何时不知道哪个线程正在访问控件以及何时GUI更新需要超过40毫秒才能完成.
然后我在http://programmers.stackexchange.com上询问了这个问题的答案.这表明,当您不需要它时,您不应该忙于过早优化.特别是如果它牺牲了代码的可读性.
所以这让我想到了一个问题:当你知道一个不同的线程访问一个控件时,你不应该只使用invoke,只有当你知道你的UI线程可以访问那段代码并且你发现它应该运行得更快时,你才应检查是否需要调用?
PS:在校对我的问题后,听起来真的像我在咆哮.但实际上我只是好奇为什么InvokeRequired似乎被许多比我更有经验的程序员所滥用.
我目前正在尝试编写一个组件,其中某些部分应该在UI线程上运行(解释会很长).所以最简单的方法是将控件传递给它,并在其上使用InvokeRequired/Invoke.但我不认为将控件引用传递给"数据/背景"组件是一个好设计,所以我正在寻找一种在UI线程上运行代码的方法,而无需提供控件.像WPF中的Application.Dispatcher.Invoke ...
任何想法,马丁
我正在.Net 2.0中构建一个非可视组件.该组件使用异步套接字(BeginReceive,EndReceive等).异步回调在运行时创建的工作线程的上下文中调用.组件用户不必担心多线程(这是主要目标,我想要的)
组件用户可以在任何线程中创建我的非可视组件(UI线程只是简单应用程序的通用线程.更严重的应用程序可以在任意工作线程中创建组件).组件触发事件,例如"SessionConnected"或"DataAvailable".
问题:由于Async Callbacks和其中引发的事件,事件处理程序在工作线程上下文中执行.我想使用一个中间层,它强制事件处理程序在首先创建组件的线程的上下文中执行.
示例代码(从异常处理等中删除...)
/// <summary>
/// Occurs when the connection is ended
/// </summary>
/// <param name="ar">The IAsyncResult to read the information from</param>
private void EndConnect(IAsyncResult ar)
{
// pass connection status with event
this.Socket.EndConnect(ar);
this.Stream = new NetworkStream(this.Socket);
// -- FIRE CONNECTED EVENT HERE --
// Setup Receive Callback
this.Receive();
}
/// <summary>
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect …
Run Code Online (Sandbox Code Playgroud) 这怎么可能?我有Windows窗体控件,派生自System.Windows.Forms.Form,此窗体中包含WebBrowser控件.Webbrowser对象实例是在form的构造函数中创建的(在InitializeComponent()方法中).然后在后台线程中我使用WebBrowser的内容进行操作,我发现在某些情况下Form.InvokeRequired == false,而WebBrowser.InvokeRequired == true.怎么会这样?
好吧,我正在编写一个对字符串,标签,链接标签,类等有用的扩展/方法文件.
但是,我有一个问题.我有一个showMessage()
更改标签文本的方法,工作正常.但我决定使用线程执行,然后我这样做:
namespace LabelExtensions
{
public static class LabelExtensionsClass
{
private delegate void UpdateState();
public static void ShowMessage(this Label label, string text)
{
if (label.InvokeRequired)
{
label.Invoke((UpdateState)delegate
{
label.Text = text;
});
}
else
{
label.Text = text;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
对不起,这是一个错字.我在论坛上输入了这段代码.错误继续.
根据文档,要使用Invoke方法需要导入:
命名空间: System.Windows.Forms
程序集: System.Windows.Forms(在System.Windows.Forms.dll中)
然后我做了:
using System.Windows.Forms;
Run Code Online (Sandbox Code Playgroud)
但是这会返回相同的错误:
The name 'Invoke' does not exist in the current context
Run Code Online (Sandbox Code Playgroud)
我怎么解决这个问题?
提前致谢.
我最近有一次非常糟糕的采访,他们会和你一起打好警察/坏警察.无论我回答什么,对他们中的一个人来说都不够好,我的信心也在逐渐缩小.他真正困惑我的最后一个问题如下:
如果控件需要InvokeRequired,那么.Invoke或.BeginInvoke会有区别吗?
让我举个例子,我是如何理解的:
public delegate string WorkLongDelegate(int i);
var del = new WorkLongDelegate(WorkLong);
var callback = new AsyncCallback(CallBack);
del.BeginInvoke(3000, callback, del);
public string WorkLong(int i)
{
Thread.Sleep(i);
return (string.Format("Work was done within {0} seconds.", i));
}
private void CallBack(IAsyncResult ar)
{
var del = (WorkLongDelegate) ar.AsyncState;
SetText2(del.EndInvoke(ar));
}
private void SetText2(string s)
{
if(InvokeRequired)
{
// What is the difference between BeginInvoke and Invoke in below?
BeginInvoke(new MethodInvoker(() => textBox1.Text = s));
}
else
{
textBox1.Text = s;
}
} …
Run Code Online (Sandbox Code Playgroud) invokerequired ×10
c# ×7
invoke ×6
winforms ×4
.net ×3
asynchronous ×1
sockets ×1
ui-thread ×1
wpf ×1