2

我为我的 WinForm 应用程序编写了一点代码,通过更改颜色编码的标签控件来显示运行它的计算机的连接状态。

在我的表单中,我有以下代码:

public frmShell()
{
    InitializeComponent();
    this.stateManager = new StateManager();
}

private void frmShell_Load(object sender, EventArgs e)
{
    // Subscribe to events
    this.stateManager.ConnectionChange += new StateManager.ConnectionChangeHandler(ConnectionHasChanged);
}

private void ConnectionHasChanged(object sender, ConnectionChangeEventArgs e)
{
    if (e.ConnectionType == ConnectionType.Network)
    {
        if (e.ConnectionState == ConnectionState.Connected)
        {
            SetLabelOnline();
        }
        else
        {
            SetLabelOffline();
        }
    }
}
private void SetLabelOffline()
{
    labelConnectivityValue.Text = "Offline";
    labelConnectivityValue.ForeColor = Color.Red;
}

private void SetLabelOnline()
{
    labelConnectivityValue.Text = "Online";
    labelConnectivityValue.ForeColor = Color.Green;
}

每次我禁用网络适配器来测试我的代码时,我都会在SetLabelOnline()SetLabelOffline()方法中收到以下错误:

跨线程操作无效:控件“labelConnectivityValue”从创建它的线程以外的线程访问。

1. 我不明白为什么我的代码会被认为是无效的跨线程操作。此外,我只是以完全相同的方式重复使用我之前在另一个 WinForm 应用程序中使用的代码。

2. 我不知道如何解决这个问题,特别是考虑到我想要实现的目标似乎非常基本。

注意:状态管理器中的代码只是一个计时器,它经常检查连接的状态,并在连接属性必须更改时触发事件,即连接状态是否已更改

4

2 回答 2

4

触发该事件的计时器或诸如此类的东西是从与拥有表单(及其所有控件)的线程不同的线程中执行的。

这可能在早期版本的 WinForms 中“起作用”,因为现在抛出异常的显式检查不存在。但是,它不能正常工作这就是添加这些检查的原因,以帮助人们找出问题所在。

有两种处理方法:

  1. 确保调用事件的代码未使用辅助线程。您提到的是计时器,可能是此代码错误地使用了错误类型的计时器。你应该调查一下。

    如果代码正在使用System.Threading.Timer,请验证它是否可以改用System.Windows.Forms.Timer。第二个计时器使用构建 Winforms 系统的消息传递系统,它将调用拥有表单及其控件的同一线程上的事件。

  2. 确保事件中的代码能够应对在不同线程上执行的情况。

执行此操作的正确方法(即上面的第 2 点)是将每个调用编组到拥有表单及其控件的同一线程,您可以通过更改每个此类方法的标头来执行此操作,如下所示:

private void ConnectionHasChanged(object sender, ConnectionChangeEventArgs e)
{
    if (InvokeRequired)
    {
        Invoke(new Action(() =>
        {
            ConnectionHasChanged(sender, e);
        }));
        return;
    }

    if (e.ConnectionType == ConnectionType.Network)
    {
        ...
于 2013-07-06T19:23:05.680 回答
2

我认为您的问题的解决方案在于您写的最后一段。您说您在 StateManager 中使用了 Timer。

.NET 框架中有 3 种常见的 Timer:

  1. System.Timers.Timer ( http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx )
  2. System.Threading.Timer ( http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx )
  3. System.Windows.Forms.Timer ( http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx )

前两个计时器使用线程来完成它们的工作并且不是线程安全的。最后一个,Forms.Timer 是处理 WinForms 时使用计时器的首选方式。

你有两个选择:

  1. 确保 StateManager 计时器是 WinForm 计时器。
  2. 使用 InvokeRequired 方法: http: //msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx了解您是否在正确的线程上。
于 2013-07-06T19:27:21.923 回答