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. この問題を解決する方法がわかりません。特に、私が達成しようとしていることは非常に基本的なことのように思えます。

注: statemanager のコードは、接続の状態を頻繁にチェックし、接続プロパティを変更する必要がある場合、つまり接続状態が変更された場合にイベントを発生させる単純なタイマーです。

4

2 に答える 2

4

そのイベントを発生させているタイマーなどは、フォーム (およびそのすべてのコントロール) を所有するスレッドとは別のスレッドからそれを実行しています。

これは、現在例外をスローしている明示的なチェックが存在しないという意味で、以前のバージョンの WinForms で「機能した」可能性があります。しかし、それは正しく機能しませんでした。そのため、人々が問題を抱えていることを理解できるように、これらのチェックが追加されました。

これを処理するには、次の 2 つの方法があります。

  1. イベントを呼び出すコードがセカンダリ スレッドを使用していないことを確認してください。タイマーについて言及していますが、このコードが誤って間違ったタイプのタイマーを使用している可能性があります。あなたはそれを調べるべきです。

    コードがSystem.Threading.Timerを使用している場合は、代わりにSystem.Windows.Forms.Timerを使用できるかどうかを確認します。この 2 番目のタイマーは、フォームとそのコントロールを所有する同じスレッドでイベントを呼び出す、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 でタイマーを使用していると言いました。

.NET フレームワークには、次の 3 種類の一般的なタイマーがあります。

  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 )

最初の 2 つのタイマーはスレッドを使用して作業を行っており、スレッド セーフではありません。最後の Forms.Timer は、WinForms を扱うときにタイマーを使用するための推奨される方法です。

次の 2 つのオプションがあります。

  1. StateManager タイマーが WinForm タイマーであることを確認してください。
  2. InvokeRequired メソッドを利用します: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx正しいスレッドにいるかどうかを理解するには。
于 2013-07-06T19:27:21.923 に答える