• 締切済み

C# イベント動作について(スレッド使用時)

現在、VS2005 Pro C#を使用してWindowsアプリケーションを作成しております。 Form1, Form2を作成し、それぞれの最大化最小化の動作を連動させる(Form1が最大(小)化したらForm2も最大(小)化する)ため以下のようなコードを作成しました。 ・Form1 //Form2の作成とResizeイベントの追加 Form2 fm2; public Form1() { InitializeComponent(); fm2 = new Form2(); fm2.Show(this); } private void Form1_Resize(object sender, EventArgs e) { fm2.WindowState = this.WindowState; } ・Form2側      //Resizeイベントの追加 private void Form2_Resize(object sender, EventArgs e) { this.Owner.WindowState = this.WindowState; } シングルスレッドのプログラムの場合これで問題なく動作します。 しかし、現在作成しているプログラムで、Form2にForm1で作成した別スレッドで計算した結果を表示させるという事をしているのですが、この場合片側のResizeイベントがうまくいかず最大最小化の動作が連動しない時があります。 デバッグで確認すると、シングルスレッドの場合、Form1,Form2のどちらかの最大最小ボタンを押せば、お互いのResizeイベントが交互に動作します。 しかし、作成しているプログラムの場合、押した側でしかResizeイベントが発生しない時がありこれが原因だと考えています。 (具体的にいうと >fm2.WindowState = this.WindowState; または >this.Owner.WindowState = this.WindowState; ↑を通っているが、別のForm側でも起こるはずのResizeイベントが発生しません。 ) シングルスレッドの場合は問題なく動作していることから、おそらく、計算に使っている別スレッドが原因かと考えていますが、スレッドを使う上での注意点がありましたらアドバイスいただけないでしょうか?

みんなの回答

回答No.2

別スレッドからフォームを操作するには、Invokeを使ってフォームを生成したスレッドに呼び出させる必要があります。これが原因かどうかは分かりませんが、参照URLに詳しく載っているので参考にしてみてください。

参考URL:
http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
itouke77
質問者

補足

ご回答ありがとうございます。 参考にさせていただきます。

回答No.1

つまり、計算処理している別スレッドからこのイベントを 呼び出しているんですよね?? デバック時にVSが教えてくれるかもしれませんが、 コントーラ(Form等)を扱っているスレッドとは異なる スレッドからコントローラ制御するにはデリゲートを使わなければなりません。 例えば、別スレッドから画面のリフレッシュをさせたい場合だと、 delegate void hogeRefreshDelegate(); private hogeRefresh() { if (this.InvokeRequired) { this.Invoke(new hogeRefreshDelegate(hogeRefresh)); } else { this.Refresh(); } } とデリゲート宣言しておいて、 別スレッドから、「hogeRefresh()」を呼び出せばいいです。 デリゲートでググればいろいろとわかりやすいサイトがあるので、 参考にしてみてください。

itouke77
質問者

補足

ご回答ありがとうございます。 >つまり、計算処理している別スレッドからこのイベントを >呼び出しているんですよね?? こちらは私の説明がわかりにくく、申し訳ありません。 現在、Form1、Form2は同時に画面に表示されています。 イベントが呼ばれるのは、スレッドからではなくそれぞれのFormの最大(最小)ボタンをマウスで押した時になります。 具体的には Form2のDataGridViewに計算結果を表示するという事をスレッドにて行っているのですが、この表示の最中(終わった後もですが)にForm1の最大化ボタンを押すと >m2.WindowState = this.WindowState; とForm2のWindowStateを変更しているつもりですが、Form2側で Resizeイベントが発生しないといった次第です。 (シングルスレッドのサンプルでは必ず発生しました。) >スレッドからコントローラ制御するにはデリゲートを使わなければなりません。 先ほど説明させていただいた >Form2のDataGridViewに計算結果を表示するという という作業はdelegateを使っています。 もしこのdelegateによるスレッドセーフの対応をしていない場合、実行した時にErrorが出ると思うのですが、この最大(最小)化ボタンに関しては出てきません。 しかし、私もこの辺りが怪しいと考えています。

