【问题标题】:InvalidOperationException when concatenating bound string连接绑定字符串时出现 InvalidOperationException
【发布时间】:2015-10-19 08:43:39
【问题描述】:

我正在尝试绑定一个 TextBox 以在 Windows 窗体中记录错误。

我像这样绑定文本框:

this.operationEventHistoryTextbox.DataBindings.Add("Text", this.Logger, "OperationLog")

Logger 对象是 Logger 类的一个实例,它包含以下属性。

public string OperationLog
{
    get
    {
       return this.operationLog;
    }
    set
    {
       if (this.operationLog != value)
       {
           this.operationLog = value;
           System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
           if (handler != null)
           {
               handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
            }
        }
    }
}

我在调用this.Logger.LogEvent("message") 时收到错误消息。我的LogEvent 方法包含以下内容:

public void LogEvent(string msg)
{
    this.OperationLog += System.DateTime.Now + ": " + msg + "\r\n";
}

InvalidOperationException 表示存在无效的跨线程操作,并且控件operationEventHistoryTextBox 是从创建它的线程之外的线程访问的。

我理解这意味着我以非线程安全的方式编写了部分代码,但我真的不明白为什么,或者要修复什么。

我可以将所有这些函数设置为调用而不是直接调用,但我想真正了解什么不起作用。

更新:我尝试使用 System.Threading.ScynchronizationContext 在正确的线程上引发 PropertyChanged 事件,但是,我继续收到错误。这是该属性的新设置器:

set
{
    if (this.operationLog != value)
    {
        System.Threading.SynchronizationContext context = System.Threading.SynchronizationContext.Current
                                                          ?? new System.Threading.SynchronizationContext();
        this.operationLog = value;
        context.Send(
            (s) =>
               {
                   System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
                   if (handler != null)
                   {
                       handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
                   }
               },
               null);
   }
}

我没有正确创建SynchronizationContext 吗?还是这里有其他工作?

更新 2: 如果我将handler(this, ... ) 的调用替换为handler(null, ... )handler(this.OperationLog),则setter 将正常运行,但不会实际更新文本。

现在我正在使用我将使用的解决方法,而不是使用 DataBinding 来链接文本,只需通过将我自己的处理程序添加到 PropertyChanged 事件来手动执行此操作。

【问题讨论】:

标签: c# multithreading winforms data-binding


【解决方案1】:

您尝试从另一个线程更新视图。 Textbox.Text 不能从其他线程设置,然后是 UI 线程

【讨论】:

    【解决方案2】:

    问题似乎是您正在从后台线程调用LogEvent 方法。由于数据绑定,文本框随后会从该后台线程更新,从而导致异常。 解决方案是确保 LogEvent 方法始终在 UI 线程上执行,或者 - 更好的是 - OperationLog setter。

    【讨论】:

    • 如果我想确保 OperationLog 的 Setter 在 UI 线程上,我该怎么做呢?如果它在我的控制类中,我可以使用operationEventHistoryTextbox.InvokeRequired 和一个委托,但由于文本框不在范围内,这将不起作用。
    • @interstellarshadow 尝试使用Application.Current.Dispatcher.Invoke
    • Application.Current.Dispatcher.Invoke, Application 中无法解析。当我将其设置为System.Windows.Forms.Application... 时,Current 不是该对象的成员
    • @interstellarshadow 你需要使用System.Windows.Application.Current.Dispatcher.Invoke
    • 好的。我在发现这一点时遇到了问题,因为 System.Windows 程序集在我的项目中不是引用的 dll——只有 system.windows.forms (见图)。编辑:我添加了 System.Windows 程序集,但它仍然不起作用
    【解决方案3】:

    好的,我找到了几乎解决方案。

    我做了以下事情:

    set
    {
        if (this.operationLog != value)
        {
            this.operationLog = value;
            System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                System.Action a  = () => handler(this, new PropertyChangedEventArgs("OperationLog"));
                System.Windows.Forms.Form.ActiveForm.Invoke(a);
            }
        }
    }
    

    这保证了在 UI 线程上引发事件,并允许它安全地更新文本框。但是,对我来说,感觉不太对劲。虽然我已经完成了它需要的工作,但感觉有些东西不适合我正在寻找的模型-视图分离。尽管如此,它肯定比手动去operationEventHistoryTextbox.Invoke( ... ) 更好。

    一个有趣的缺点是,当活动表单不与您正在处理的工作共享线程时,这可能会导致错误——这将包括其他进程中的表单,因此不允许使用 alt 制表符。

    编辑:我找到了解决该问题的方法。我没有使用System.Windows.Forms.Form.ActiveForm,而是使用System.Windows.Forms.Application.OpenForms[0] 来引用应用程序中的第一个表单。这为我提供了运行调用或开始调用的上下文。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-25
      • 2011-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-26
      • 2021-08-04
      • 2016-09-12
      • 2013-06-13
      相关资源
      最近更新 更多