繁体   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