Để giải thích vấn đề này tôi đặt tất cả mọi thứ cần thiết vào một ứng dụng mẫu nhỏ mà hy vọng giải thích vấn đề. Tôi thực sự cố gắng đẩy mọi thứ vào càng ít dòng càng tốt, nhưng trong ứng dụng thực sự của tôi, những diễn viên khác nhau không biết nhau và cũng không nên. Vì vậy, câu trả lời đơn giản như "lấy biến một vài dòng ở trên và gọi Invoke trên nó" sẽ không hoạt động.BindingSource và Cross-Thread ngoại lệ
Vì vậy, hãy bắt đầu với mã và sau đó giải thích thêm một chút. Ban đầu, có một lớp đơn giản triển khai 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;
}
Vì vậy, không có gì đặc biệt. Và đây mã ví dụ mà chỉ đơn giản có thể được đưa vào bất kỳ dự án giao diện điều khiển ứng dụng rỗng:
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();
}
Nếu bạn muốn chạy mã này, bạn sẽ nhận được một ngoại lệ chéo chủ đề bằng cách cố gắng để thay đổi MyText
tài sản. Điều này xảy ra, gây ra các đối tượng MyData
gọi PropertyChanged
sẽ bị bắt bởi số BindindSource
. Sau đó, theo số Binding
, hãy cố gắng cập nhật thuộc tính Text
của số TextBox
. Mà rõ ràng dẫn đến ngoại lệ.
Vấn đề lớn nhất của tôi ở đây xuất phát từ thực tế là đối tượng MyData
không nên biết bất kỳ điều gì về gui (do đó là đơn giản đối tượng dữ liệu). Ngoài ra các chủ đề công nhân không biết bất cứ điều gì về một gui. Nó chỉ đơn giản là hành động trên một loạt các đối tượng dữ liệu và thao túng chúng.
IMHO tôi nghĩ rằng BindingSource
nên kiểm tra xem chuỗi nào đối tượng nhận đang sống và thực hiện một cách thích hợp Invoke()
để nhận giá trị của chúng. Tuy nhiên, câu hỏi của tôi là:
Làm thế nào có thể giải quyết ngoại lệ cross-thread này nếu đối tượng dữ liệu và chuỗi công nhân không biết gì về nguồn ràng buộc đang lắng nghe cho các sự kiện của họ để đẩy dữ liệu vào gui.
Nhưng vấn đề là tôi không biết thread UI trong lớp 'MyData'. Vì vậy, làm thế nào để gọi thread UI nếu tôi không có quyền truy cập vào bất kỳ điều khiển hiện đang ở trên biểu mẫu để gọi 'Invoke()' trên nó? – Oliver
@Oliver: hey bạn đã giải quyết vấn đề này? tôi cũng bị mắc kẹt với một cái gì đó như thế này? –
@mahesh: Thêm câu trả lời cho câu hỏi này, về cách tôi giải quyết vấn đề này. Nó không hoàn hảo, nhưng tốt hơn là không có gì. – Oliver