C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: 別クラスからフォームの操作をしたい


(過去ログ 118 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■69385 / inTopicNo.1)  別クラスからフォームの操作をしたい
  
□投稿者/ クー、 (1回)-(2013/12/21(Sat) 15:32:48)

分類:[C#] 

Visual Studio 2010のC# WindowsFormアプリケーションでプログラムを作っているのですが、
不明な点が出てきたので、質問させて頂きます。

(別ファイルの)別クラスで処理をさせて、処理の途中で進行状況を文字列で送って、Form画面に表示させたいのですが、
その動作を行うようにしても、フォーム側に変化がなく、そのクラスの処理が全て終わると、文字列がForm画面に表示されます。
ブレークポイントを置いて、処理を見てみましたがプログラムは問題なく流れているのですが、表示の更新はされません。
ただし、処理の途中でmessagebox.showを行うと、送っているはずの文字列で送って、Form画面に表示されます。
Thread.Sleep()などを置いてみましたが、それの場合は文字列は更新されません。

そのコードは少し長いので、別クラスからフォームを触るとこだけを貼ります。
-----------------
Form1.cs

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            f1 = this;
        }
        public static Form1 f1;

        public void update(string message)
        {
           label1.Text = message;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Class1 sample = new Class1();
        }
    }
-------------
Class1.cs

    class Class1
    {
        public Class1()
        {
            Form1.f1.update("こんにちは");
            Thread.Sleep(2000);
            Form1.f1.update("こんばんは");
            
        }    
    }
--------------
※実際はThread.Sleepを起きませんが、動作を確認するために置いてみました。
 Thread.Sleepを置かずに、ブレークポイントで処理の流れを確認しましたが、同じ症状で、
 Form1.f1.update("こんにちは");がpublic void update(string message)に移って、エラー無しで動くのですが、
 画面上には変化がありません。

不慣れなもので、文章が読みづらいかと思いますが、よろしくお願いします。

引用返信 編集キー/
■69386 / inTopicNo.2)  Re[1]: 別クラスからフォームの操作をしたい
□投稿者/ 魔界の仮面弁士 (471回)-(2013/12/21(Sat) 16:18:39)
No69385 (クー、 さん) に返信
> そのクラスの処理が全て終わると、文字列がForm画面に表示されます。
UIスレッドがメソッドを処理している最中は、画面には反映されません。
画面描画処理には『メッセージループ』が必要なため、通常は、
メソッド処理が完了した後のアイドル時間にて反映されることになります。

(既定では、Program.cs 内に記載されている Application.Run メソッドにて
 メッセージループが処理されています)

「別クラス」が UI スレッド上で長い処理を実行しているのであれば、
その間、画面表示は凍りつくことになります。それを避けるのであれば、
ワーカースレッドを利用することを検討してみてください。
まずは、BackgroundWorker コンポーネントを試してみるのが良いでしょう。



> messagebox.showを行うと、送っているはずの文字列で送って、Form画面に表示されます。
C# ユーザーなら、大文字小文字を区別しましょうという突っ込みはさておき。

MessageBox あるいは Form.ShowDialog は、いわゆる「モーダルダイアログ」として
表示されます。モーダルダイアログが閉じられるまでの間、呼び出し元のメソッドは
一時停止状態になっていますが、ダイアログ側がメッセージループを内包しているため、
その間、画面描画等が阻害されることはありません。

もちろん、ShowDialog で呼び出されたダイアログ内で長い処理を行えば
同じように凍りついてしまいますけれどね。
引用返信 編集キー/
■69387 / inTopicNo.3)  Re[2]: 別クラスからフォームの操作をしたい
□投稿者/ 魔界の仮面弁士 (472回)-(2013/12/21(Sat) 16:39:20)
No69386 (魔界の仮面弁士) に追記
> まずは、BackgroundWorker コンポーネントを試してみるのが良いでしょう。

ということで、まずはサンプル。

(1) Form1 に、BackgroundWorker を貼り付ける。
(2) backgroundWorker1 の WoekerReportsProgress を True に設定。
(3) backgroundWorker1 のイベントを、3つとも割り当てる(DoWork/ProgressChanged/RunWorkerCompleted)。
(4) テスト用に、起動ボタン(button1)とラベル(label1)も貼っておく。



コードとしてはこんな感じ。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    /// <summary>
    /// 別スレッドに長い処理を行わせるための起動ボタン
    /// </summary>
    private void button1_Click(object sender, EventArgs e)
    {
        UseWaitCursor = true;

        label1.Text = "はじまるよー";
        backgroundWorker1.RunWorkerAsync(480);  // 長い処理の開始を依頼

        // 別スレッドに作業を依頼した後、
        // UI スレッド側の button1_Click を抜ける。
    }

    
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // ここは、UI スレッド側で実行される処理。
        // 処理中の状況を画面に通知するなどに使われる。
        label1.Text = (string)e.UserState;
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bgw = (BackgroundWorker)sender;
        int f = (int)e.Argument;    // 呼び出し元から渡された任意のパラメータ

        System.Threading.Thread.Sleep(f);   // 何かの長い処理

        bgw.ReportProgress(0, "進捗報告1回目");   // 中間報告

        System.Threading.Thread.Sleep(f);   // 何かの長い処理

        bgw.ReportProgress(0, "進捗報告2回目");   // 中間報告

        System.Threading.Thread.Sleep(f);   // 何かの長い処理

        // 処理結果を返す
        e.Result = "終わったよー";    // ここでは文字列を返している

        Cursor.Current = Cursors.Default;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // ここは、別スレッドでの作業が終わった後に呼び出される処理。

        label1.Text = (string)e.Result;     // 結果を受け取って画面に表示

        UseWaitCursor = false;
    }

}

引用返信 編集キー/
■69389 / inTopicNo.4)  Re[2]: 別クラスからフォームの操作をしたい
□投稿者/ クー、 (2回)-(2013/12/21(Sat) 18:32:01)
No69386 (魔界の仮面弁士 さん) に返信
> ■No69385 (クー、 さん) に返信
>>そのクラスの処理が全て終わると、文字列がForm画面に表示されます。
> UIスレッドがメソッドを処理している最中は、画面には反映されません。
> 画面描画処理には『メッセージループ』が必要なため、通常は、
> メソッド処理が完了した後のアイドル時間にて反映されることになります。
>
> (既定では、Program.cs 内に記載されている Application.Run メソッドにて
>  メッセージループが処理されています)
>
> 「別クラス」が UI スレッド上で長い処理を実行しているのであれば、
> その間、画面表示は凍りつくことになります。それを避けるのであれば、
> ワーカースレッドを利用することを検討してみてください。
> まずは、BackgroundWorker コンポーネントを試してみるのが良いでしょう。
>
>
>
>>messagebox.showを行うと、送っているはずの文字列で送って、Form画面に表示されます。
> C# ユーザーなら、大文字小文字を区別しましょうという突っ込みはさておき。
>
> MessageBox あるいは Form.ShowDialog は、いわゆる「モーダルダイアログ」として
> 表示されます。モーダルダイアログが閉じられるまでの間、呼び出し元のメソッドは
> 一時停止状態になっていますが、ダイアログ側がメッセージループを内包し