簡體   English   中英

c#線程安全控制DataBindings到BackgroundWorker修改的屬性

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM