[英]c# Thread-safe control DataBindings to a property modified by a BackgroundWorker
我正在努力嘗試將TextBox Text DataBind綁定到由BackgroundWorker修改的屬性。 這是我的代碼:
這是我的BackgroundWorker,它位於套接字服務器類中(此類由我的主UI線程實例化):
private void clientsConnections_DoWork(object sender, DoWorkEventArgs e)
{
try
{
while (true)
{
clientsConnections.Reset();
log.logs = "Waiting for a connection...";
server.BeginAccept(
new AsyncCallback(AcceptCallback), server);
clientsConnections.WaitOne();
}
}
catch (Exception er)
{
log.logs = er.ToString();
}
}
這是具有我想要綁定的屬性的類:
class eventsLog : INotifyPropertyChanged
{
private string log;
private string lastEntry;
public string logs
{
get { return log; }
set { lastEntry = DateTime.Now.ToString() + ": " + value + "\r\n";
log = lastEntry + log;
OnPropertyChanged("logs");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string lastlog
{
get { return lastEntry; }
}
}
以下是我嘗試將TextBox文本數據綁定到該logs屬性的方法:
Binding bi = textBox2_Output.DataBindings.Add("Text", fServer.log, "logs");
bi.ControlUpdateMode = ControlUpdateMode.OnPropertyChanged;
bi.DataSourceUpdateMode = DataSourceUpdateMode.Never;
在運行應用程序時,當BackgroundWorker更新log.logs屬性時,我得到一個InvaliOperationException,即從其他線程訪問TextBox控件,然后創建它的線程。
遺憾的是,在主UI線程中我無法檢查InvokReguired,因為它在一個由主線程實例化的類中的log.logs屬性。 線程不安全沖突是因為log.logs屬性被BackgroundWorker線程修改,我估計......?!
我也試過這樣做:
Action action = () => textBox2_Output.DataBindings.Add("Text", fServer.log, "logs");
textBox2_Output.Invoke(action);
和:
BeginInvoke((MethodInvoker)delegate
{
textBox2_Output.DataBindings.Add("Text", fServer.log, "logs");
});
沒有運氣......
我正在考慮嘗試在屬性集定義中檢查InvokRequired,但我沒有想法如何從那里引用控件。
那么,將DataBind控件轉換為某個屬性的正確方法是什么?這個屬性在某個時候被另一個線程修改了?
我試圖找到答案,但遺憾的是大多數討論是從BackgroundWorker的角度進行的,檢查InvokRequired然后進行實際的調用或線程安全控制調用。
請指出正確的方向來探索此問題的解決方案。 謝謝。
這是“Winforms”的限制,您無法更新綁定到工作線程中的控件的屬性。 WPF通過自動將控件封送到UI線程來解決這個問題。
我們需要在“Winforms”中做同樣的事情,我們使用.net 2.0提供的SynchroniazationContext
抽象來實現它。
class eventsLog : INotifyPropertyChanged
{
private SynchronizationContext context;
public eventsLog(SynchronizationContext context)
{
this.context = context ?? new SynchroniazationContext();
}
public string logs
{
get { return log; }
set {
lastEntry = DateTime.Now.ToString() + ": " + value + "\r\n";
log = lastEntry + log;
context.Post(()=> OnPropertyChanged("logs"), null);
//OnPropertyChanged("logs") will be invoked in UI thread asynchronously
}
}
...
}
當您創建一個新的eventsLog
實例時,您應該將SynchroniazationContext.Current
傳遞給構造函數,就是這樣。 您的屬性更改通知將在主線程中運行。
public MainForm()
{
this.eventsLog = new eventsLog(SynchroniazationContext.Current);
}
閱讀有關SynchronizationContext的更多信息。
注意:必須僅從UI線程創建eventsLog
實例,否則SynchroniazationContext.Current
將為null,因此將在“ThreadPool”中引發屬性更改通知,這意味着您將獲得跨線程異常。
當BackgroundWorker更新log.logs屬性時,BGW是完成還是處於其他內容之中?
如果已完成,則可以通過BGW Result傳遞更新的屬性,並讓RunWorkerCompleted句柄更新表單控件,因為RunWorkerCompleted在與表單UI相同的線程中運行。
如果BGW仍處於某些其他處理的中間,則可以在userState參數中的ProgressChanged事件中發送更新。 ReportProgress事件以整數形式訪問percentProgress,但該整數實際上並不代表0到100之間的進度百分比。它可能是步數,或者整數值可能表示一個特殊值,用於表示在表單上更新某些內容。 這些價值觀甚至可能是負面的:
只是在這里吐痰:
const int UpdateLogLogs = -1;
在BGW中,您將擁有:
worker.ReportProgress(UpdateLogLogs, value);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.