0

Sharedクラスを使用してアプリケーションのすべての共通オブジェクトを格納するWindowsフォームアプリケーションがあります。設定クラスには、定期的に処理を実行するオブジェクトのコレクションがあります。次に、興味深いものがあります。メインフォームにアラートを送信し、更新する必要があります。

現在、オブジェクトのイベントを介してこれを行っています。各オブジェクトが作成されたら、イベントハンドラーを追加してイベントをフォームにマップします。ただし、これらのリクエストがフォームのメインコピーで常に終了するとは限らないことを示唆する問題が発生しています。たとえば、私のフォームには通知トレイアイコンがありますが、フォームがイベントをキャプチャしてバブルを表示しようとすると、バブルは表示されません。ただし、そのコードを変更してアイコンを表示し(既に表示されていますが)、バブルを表示すると、2番目のアイコンが表示され、バブルが正しく表示されます。

誰かがこれに遭遇したことがありますか?フォームの単一のインスタンスによってすべてのイベントを強制的にキャプチャする方法はありますか、それともこれを処理するまったく異なる方法がありますか?必要に応じてコードサンプルを投稿できますが、これは一般的なスレッドの問題だと思います。

詳細:現在、フォームのイベントハンドラーでMe.InvokeRequiredを使用していますが、この場合は常にFALSEが返されます。また、このフォームから表示したときに作成された2番目のトレイアイコンにはコンテキストメニューがありませんが、「実際の」アイコンにはあります-それは誰かの手がかりになりますか?

髪を抜くぞ!これはそれほど難しいことではありません!

解決策:手がかりを与えてくれたnobugzに感謝します。それにより、現在使用しているコードにたどり着きます(これは美しく機能しますが、これを行うためのより良い方法があると思わずにはいられません)。「IsPrimary」というフォームにプライベートブール変数を追加し、フォームコンストラクターに次のコードを追加しました。

    Public Sub New()
        If My.Application.OpenForms(0).Equals(Me) Then
            Me.IsFirstForm = True
        End If
    End Sub

この変数が設定されてコンストラクターが終了すると、イベントハンドラーに直接移動し、このように処理します(警告:探しているフォームはアプリケーションのプライマリフォームであるため、My.Application.OpenForms( 0)必要なものを取得します。スタートアップ以外のフォームの最初のインスタンスを探していた場合は、それが見つかるまで繰り返す必要があります):

    Public Sub EventHandler()
        If Not IsFirstForm Then
            Dim f As Form1 = My.Application.OpenForms(0)
            f.EventHandler()
            Me.Close()
        ElseIf InvokeRequired Then
            Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
        Else
            ' Do your event handling code '
        End If
    End Sub

まず、正しいフォームで実行されているかどうかを確認します。実行されていない場合は、正しいフォームを呼び出します。次に、スレッドが正しいかどうかを確認し、正しくない場合はUIスレッドを呼び出します。次に、イベントコードを実行します。潜在的に3回の呼び出しになるのは好きではありませんが、別の方法を考えることはできません。少し面倒ですが、うまくいくようです。誰かがそれを行うためのより良い方法を持っているなら、私はそれを聞いてみたいです!

繰り返しになりますが、すべての助けに感謝します-これは私を狂わせるつもりでした!

4

4 に答える 4

3

糸脱毛の問題でもあると思います。イベントハンドラーでControl.Invoke()を使用していますか?.NETは通常、アプリをデバッグするときに違反をキャッチしますが、できない場合もあります。NotifyIconはその1つであり、スレッドの親和性をチェックするためのウィンドウハンドルはありません。

OPが質問を変更した後に編集します。

従来のVB.NETトラップは、フォームインスタンスをそのタイプ名で参照することです。Form1.NotifyIcon1.Somethingのように。スレッドを使用すると、期待どおりに機能しません。既存のインスタンスを使用せずに、Form1クラスの新しいインスタンスを作成します。そのインスタンスは表示されず(Show()は呼び出されませんでした)、メッセージループをポンプしないスレッドで実行されているため、ドアネイルとして機能しなくなります。2番目のアイコンが表示されるのを見るのは死んだプレゼントです。したがって、スレッドから使​​用していることがわかっている場合は、InvokeRequired=Falseを取得しています。

既存のフォームインスタンスへの参照を使用する必要があります。それを実現するのが難しい場合(通常、クラスコンストラクターの引数として「Me」を渡す)、Application.OpenFormsを使用できます。

  Dim main As Form1 = CType(Application.OpenForms(0), Form1)
  if (main.InvokeRequired)
    ' etc...
于 2008-11-07T21:13:08.530 に答える
0

C#では次のようになります。

private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(StatusHandler, sender, e);
        }
        else
        {
          //do work
        }
    }
于 2008-12-02T23:05:23.110 に答える
0

Control.InvokeRequiredを使用して適切なスレッドを使用しているかどうかを判断し、そうでない場合はControl.Invokeを使用します。

于 2008-11-07T21:13:20.077 に答える
0

フォームのInvokeメソッドのドキュメントを参照してください。これにより、フォームを更新するコードを、フォームを所有するスレッドで実行できるようになります(Windowsフォームはスレッドセーフではありません)。Private Delegate Sub UpdateStatusDelegate(ByVal newStatus as String)のようなもの

Public sub UpdateStatus(ByVal newStatus as String)If Me.InvokeRequired Then Dim d As New UpdateStatusDelegate(AddressOf UpdateStatus)Me.Invoke(d、new Object(){newStatus})Else'フォームステータスを更新しますEndIf

サンプルコードを提供していただければ、よりカスタマイズされた例を提供させていただきます。

OPがInvokeRequiredを使用していると言った後に編集します。

InvokeRequiredを呼び出す前に、フォームハンドルが作成されていること、および私が信じているHandleCreatedプロパティがあることを確認してください。コントロールに現在ハンドルがない場合、InvokeRequiredは常にfalseを返します。これは、正しいことを行ったとしても、コードがスレッドセーフではないことを意味します。見つけたら質問を更新してください。いくつかのサンプルコードも役立ちます。

于 2008-11-07T21:14:27.937 に答える