関連するQ&A

  • VB.NET スレッドからのイベント受信について

    はじめて質問させていただく、VB.NET初心者です。 ただいまスレッド、イベント処理を作成中で Form1にButton1、Label1を貼り付け以下の様なプログラムを記述しました。 (スレッドにて5秒ごとにイベントを発生させます。) 'イベント引数 Public Class EventArgs Inherits System.EventArgs Public nowTime As String End Class 'イベント発行 Public Class EventTest Public Event _event(ByVal sender As Object, ByVal e As EventArgs) Public Sub Go() Dim args As New EventArgs() args.nowTime = Now.ToString RaiseEvent _event(Me, args) End Sub End Class 'フォーム内処理 'スレッド宣言 Dim MyThread As New System.Threading.Thread(AddressOf _Thread) Private WithEvents _test As New EventTest() 'イベント受信   Private Sub Handler(ByVal sender As System.Object, ByVal e As EventArgs) Handles _test._event Label1.Text = e.nowTime End Sub 'スレッド(5秒間隔でイベント発行) Private Sub _Thread() Dim i As Integer For i = 0 To 99 System.Threading.Thread.Sleep(5000) _test.Go() Next End Sub 'スレッド起動 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click MyThread.Start() End Sub End Class 開発環境から普通に実行すれば正常に動作している様なのですが、 イベント受信部(Handler)のLabel1.Text = e.nowTime部に ブレークポイントを置いてLabel1のウォッチをするとその時点で 処理がとまってしまいます。なぜとまるのかが知りたいです。 作成方法がまずいのでしょうか? 皆様よろしくお願いいたします。

  • Visual C#にてフォーム非表示時のタイマー

    Visual C# 2010でGUIプログラムを作っています。 フォームを最小化した時にタスクトレイにのみアイコンを表示する機能がついています。 タイマーで1分ごとに処理する必要があるのですが、最小化時(Form.visible = false)にだけ処理を実行してくれません。 最小化していない通常のフォーム表示の時はタイマーが正常に動作し、1分ごとに一定の処理をしてくれます。 目的は最小化時にタスクトレイにのみアイコンを表示し、1分ごとに処理をし、 特定の条件に当てはまった場合はアイコンの上にバルーン表示する事です。 タスクトレイにのみ表示している時でもタイマーで一定時間ごとに処理することはできた気がするのですが、 何が原因かわかりませんがいつの間にか出来なくなっていました。 どういった対策をすればよいでしょうか? private void Form1_Resize(object sender, EventArgs e) { if (OptionData.TaskTray == true) { if (this.WindowState == FormWindowState.Minimized) { //フォームを非表示にする this.Visible = false; //タスクトレイにアイコンを表示する notifyIcon1.Visible = true; } } } private void timer1_Tick(object sender, EventArgs e) { DataUpdateButton.PerformClick(); } private void notifyIcon1_DoubleClick(object sender, EventArgs e) { //フォームを表示する this.Visible = true; if (this.WindowState == FormWindowState.Minimized) { //ノーマルウィンドウに戻す this.WindowState = FormWindowState.Normal; //アクティブにする this.Activate(); if (OptionData.NoTaskTrayIcon == true) { //タスクトレイからアイコンを削除する notifyIcon1.Visible = false; } } }

  • 3フォームの重なり順を変えない方法+同時最小化

     今晩は、質問させていただきます。どうぞよろしくお願いいたします。 Form1(メインフォーム)の下にForm2を、 Form2の下にForm3を表示し、これらの順番が変わらないように致したく、 次のようにコーディングいたしました。 【Form1のコード】  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load    Dim form2o As New Form2    Dim form3o As New Form3    Me.Owner = form2o    form2o.Show()  End Sub 【Form2のコード】  Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load    Dim form3o As New Form3    Me.Owner = form3o    form3o.Show()  End Sub  この状態のまま、タスクバー上のアプリケーション名をクリックした際の 最小化⇔普通化を(3フォームとも一緒に)させたいのですがうまくいきません。。  Form1に、下の【コード1】のようにコーディングいたしましたら、 タスクバーのForm1クリック(最小化) →3つとも最小化(成功)いたしますが、 Form1とForm2がタスクバー上から消えてしまい、Form1クリックによる最大化ができなくなります。  またForm3に、下の【コード2】のようにコーディングいたしましたら、 タスクバーのForm3をクリックしてもForm1が一回ちらつくだけで、最小化すらできません。。  まったく違うイベントを取る方法や、Owner指定方法自体を行わない方法でも結構でございますので、 何かこの動作をうまくできる方法はないでございましょうか?  ずっと悩んでおりますが、解決致しません。もしお詳しい方がいらっしゃいましたら, 是非ともアドバイスいただきたく、どうぞよろしくお願いいたします。 【コード1】   Public Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize     Dim form2o As New Form2     If Me.WindowState = FormWindowState.Minimized Then       Owner.WindowState = FormWindowState.Minimized       Owner.Owner.WindowState = FormWindowState.Minimized     ElseIf Me.WindowState = FormWindowState.Normal Then       Owner.WindowState = FormWindowState.Normal       Owner.Owner.WindowState = FormWindowState.Normal     End If   End Sub 【コード2】   Public Sub Form3_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize     If Me.WindowState = FormWindowState.Minimized Then       Form1.WindowState = FormWindowState.Minimized       Form2.WindowState = FormWindowState.Minimized     ElseIf Me.WindowState = FormWindowState.Normal Then       Form2.WindowState = FormWindowState.Normal       Form1.WindowState = FormWindowState.Normal     End If   End Sub  何卒お願いいたします。

  • C#でスレッド実行中のイベントについて

    メインフォームの「処理開始ボタン」をクリックすると別スレッドが起動して、そのスレッド中で重たい処理をさせ、処理中に「中断ボタン」をクリックすると中断してアイドル状態に戻る様なプログラムについてですが、 先ず、Invokeを使わずに直接スレッドを起動すると期待通りの動作となり、Thread.Sleep()中でもボタンクリックのイベントが発生します。 しかし、Invokeとデリゲートを使ったスレッドを起動させるとスレッドの処理が終了するまでイベントが発生せず、行ったきり状態になってしまいます。 処理ループ内にAplication.DoEvent()を入れるとイベントが発生する様になりますが、Thread.Sleep()中はフリーズ状態となります。 Invokeを使ったスレッドでも、Invokeを使わない場合と同じ動作をさせる方法があれば教えて頂けないでしょうか。 どうぞ宜しくお願いします。

  • VB.NET:モーダルで表示したフォームを最小化

    Form2をForm1の所有として、モーダルで表示しました。 '------------------------ Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdChildMdl.Click  'Form2を開く  Dim frmN As New Form2  frmN.ShowDialog(Me) 'MeはForm1 End Sub '------------------------ Form2が最小化された時に、親フォームであるForm1も最小化させたいと思い、 Form2のResizeメソッドに以下のようなコードを書きました。 '------------------------ Private Sub Form2_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Resize  Select Case Me.WindowState   Case FormWindowState.Minimized    '親フォームも最小化((1))    Me.Owner.WindowState = FormWindowState.Minimized   Case FormWindowState.Maximized    '親フォームも最大化((2))    Me.Owner.WindowState = FormWindowState.Maximized   Case FormWindowState.Normal    '親フォームも通常サイズ((3))    Me.Owner.WindowState = FormWindowState.Normal  End Select End Sub '------------------------ しかし、(2)、(3)は問題なく実行できるのですが、 (1)を実行すると、自フォーム(Form2)が閉じてしまうのです。 そういう仕様なのでしょうか? ご存知の方、教えてください。よろしくお願いします。

  • 別スレッドとイベントの終了手順について

    C#の質問になります。 メインフォーム上で別スレッドを起動し、別スレッドからのイベントで メインフォーム上のテキストボックスにメッセージを表示しています。 サンプルソースはフォームにボタン2個とテキストボックス1個を貼り 付けたものになり、ボタン1でスレッド起動、ボタン2で停止させてい ます。 正常パターンでボタン1とボタン2を交互に押下すると意図したとおり テキストボックスにメッセージが出力されます。 このプログラムで、ボタン1を押下し別スレッドが起動した状態で、 フォームの×ボタンを押下すると別スレッドの停止処理中にJoin() 呼び出しで永久に止まってしまいます。 止めるべきスレッド中でイベント(OnTraceEvent)を呼び出している のが問題のような気がします。(この処理がなければ正常) このような時の終了手順の王道的なものはありますでしょうか。   public partial class Form1 : Form   {     ThreadTest _thread = null;     public Form1()     {       InitializeComponent();     }     private void button1_Click( object sender, EventArgs e )     {       if ( this._thread == null )       {         this._thread = new ThreadTest();         this._thread.TraceEvent += new ThreadTest.TraceEventHandler( OnTrace );         this._thread.Open();       }     }     private void button2_Click( object sender, EventArgs e )     {       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }     private void OnTrace(String message)     {       if ( this.IsHandleCreated == false )       {         return;       }       MethodInvoker process = (MethodInvoker)delegate()       {         textBox1.AppendText( message + "\r\n" );       };       if ( this.InvokeRequired )       {         this.Invoke( process );       }       else       {         process.Invoke();       }       return;     }     private void Form1_FormClosed( object sender, FormClosedEventArgs e )     {       //フォームの×ボタンを押下した時にスレッドを停止しないと       //破棄されたコントロールを操作しようとするため下記を追加       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }   }   class ThreadTest   {     public delegate void TraceEventHandler( String message );     public event TraceEventHandler TraceEvent;     protected virtual void OnTraceEvent( String message )     {       TraceEventHandler TraceEventTemp = TraceEvent;       if ( TraceEventTemp != null )       {         TraceEventTemp( message );       }     }     private Thread _threadLoop = null;     private volatile Boolean _threadFlag = false;     public void Open()     {       //スレッド開始       if ( this._threadLoop == null )       {         this._threadLoop = new Thread( new ThreadStart( Loop ) );         this._threadLoop.Start();         while ( !this._threadLoop.IsAlive ) ;       }     }     public void Close()     {       //スレッド停止       if ( this._threadLoop != null )       {         this._threadFlag = false;         //this._threadLoop.Abort();//ここを有効にすればとりあえず終了する         this._threadLoop.Join();         this._threadLoop = null;       }     }     public void Loop()     {       this._threadFlag = true;       while ( this._threadFlag )       {         OnTraceEvent( DateTime.Now.ToString( "yyyy/MM/dd hh:mm:ss:fff" ) );         Thread.Sleep( 100 );       }     }   }

  • 自作クラスのイベントを外部ハンドラでスレッドセーフ

    ■.NETモジュールプロジェクト(DLL) Public Class MyClass   Public Event hoge( ByVal sender As Object, ByVal e As EventArgs )   Private WithEvents Timer As Timers.Timer   Private Sub New()     Timer = New Timers.Timer : Timer.Interval = 10 : Timer.Start()   End Sub   Private Sub fuga( ByVal sender As Object, ByVal e As EventArgs ) Handles Timer.Elapsed     RaiseEvent hoge( Me, New EventArgs ) ' 一定時間ごとにイベントを発生させる   End Sub End Class ■上の.NETモジュールを参照している Windows Form アプリケーションプロジェクト(EXE) Public Class Form1   Private WithEvents foo As MyClass.MyClass   Public Sub New()     InitializeComponent()     foo = New MyClass.MyClass   End Sub   Private Sub bar() Handles foo.hoge     TextBox1.Text = "baz" ' TextBox1 は デザイナで Form1 に貼り付け済み   End Sub End Class という 2 プロジェクトを含むソリューションを作成しました。実行すると、 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'TextBox1' がアクセスされました。 というエラーが出てきます。 foo が MyClass ではなく、system.windows.forms.dll などに入っている Button や Form といったコントロールの場合は、上記の bar() のような書き方でもスレッドセーフに扱えるので、MyClass もForm1 のコードを修正する事なく、利用できたらなと思っています。 その場合、どのように MyClass を書けばいいでしょうか。環境は VS2008 で、.NET2.0 ベースです。

  • C#で別スレッドからメインformをアクティブ化

    質問番号:7352165で質問中の者です。 デリゲートとInvokeを使ったスレッドでは、スレッド実行中にイベントが発生しないので止む無く直接スレッドを起動しているのですが、この方法ではメインformのTextBoxなどに直接アクセスできません。 それでアクセスするメソッドをデリゲート+Invokeで呼んでおり、TextBoxの表示は正常に動作していますが、フォーカスをメインformに移動させる為にActivate();メソッドを記述すると「フィールド初期化子は静的でないフィールド、メソッド、または……を参照できません」の記述エラーとなります。 あまり理解できていないので基本的な使い方が間違っているかも知れませんが、何か解決策があればお教え下さい。 public void FormActive()  ← メインform(fmTax)に記述 {   this.Activate(); } private delegate void ActivDelegate(); ← 別スレッドに記述 ActivDelegate activDelegate = delegate() {   fmTax.FormActive(); ← ここにエラー表示 };

  • C#(VS2013)のWorkerスレッド

    Q1) C#(VS2013)のForm上に配置しました、mouseClick(...)を、Workerスレッドから 割込み起動する方法はありますか? mouseClick(...)とWorkerスレッドに関しましては、下記のコードを参照ください。 ================== private void mouseClick(object sender,MouseEventArgs e) { //これも動作する、1ピクセル単位で描画 Invalidate(); //Invalidate()は、mouseClickルーチンを抜けてからその効力を発行 } public class Worker { // Form1 fm1=new Form1(); int CNT=0; static System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch(); public void DoWork() { while(!_shouldStop) { //JOB======== while(Form1.flag_DRAWSRT==true && CNT<400) ; //CNT<400必要 //========================= ここに、mouseClick(...)を起動するコードを書きます(その質問です) //========================= //Form1.flag_DRAWSRT=true; //fm1.pictureBox1_drawA(); Thread.Sleep(100); //fm1.OnPaintA(); //=========== if(CNT==0) { sw.Reset(); sw.Start(); } if(CNT>400) { _shouldStop=true; Console.WriteLine("Period[ms]="+sw.Elapsed); } CNT++; } Console.WriteLine("worker thread: terminating gracefully."); } public void RequestStop() { _shouldStop = true; } public void RequestStart() { _shouldStop = false; } // Volatile is used as hint to the compiler that this data // member will be accessed by multiple threads. private volatile bool _shouldStop=false; }//public class Worker { 以上、宜しくお願いします。

  • C#でKeyDownイベントが発生しない。

    VS2010 CS.NET 環境です。 Formにボタンが2個あり。ボタン1はF1キー、ボタン2はF2キーで動作するようにしています。 private void Form1_KeyDown(object sender, KeyEventArgs e) {    switch (e.KeyCode)    { case Keys.F1: this.btn1.PerformClick(); break; case Keys.F2: this.btn2.PerformClick(); break; } } ボタン1は計算処理をします。 ボタン2は別のForm2を開きます。 ボタン1はF1キーで何度でも動作しますが、ボタン2はF2キーでForm2を開いて、それを閉じて元のForm1に戻ると次のF2キーではKeyDownイベントが発生しません。 この時はF1キーもKeyDownイベントが発生しなくなります。 Form1のどこかをクリックすると、KeyDownイベントが発生するようになります。 別Formを開いて戻ってもKeyDownイベントを発生させるにはどうしたらいいでしょうか? よろしくお願いします。