采取方法System.Windows.Forms.Control.Invoke(Delegate方法)
为什么会出现编译时错误:
string str = "woop";
Invoke(() => this.Text = str);
// Error: Cannot convert lambda expression to type 'System.Delegate'
// because it is not a delegate type
Run Code Online (Sandbox Code Playgroud)
但这很好用:
string str = "woop";
Invoke((Action)(() => this.Text = str));
Run Code Online (Sandbox Code Playgroud)
当方法需要普通代表时?
我知道,当从任何非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窗体引用.有任何想法吗?
public delegate void SafeInvokeDelegate(System.Action action);
public class SafeInvoke
{
private readonly System.Windows.Forms.Control _threadControl;
public SafeInvoke()
{
_threadControl = new System.Windows.Forms.Control();
}
public void Invoke(System.Action action)
{
if (_threadControl.InvokeRequired)
_threadControl.Invoke(new SafeInvokeDelegate(Invoke), new object[] {action});
else if (action != null) action();
}
}
Run Code Online (Sandbox Code Playgroud)
上面的类可能会这样使用:
SafeInvoke _safeInvoker = new SafeInvoke();
void SafeClearItems()
{
_safeInvoker.Invoke(delegate
{
listView1.Items.Clear();
});
}
Run Code Online (Sandbox Code Playgroud)
我如何删除SafeInvoke类中的System.Windows.Forms.Control但保持相同的功能?
我是C#的新手,我正在尝试创建一个简单的客户端服务器聊天应用程序.
我在我的客户端窗体上有RichTextBox,我试图从另一个类的服务器更新该控件.当我尝试这样做时,我得到错误:"跨线程操作无效:控制textBox1从其创建的线程以外的线程访问".
这里是我的Windows窗体的代码:
private Topic topic;
public RichTextBox textbox1;
bool check = topic.addUser(textBoxNickname.Text, ref textbox1, ref listitems);
Run Code Online (Sandbox Code Playgroud)
主题类:
public class Topic : MarshalByRefObject
{
//Some code
public bool addUser(string user, ref RichTextBox textBox1, ref List<string> listBox1)
{
//here i am trying to update that control and where i get that exception
textBox1.Text += "Connected to server... \n";
}
Run Code Online (Sandbox Code Playgroud)
那怎么办呢?如何从另一个线程更新文本框控件?
我正在尝试使用.net远程处理来创建一些基本的聊天客户端/服务器应用程序.我想将Windows窗体客户端应用程序和控制台服务器应用程序作为单独的.exe文件.这里我试图从客户端调用服务器函数AddUser,我想要AddUser函数更新我的GUI.我已经修改了代码,因为你建议使用Jon,但是现在代替了跨线程异常,我得到了这个异常... "SerializationException:Assembly中的类型主题没有被标记为可序列化".
生病了我的整个代码,尽量保持简单.
任何建议都是受欢迎的.非常感谢.
服务器:
namespace Test
{
[Serializable]
public class Topic : MarshalByRefObject
{
public bool AddUser(string user, …Run Code Online (Sandbox Code Playgroud) 我正在使用单声道测试我的应用程序预装Linux端口,我有一个线程问题.我最初考虑在这里粘贴3000个代码行,但最后我设计了一个小的最小例子;)
你有一个带有按钮的表单(诗意地命名Button1,并且有一个标签(毫无意外地带有名称Label1)).整个地段都在一个叫做的形式上过着幸福的生活Form1.单击Button1启动无限循环,增加本地计数器并更新Label1(使用Invoke)以反映其值.
现在在Mono中,如果您调整表单大小,标签将停止更新,永远不会重新启动.MS实现不会发生这种情况.BeginInvoke没有更好的工作; 更糟糕的是,它使UI在两种情况下都挂起.
你知道这种差异来自哪里吗?你会如何解决它?最后,为什么BeginInvoke不在这里工作?我一定是犯了一个大错...但是哪个?
Invoke调用永远不会返回.我想知道为什么.BeginInvoke,在调整大小操作结束之前也不会执行异步调用.在MS.Net上,它们在调整大小时继续运行.代码看起来像这样(C#版本更低):
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim T As New Threading.Thread(AddressOf Increment)
T.Start()
End Sub
Sub UpdateLabel(ByVal Text As String)
Label1.Text = Text
End Sub
Delegate Sub UpdateLabelHandler(ByVal Text As String)
Sub Increment()
Dim i As Long = 0
Dim UpdateLabelDelegate …Run Code Online (Sandbox Code Playgroud) 我正在替换我用Serilog自己编写的所有现有日志记录代码.我想知道是否可以在文本框,列表视图或其他GUI控件中显示日志.如果是这样的话?我的UI是WinForms.
目前,我正在使用滚动文件接收器.除此之外,我想这样做.
我正在修改一个用C#编写的应用程序,该应用程序大量使用多线程来播放音频文件并向用户显示图像.鉴于它是多线程的,我需要经常使用Invoke方法来更改表单元素.我遇到了一种我不太满意的模式,在那里我发现自己编写频繁的,小的委托方法,通常只做一件事.一个例子如下:
delegate void setImageCallback(Image img);
private void setImage(Image img)
{
this.pictureBox1.Image = img;
}
private void someOtherMethod()
{
...
if (this.pictureBox1.InvokeRequired)
{
this.Invoke(new setImageCallback(setImage), Image.FromFile("example.png");
}
else
{
this.pictureBox1.Image = Image.FromFile("example.png");
}
...
}
Run Code Online (Sandbox Code Playgroud)
人们通常如何处理这些情况,以便您不会发现自己编写一些荒谬的代表和方法只是为了保持线程安全?显然,类似方法的合并很好,但如果我可能需要更新表单上的每个表单元素,我不希望为每个表单元素和方法都有一个"修改"委托和方法.
谢谢.
我使用以下代码来调用我的应用程序中的主UI线程上的控件.我在Status Strip中的进度条没有InvokeRequired,我需要以某种方式调用System.Windows.Forms.ToolStripProgressBar.
if (txtbox1.InvokeRequired)
{
txtbox1.Invoke(new MethodInvoker(delegate { txtbox1.Text = string.Empty; }));
}
Run Code Online (Sandbox Code Playgroud) 我有一个带有TreeView控件的UserControl,名为mTreeView.我可以从多个不同的线程获取数据更新,这些更新会导致TreeView更新.为此,我设计了以下模式:所有数据更新事件处理程序必须获取锁,然后检查InvokeRequired; 如果是这样,请通过调用Invoke来完成工作.这是相关的代码:
public partial class TreeViewControl : UserControl
{
object mLock = new object();
void LockAndInvoke(Control c, Action a)
{
lock (mLock)
{
if (c.InvokeRequired)
{
c.Invoke(a);
}
else
{
a();
}
}
}
public void DataChanged(object sender, NewDataEventArgs e)
{
LockAndInvoke(mTreeView, () =>
{
// get the data
mTreeView.BeginUpdate();
// perform update
mTreeView.EndUpdate();
});
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,有时,在启动时,我会在mTreeView.BeginUpdate()上得到一个InvalidOperationException,说mTreeView是从一个不同于它创建的线程更新的.我在调用堆栈中返回到我的LockAndInvoke,并且看,c.InvokeRequired是真的但是其他分支已被占用!就像在调用else分支后,在另一个线程上将InvokeRequired设置为true一样.
我的方法有什么问题,我该怎么做才能防止这种情况发生?
编辑:我的同事告诉我,问题是InvokeRequired是错误的,直到创建控件,所以这就是它在启动时发生的原因.但他不知道该怎么办.有任何想法吗?