0

Visual Studio 2010でC#とWinformsを使用しています

シリアルポートを介して出力を読み取り、画面に出力しようとしているプログラムがあります。当初はコンソールプログラムとして開始されましたが、現在はフォームのフィールドに出力を配置したい場所に進化しています。シリアルポートから探している出力を解析して記述し、機能させるコードがあります。基本的に、Console.WriteLineをlabel.text="";に変更する必要があります。シリアルポートをリッスンする関数をGUIコードにマージして、すべてが同じファイルに含まれるようにしました。

ただし、関数をラベルに書き込む方法に夢中になっています。STATICなので、「label.text=」とだけ言うことはできません。使用する関数内に新しいフォームオブジェクトを作成しようとしましたが、フォームのコントロールにアクセスできましたが、実行時に表示されるフォームは更新されません(フォームの新しいインスタンスを作成したためだと思います)既存のインスタンスにアクセスするのではなく?)

シリアルリスナーをGUIと同時に実行する必要があるため、GUIラベルは、関数をほぼリアルタイムで実行した結果で更新されるため、スレッド化するように設定しようとしました。 、GUIはmain()によって開始される1つのスレッドであり、シリアルリスナーはボタンをクリックして開始したときに開始される別のスレッドです。ただし、system.threadingを使用して初期化するには静的である必要があるため、シリアルリスナースレッドのラベルにアクセスできないという同じ問題が発生します。

シリアルリスナーにバックグラウンドワーカーを使用する必要があるかもしれないと思っていますが、それらの経験はまったくありません。バックグラウンドワーカーはGUIのラベルをリアルタイムで更新できますか?

特定のコードを投稿することはできませんが、一般的な考え方は次のとおりです。

Main()がGUIthreadを開始します

GUIにはシリアルリスナーを開始するボタンがありますOnClickボタンはListenerThreadを開始しますListenerThreadはコンソールに出力しますが、代わりにフォームラベルに出力します

リスナーはスレッド化する必要があるため静的であるため、GUI.Labelにアクセスできません。リスナー内に新しいGUIインスタンスを作成すると、そのインスタンスのコントロールを呼び出すことができますが、実行時にGUIを更新しません。

ラベルが公開されていることを確認しました。

4

4 に答える 4

2

BackgroundWorkerクラスは基本的にこのためだけに作られました。

メソッドDoWorkに実際の作業を行わせ、ReportProgess必要に応じて作業中に呼び出されるようにします。任意のデータをstring(または必要に応じて他のデータとして)渡し、その値をProgressChangedイベントハンドラーで使用できます。この値は、フォームがUIを更新するために処理できます。

は、およびイベントがUIスレッドで実行さBackgroundWorkerれることを自動的に保証するため、これを気にする必要はありません。ProgressChangedRunWorkerCompleted

サンプルワーカーは次のとおりです。

public class MyWorker//TODO give better name
{
    public void DoWork(BackgroundWorker worker)//TODO give better name
    {
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(1000);//to mimic real work
            worker.ReportProgress(0, i.ToString());
        }
    }
}

そして、これがバックグラウンドワーカーの設定例です。ここでは、変数を閉じることができるので便利なため(つまり、これらの匿名メソッドのそれぞれで変数を使用するため)、ラムダを使用しますが、必要に応じて、各イベントハンドラーをメソッドにリファクタリングできます。

private void button1_Click(object sender, EventArgs e)
{
    var bgw = new BackgroundWorker();
    MyWorker worker = new MyWorker();

    bgw.WorkerReportsProgress = true;
    bgw.DoWork += (s, args) => { worker.DoWork(bgw); };
    bgw.ProgressChanged += (s, data) =>
    {
        label1.Text = data.UserState.ToString();
    };
    bgw.RunWorkerCompleted += (s, args) =>
    {
        label1.Text = "All Done!";
    };

    bgw.RunWorkerAsync();//actually start the worker
}

