C# c线程安全控件数据绑定到BackgroundWorker修改的属性

C# c线程安全控件数据绑定到BackgroundWorker修改的属性,c#,data-binding,thread-safety,controls,backgroundworker,C#,Data Binding,Thread Safety,Controls,Backgroundworker,我正在努力将文本框文本数据绑定到由BackgroundWorker修改的属性。 这是我的密码: 这是我的BackgroundWorker,位于socket服务器类中。该类由我的主UI线程实例化: private void clientsConnections_DoWork(object sender, DoWorkEventArgs e) { try { while (true) {

我正在努力将文本框文本数据绑定到由BackgroundWorker修改的属性。 这是我的密码:

这是我的BackgroundWorker,位于socket服务器类中。该类由我的主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属性时,我收到一个InvalioOperationException,即TextBox控件是从创建它的线程以外的线程访问的

遗憾的是,在主UI线程中,我无法检查InvokeReguired,因为log.logs属性位于主线程实例化的类中。 线程不安全冲突是因为log.logs属性被BackgroundWorker线程修改,我想

我也尝试过这样做:

 Action action = () => textBox2_Output.DataBindings.Add("Text", fServer.log, "logs");
 textBox2_Output.Invoke(action);
以及:

没有运气

我正在考虑在属性集定义中检查InvokRequired,但是我不知道如何从那里引用控件

那么,将控件数据绑定到另一个线程在某个点修改的属性的正确方法是什么

我试图找到答案,但遗憾的是,大多数讨论都是从BackgroundWorker的角度进行的,检查InvokRequired,然后执行实际的调用或线程安全控制调用

请给我指出正确的方向,探索解决这个问题的方法。
谢谢。

这是Winforms的限制,您无法更新绑定到工作线程中控件的属性。WPF通过自动将控件封送到UI线程来解决这个问题

我们在Winforms中也需要这样做,我们使用.NET2.0提供的SynchronizationContext抽象来实现它

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实例时,应将SynchronizationContext.Current传递给构造函数,仅此而已。您的属性更改通知将在主线程中运行

public MainForm()
{
    this.eventsLog = new eventsLog(SynchroniazationContext.Current);
}
阅读更多关于


注意:必须仅从UI线程创建eventsLog实例,否则SynchronizationContext.Current将为null,因此将在ThreadPool中引发属性更改通知,这意味着您将获得跨线程异常。

这是Winforms的限制,无法更新绑定到工作线程中控件的属性。WPF通过自动将控件封送到UI线程来解决这个问题

我们在Winforms中也需要这样做,我们使用.NET2.0提供的SynchronizationContext抽象来实现它

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实例时,应将SynchronizationContext.Current传递给构造函数,仅此而已。您的属性更改通知将在主线程中运行

public MainForm()
{
    this.eventsLog = new eventsLog(SynchroniazationContext.Current);
}
阅读更多关于


注意:必须仅从UI线程创建eventsLog实例,否则SynchronizationContext.Current将为null,因此将在ThreadPool中引发属性更改通知,这意味着您将获得跨线程异常。

当BackgroundWorker更新log.logs属性时,BGW是否已完成或处于其他操作的中间? 如果已完成,则可以通过BGW结果传递更新的属性,并让RunWorkerCompleted处理表单控件的更新,因为RunWorkerCompleted与表单UI在同一线程中运行

如果BGW仍在进行其他处理,则可以在userState参数中的ProgressChanged事件中发送更新。ReportProgress事件以整数形式访问percentProgress,但该整数实际上不必表示0-100之间的进度百分比。它可能是步数,或者整数值可能表示一个特殊值,以表示将某些内容更新回表单。这些值甚至可以是负值:

就在这里说:

const int UpdateLogLogs = -1;
在BGW中,您将拥有:

worker.ReportProgress(UpdateLogLogs, value);

当BooWrWork更新Log.Logic属性时,BGW是否已完成或处于其他操作的中间? 如果已完成,则可以通过BGW结果传递更新的属性,并让RunWorkerCompleted处理表单控件的更新,因为RunWorkerCompleted与表单UI在同一线程中运行

如果BGW仍在进行其他处理,则可以在userState参数中的ProgressChanged事件中发送更新。ReportProgress事件以整数形式访问percentProgress,但该整数实际上不必表示0-100之间的进度百分比。它可能是步数,或者整数值可能表示一个特殊值,以表示将某些内容更新回表单。这些值甚至可以是负值:

就在这里说:

const int UpdateLogLogs = -1;
在BGW中,您将 n有:


5年多了,这仍然帮我省了很多钱!非常感谢。5年多了,这仍然帮我省了很多钱!非常感谢。