将线程安全的 UI 访问器保留在 C# 中的单独类中
在我的多线程应用程序中,我需要对 UI 元素进行跨线程访问,并且我正在使用线程安全方法来执行此操作。我在许多项目中反复使用它,并将它们保留在表单文件本身会使文件看起来很丑陋。所以我想创建一个单独的类,我可以在其中放置所有这些并在需要时调用它们,但我遇到了麻烦。例如,为了更改控件的文本元素,我使用以下
delegate void SetTextCallback(string text, Control ctrl);
public void SetText(string text, Control ctrl)
{
if (ctrl.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text, ctrl });
}
else
{
if (ctrl.GetType() == typeof(Label))
{
ctrl.Text = text;
}
else
{
ctrl.Text += Environment.NewLine + text;
}
}
}
代码并将此函数称为:
SetText("some text",label1);
如果它在表单类中,则效果很好,如果我将其放入另一个类中,我会在行中收到错误
this.Invoke(d, new object[] { text, ctrl });
有人能告诉我吗?我怎样才能正确地做到这一点。
另外,是否可以让一种 UI 访问器方法完成所有操作,也就是说,现在我有多种方法,例如用于更改文本的方法,一种用于更改启用的属性的方法,一种用于更改背景颜色的方法,一种用于更改前景色的方法。是否可以用类似的东西来做
public void ChangePropert(Control ctrl,Property prop,Value val)
In my multi threaded apps i need to do cross thread access on UI elements and i am using the thread safe methods to do that. I am repeatedly using this a lot in many of my projects and keeping them in the form file itself is making the file look ugly. So i want to create a seprate class where i can put all this and call them whenever needed but i am having trouble with it. For instace for changing the text element of a control i am using the following
delegate void SetTextCallback(string text, Control ctrl);
public void SetText(string text, Control ctrl)
{
if (ctrl.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text, ctrl });
}
else
{
if (ctrl.GetType() == typeof(Label))
{
ctrl.Text = text;
}
else
{
ctrl.Text += Environment.NewLine + text;
}
}
}
and call this function as
SetText("some text",label1);
This works fine if it is in the form class, if i put it into another class i am getting an error in the line
this.Invoke(d, new object[] { text, ctrl });
Can some one tell me how can i do this properly.
Also is it possible to have one UI accessor method do all the stuff, that is right now i am having multiple methods like this one to change the text one to change the enabled property one to change the back color and one to change the fore color. Is it possible to do it with something like
public void ChangePropert(Control ctrl,Property prop,Value val)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
所有这一切的问题是您开始将 UI 代码泄漏到控件实际驻留的表单之外。线程不必了解控件,它应该执行工作并更新主线程,并让主线程关心 UI 中需要做什么。
实现此目的的方法是拥有第二个线程可以调用的回调,但强制该回调实际在主线程上执行,而不是在第二个线程上执行。您可以通过使用同步上下文来完成此操作。
您需要将辅助线程包装在一个可以保留对主线程同步上下文的引用的类中。然后辅助线程可以使用它进行回调。
示例:
您的线程类将如下所示:
现在您可以摆脱每个控件上的所有调用垃圾。
The problem with all this is you are starting to leak UI code outside of the form where the controls actually reside. A thread should not have to know about controls, it should do work and update the main thread and let the main thread worry about what needs to be done in the UI.
The way to accomplish this is have a callback that a second thread can call, but force that callback to actually be executed on the main thread instead of executed on the second thread. You can accomplish this by using the Synchronization context.
You need to wrap your secondary threads in a class that can keep a reference to the main thread synchronization context. Then the secondary threads can use this for call backs.
Example:
And your thread class will look something like this:
Now you can just get rid of all the invoke crap on every control.
您可以将这些内容作为扩展方法分开。这将允许您调用对象本身的方法,而不是像现在一样将其作为参数传递。
所以你可以这样做:
label1.SetText("some text");
代替SetText("some text", label1);
另一个好处是你可以每种控件类型都有单独的实现,因此您可以使用一个用于标签,一个用于文本框。这将使代码更加清晰。
最后,关于使用反射设置属性的问题。您可以使用 Type.GetProperty() 方法获取对该属性的引用。这将返回一个 PropertyInfo 对象,您可以使用它来设置属性值,如下所示:
You could separate this stuff out as Extension methods. That would allow you to call methods in the object itself instead of passing it in as a parameter like you do now.
So you could do:
label1.SetText("some text");
instad ofSetText("some text", label1);
An additional gain would be that you could have separate implementations for each control type, so you could have one for label and one for the text box. This would make the code somewhat cleaner.
Finally, regarding your question about using reflection to set the properties. You can get a reference to the property using the Type.GetProperty() method. This returns a PropertyInfo object that you can use to set the property value like this:
就在您调试项目时,对吧?
无论如何,如果您有另一个选择不创建单独的类来操作它,您可以在每个
form
上将此CheckForIllegalCrossThreadCalls
属性设置为false
调用除自己线程之外的线程。CheckForIllegalCrossThreadCalls - MSDN
It is just while you debugging your project, right?
anyway, if you had another option not to create a separate class to manipulate this, you can set this
CheckForIllegalCrossThreadCalls
property tofalse
on eachform
that calls threads other than its own thread.CheckForIllegalCrossThreadCalls - MSDN