ここで、フォーム内のコントロールはいずれもパブリックではなく、静的でもないことに注意してください。また、クラスの外部でフォームへの参照を渡していません。Formそれぞれが独自のコントロールを更新する責任があるのが最良の形式であると考えられています。他の人が直接アクセスすることを許可するべきではありません。他のワーカークラスがラベルに直接アクセスしたり、そのテキストを変更したりすることを許可するのではなく、ワーカーがフォームに「データがあります。これらの値に基づいて更新することができます。 「」その場合、それ自体を更新する責任があるのはフォームです。イベントは、これらのワーカー、または他のタイプの子要素(たとえば、作成した他のフォームなど)が「親」に通知できるようにするために使用するものです。

于 2012-10-29T19:03:33.017 に答える
0

バックグラウンドワーカーが使えると思いますし、とても使いやすいです。

BackgroundWorkerを使用するには、少なくとも2つのイベントを実装する必要があります。

backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

そこであなたはあなたの入力を読みます。backgroundWorker1.RunWorkerAsync(...)の呼び出しがトリガーされます

backgroundWorker1_ProgressChanged(....)

そこでラベルを更新します。たぶん、それを更新するためにデリゲートを作成する必要があります。

次のものを実装することもできます。

backgroundWorker1_RunWorkerCompleted(....)

それが止まったときにあなたに知らせるために...

于 2012-10-29T18:48:31.033 に答える
0

Windowsコントロールに書き込むには、UIスレッドを使用している必要があります。別のスレッドで実行されているシリアルリスナーがある場合は、Windowsコントロールを変更する前にスレッドを切り替える必要があります。BeginInvokeは便利です。http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx

私がすることは、リスナーが何かを表示したいときにいつでも呼び出されるアクションをシリアルリスナーに追加することです。そして、このアクションはBeginInvokeを呼び出します。

何かのようなもの:

static class SerialListner
{
    public Action<string> SomethingToDisplay;

    void GotSomethingToDisplay(string s)
    {
        SomethingToDisplay(s);
}

そして、ウィンドウフォームのどこかに

SerialListern.SomethingToDisplay = (s) => 
   label.BeginInvoke((Action) () => label.Text = s);
于 2012-10-29T18:46:58.567 に答える
0

静的リスナーメソッドについてあなたが言ったことと、それが以前はコンソールアプリケーションであったことを続けると、比較的小さな変更は次のようになると思います。

class Program
{
    static void Main(string[] args)
    {
        // Create a main window GUI
        Form1 form1 = new Form1();

        // Create a thread to listen concurrently to the GUI thread
        Thread listenerThread = new Thread(new ParameterizedThreadStart(Listener));
        listenerThread.IsBackground = true;
        listenerThread.Start(form1);

        // Run the form
        System.Windows.Forms.Application.Run(form1);
    }

    static void Listener(object formObject)
    {
        Form1 form = (Form1)formObject;

        // Do whatever we need to do
        while (true)
        {
            Thread.Sleep(1000);
            form.AddLineToTextBox("Hello");
        }
    }
}

この場合、Form1は明らかにフォームクラスでありListener、リスニングメソッドです。ここで重要なのは、リスナーがGUIの非静的メンバーにアクセスできるように、(Thread.Startを介して)Listenメソッドに引数としてフォームオブジェクトを渡すことです。Form1.AddLineToTextBoxを次のように定義したことに注意してください。

public void AddLineToTextBox(string line)
{
    if (textBox1.InvokeRequired)
        textBox1.Invoke(new Action(() => { textBox1.Text += line + Environment.NewLine; }));
    else
        textBox1.Text += line + Environment.NewLine;
}

特に、Listenerメソッドは別のスレッドで実行されているInvokeため、変更を加えるにはGUIコントロールのメソッドを使用する必要があることに注意してください。ここではラムダ式を使用しましたが、以前のバージョンの.netをターゲットにしている場合は、完全なメソッドを同じように簡単に使用できます。私のtextBox1は、trueにTextBox設定され、falseに設定されていることに注意してください(ラベルに似ています)。MultilineReadOnly

より多くの作業が必要になる可能性がありますが、おそらくより洗練された代替アーキテクチャは、反対の依存関係を実行することです。つまり、Listenerオブジェクトへの参照を使用してフォームを作成します。次に、リスナーは、表示を更新するためにGUIがサブスクライブするイベントを発生させます。

于 2012-10-29T19:18:37.213 に答える