- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
为了解释这个问题,我把所有需要的东西都放到了一个小的示例应用程序中,希望能解释这个问题。我真的试图尽可能减少所有内容,但在我的实际应用程序中,这些不同的 Actor 彼此不认识,也不应该。因此,像“在上面几行中获取变量并对其调用 Invoke”这样的简单回答是行不通的。
让我们从代码开始,然后再进行更多解释。首先有一个实现 INotifyPropertyChanged 的简单类:
public class MyData : INotifyPropertyChanged
{
private string _MyText;
public MyData()
{
_MyText = "Initial";
}
public string MyText
{
get { return _MyText; }
set
{
_MyText = value;
PropertyChanged(this, new PropertyChangedEventArgs("MyText"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
所以没什么特别的。这里的示例代码可以简单地放入任何空的控制台应用程序项目中:
static void Main(string[] args)
{
// Initialize the data and bindingSource
var myData = new MyData();
var bindingSource = new BindingSource();
bindingSource.DataSource = myData;
// Initialize the form and the controls of it ...
var form = new Form();
// ... the TextBox including data bind to it
var textBox = new TextBox();
textBox.DataBindings.Add("Text", bindingSource, "MyText");
textBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
textBox.Dock = DockStyle.Top;
form.Controls.Add(textBox);
// ... the button and what happens on a click
var button = new Button();
button.Text = "Click me";
button.Dock = DockStyle.Top;
form.Controls.Add(button);
button.Click += (_, __) =>
{
// Create another thread that does something with the data object
var worker = new BackgroundWorker();
worker.RunWorkerCompleted += (___, ____) => button.Enabled = true;
worker.DoWork += (___, _____) =>
{
for (int i = 0; i < 10; i++)
{
// This leads to a cross-thread exception
// but all i'm doing is simply act on a property in
// my data and i can't see here that any gui is involved.
myData.MyText = "Try " + i;
}
};
button.Enabled = false;
worker.RunWorkerAsync();
};
form.ShowDialog();
}
如果您要运行此代码,您将通过尝试更改 MyText
属性得到一个跨线程异常。这会导致 MyData
对象调用 PropertyChanged
,这将被 BindindSource
捕获。然后,这将根据 Binding
,尝试更新 TextBox
的 Text
属性。这显然会导致异常。
我最大的问题是 MyData
对象不应该知道有关 gui 的任何信息(因为它是一个简单 数据对象)。此外,工作线程对图形用户界面一无所知。它只是作用于一堆数据对象并对其进行操作。
恕我直言,我认为 BindingSource
应该检查接收对象在哪个线程上,并执行适当的 Invoke()
以获得它们的值。不幸的是,这不是内置的(或者我错了吗?),所以我的问题是:
如果数据对象或工作线程都知道正在监听其事件以将数据推送到 gui 中的绑定(bind)源,如何解决此跨线程异常。
最佳答案
下面是上面例子中解决这个问题的部分:
button.Click += (_, __) =>
{
// Create another thread that does something with the data object
var worker = new BackgroundWorker();
worker.DoWork += (___, _____) =>
{
for (int i = 0; i < 10; i++)
{
// This doesn't lead to any cross-thread exception
// anymore, cause the binding source was told to
// be quiet. When we're finished and back in the
// gui thread tell her to fire again its events.
myData.MyText = "Try " + i;
}
};
worker.RunWorkerCompleted += (___, ____) =>
{
// Back in gui thread let the binding source
// update the gui elements.
bindingSource.ResumeBinding();
button.Enabled = true;
};
// Stop the binding source from propagating
// any events to the gui thread.
bindingSource.SuspendBinding();
button.Enabled = false;
worker.RunWorkerAsync();
};
所以这不会再导致任何跨线程异常。此解决方案的缺点是您不会在文本框中显示任何中间结果,但总比没有好。
关于c# - BindingSource 和跨线程异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7079474/
我是一名优秀的程序员,十分优秀!