teratail header banner
teratail header banner
質問するログイン新規登録
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

948閲覧

C# text boxに表示される値が変化しない

TAYAMA

総合スコア4

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2024/03/13 14:34

1

0

実現したいこと

FormAにあるスタートボタンを押したらデリゲートを作成し、500ms毎に値を1ずつカウントアップし、テキストボックスに表示出来るようにしたいです。
500ms毎に値をカウントアップさせる関数は、FormBから呼び出す仕様にしたいです。

発生している問題・分からないこと

プログラム内部ではFormBから関数が呼び出されていて、500ms毎に値がカウントアップされているところまでは出来ました。(Massagebox.show()を使って確認しました。)。

しかしFormAのテキストボックスには、初期値のみ表示されているだけで数値がカウントアップされていないです。(恐らく、表示を行う関数が1回しか呼ばれていない事が原因です。)。
InvokeをFormBの該当関数内に記載してみたりインスタンスを生成したりしたのですがエラーをはくだけで何も変わりませんでした。色々調べたりもしましたが、解決への糸口が見つけられずに困っています。
テキストボックスの値を変更させるためには、どのようにしたら良いかアドバイス頂けないでしょうか?

該当のソースコード

FormA

1using System; 2using System.CodeDom; 3using System.Collections.Generic; 4using System.ComponentModel; 5using System.ComponentModel.Design; 6using System.Data; 7using System.Diagnostics.Eventing.Reader; 8using System.Drawing; 9using System.Globalization; 10using System.Linq; 11using System.Runtime.ConstrainedExecution; 12using System.Runtime.InteropServices; 13using System.Security.Cryptography.X509Certificates; 14using System.Text; 15using System.Threading; 16using System.Threading.Tasks; 17using System.Windows.Forms; 18using System.Windows.Forms.DataVisualization.Charting; 19using System.Web; 20using static System.Windows.Forms.VisualStyles.VisualStyleElement; 21using System.Timers; 22using TextBox = System.Windows.Forms.TextBox; 23using Timer = System.Windows.Forms.Timer; 24using System.Runtime.InteropServices.WindowsRuntime; 25  26namespace TimeCount 27{ 28    public partial class FormA : Form 29    { 30        int value1 = 5; 31        int value2 = 0; 32        int value3 = 100; 33        34       public void Value1() { 35             value1++; 36        } 37        public void Value2() { 38            value2++; 39        } 40        public void Value3() 41        { 42            Value3++; 43        } 44        45        public System.Timers.Timer timer500ms = new System.Timers.Timer { AutoReset = true, Interval = 1000 }; 46        public delegate void DelegateProcess(); 47        public DelegateProcess timer500msDP; 48 49 public void ResultOutput(object sender, EventArgs e) 50        { 51            textBox1.Text = value1.Tostring(); 52 textBox2.Text = value2.Tostring(); 53 textBox3.Text = value3.Tostring(); 54 } 55 56 public void buttonStart(object sender, EventArgs e) 57        { 58            FormB fmB = new FormB();             59            timer500msDP = new DelegateProcess(ResultOutput); 60            this.Invoke(timer500msDP); 61            timer500ms.Start(); 62            timer500ms.Elapsed += fmB.500ms; 63        } 64 65        private void buttonStop(object sender, EventArgs e) 66        { 67            timer1s.Stop(); 68        }

FormB

1using System; 2using System.CodeDom; 3using System.Collections.Generic; 4using System.ComponentModel; 5using System.ComponentModel.Design; 6using System.Data; 7using System.Diagnostics.Eventing.Reader; 8using System.Drawing; 9using System.Globalization; 10using System.Linq; 11using System.Runtime.ConstrainedExecution; 12using System.Runtime.InteropServices; 13using System.Security.Cryptography.X509Certificates; 14using System.Text; 15using System.Threading; 16using System.Threading.Tasks; 17using System.Windows.Forms; 18using System.Windows.Forms.DataVisualization.Charting; 19using static System.Windows.Forms.VisualStyles.VisualStyleElement; 20using System.Timers; 21using static CanTestTool_XlDrvLib.Form2; 22using Timer = System.Windows.Forms.Timer; 23using vxlapi_NET; 24  25namespace TimeCount 26{ 27    public partial class FormB : Form 28    { 29       FormA fmA = new FormA(); 30 31       public void 500ms(Object source, System.Timers.ElapsedEventArgs e) 32       { 33            fmA.Value1(); 34            fmA.Value2(); 35            fmA.Value3(); 36        } 37 } 38}

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

InvokeやBeginInvokeを使用してみましたが、「ウィンドウハンドルが作成される前コントロールで Invoke または BeginInvoke を呼び出せません」のエラーが出て来ました。
その為、while文を使ってウィンドウハンドルが作成されるまでsleep(100)を繰り返して見ましたが、何も変わらなかったです。

補足

特になし

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOZ6.0

2024/03/13 15:32

おそらくプログラムの抜粋なのでしょうけど、コンパイルエラーが出ています。 まずは小さいプロジェクトを作ってきちんと動くようにしてみましょう。
YAmaGNZ

2024/03/13 21:33

FormBにて新しいFormAを作っていますよ FormA fmA = new FormA(); その新しいFormAに対して操作しているので表示しているFormAとは別物なので表示が変化しないのでしょう。
TAYAMA

2024/03/14 13:03

YAmaGNZさん ご回答頂き、ありがとうございます。 悩んでいた事が理解できました! FormA fmA = new FormA;の箇所をコメントアウトし、FormBのpublic void 500msの関数をpublic static void 500msに直して実行して見ました。 そしたら、初期値が表示されただけでなく値も更新できました。しかし、1秒ごとの更新ではなくボタンを1回押すごとに更新されてしまいました。 私は、FormA fmA = new FormA;を定義してInvokeを使った方がうまく行くと思い、修正&実行も試みましたが、エラーが出てきてしまいました。 行き詰まりを感じているのですが、アドバイスやヒントを頂けないでしょうか?
TAYAMA

2024/03/14 13:09

KOZ6.0さん ご回答頂き、ありがとうございます。 仰る通り、プログラムの抜粋となります。(抜粋とは言え、現在私が修正を行なっているプログラムはFormAとFormBの2つになります。) 他のプログラムはサンプルとしてもらったもので、全てある状態で実行すると、エラーは出ないのですが、期待値(1秒ごとにテキストボックスの値を1ずつ増やす)には程遠い結果となっています。 お手数おかけしてしまい、申し訳ございません。
YAmaGNZ

2024/03/14 14:26

FormBにてFormAをnewするのではなく FormAを受け取るようなプロパティなり、コンストラクタの引数なりにしてFormB側へFormAのインスタンスを渡せばいいだけだと思います。
len_souko

2024/03/14 22:06

FormA fmA = new FormA; これは呪文ではありません ちゃんと意味があります その意味を理解していないようですので、 > 私は、FormA fmA = new FormA;を定義してInvokeを使った方がうまく行くと思い 等という返答をしています まずは最低限、クラスとインスタンスについて勉強してください その理解がないと上記のコードがダメだという指摘を一生理解できません
TAYAMA

2024/03/18 15:40

YAmaGNZさん コメント頂き、ありがとうございます。 また、返信遅くなってしまい申し訳ございません。 参照渡しを使ってやって見ました。1秒ずつ値をカウントアップするところまでは出来ましたが、テキストボックスの値は変化せずでした。 まだ私の理解が甘かった所があると思うので、もう少し自分なりに考えてみます。
TAYAMA

2024/03/18 15:43

len_soukoさん コメント頂き、ありがとうございます。 また、返信遅くなってしまい申し訳ございません。 ご指摘頂いた通り、私自身完璧に理解できていなかったと思います。 もっと、クラスやインスタンスについて基本的なところを勉強し、今回出来なかった所の原因を考えてみます!
len_souko

2024/03/18 19:25

変更したソースの提示がないので断言はできませんが、ほかの方が書かれているコードを見た感じ、 > 参照渡しを使ってやって見ました。 とありますが多分参照渡しは使っていないかと思われます。 直感ですがよくjavaの人が参照渡しと誤って説明していると例に出る参照型の値渡しのことかと思われます この誤解の原因は個人的にはほとんどの言語で入門者向けに行われている変数の説明に「値を入れる箱」とあるのが原因だと思っています その言葉を流用するならば、実際には「値を入れている箱への参照のエイリアス(別名)」の方が近いかと思います 値型の場合は変数の示すポインタからその型の必要とするサイズ分連続したメモリ領域をその型として読み込んで使用しますし、参照型の場合は今だと大抵64bitの動作環境になるので、64bitつまり8byte分の連続した領域をポインタとして読み込み、そのポインタの示すメモリ領域を起点として型の示す連続した領域を使用する感じですね 参照渡しの場合は変数(の示すエイリアス)そのものを渡しているので、newした場合は呼び出し先と呼び出し元の両方のインスタンスが常に同じですが、参照型の値渡しの場合は変数自体は別で変数の(示すそれぞれのポインタに格納されている)値が同じなだけなので、呼び出し先でnewした場合は呼び出し元の変数の値と呼び出し先の変数の値は別物となってしまい、呼び出し先でそのインスタンスに対して変更した値は呼び出し下では参照されていないので見失ってしまいます c#では引数にrefなどのキーワードで参照渡しを指定できます
TAYAMA

2024/03/20 12:12

len_soukoさん ご回答頂き、ありがとうございます! 参照渡しの解説、分かりやすいです! 参考になりました^ ^ また今回の質問なのですが、無事に解決致しました。 色々アドバイスをくださり、ありがとうございます! 勉強不足の私ですが、少しずつ精進していきます!
guest

回答1

0

ベストアンサー

FormAにあるスタートボタンを押したらデリゲートを作成し、500ms毎に値を1ずつカウントアップし、テキストボックスに表示出来るようにしたいです。

System.Timers.Timerを使っているので「デリゲート」うんぬんが出てくるのでしょうが、System.Windows.Forms.Timerに変更はできないのでしょうか?
そうすれば難しいことを考えずに済むのですが...
Timer クラス (System.Timers) | Microsoft Learn
Timer クラス (System.Windows.Forms) | Microsoft Learn

cs:FormA.cs

1using System; 2using System.Windows.Forms; 3 4 5namespace Qcqonfgr3inq2r5 6{ 7 public partial class FormA : Form 8 { 9 private int value1 = 5; 10 private int value2 = 0; 11 private int value3 = 100; 12 private System.Timers.Timer timer500ms = new System.Timers.Timer 13 { 14 AutoReset = true, 15 Interval = 500, 16 }; 17 //private System.Windows.Forms.Timer timer500ms = new System.Windows.Forms.Timer 18 //{ 19 // Interval = 500, 20 //}; 21 22 public FormA() 23 { 24 InitializeComponent(); 25 } 26 27 public void Value1() { value1++; } 28 public void Value2() { value2++; } 29 public void Value3() { value3++; } 30 public void ResultOutput() 31 { 32 textBox1.Text = value1.ToString(); 33 textBox2.Text = value2.ToString(); 34 textBox3.Text = value3.ToString(); 35 } 36 37 private void buttonStart(object sender, EventArgs e) 38 { 39 FormB fmB = new FormB(this); 40 fmB.Show(); 41 42 timer500ms.Start(); 43 timer500ms.Elapsed += fmB.Timer500ms_Elapsed; 44 //timer500ms.Tick += fmB.Timer500ms_Tick; 45 } 46 47 private void buttonStop(object sender, EventArgs e) 48 { 49 timer500ms.Stop(); 50 } 51 } 52}

cs:FormB.cs

1using System; 2using System.Windows.Forms; 3 4 5namespace Qcqonfgr3inq2r5 6{ 7 public partial class FormB : Form 8 { 9 private FormA fmA; 10 11 public FormB(FormA formA) 12 { 13 InitializeComponent(); 14 fmA = formA; 15 } 16 17 public void Timer500ms_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 18 { 19 fmA.Value1(); 20 fmA.Value2(); 21 fmA.Value3(); 22 23 // Invokeで検索すると(歴史的経緯から)色々出てくるが、記述が短くて済む 24 // ラムダ式を組み込みのActionデリゲートにキャストして呼び出し 25 fmA.Invoke((Action)(() => fmA.ResultOutput())); 26 27 } 28 public void Timer500ms_Tick(object sender, EventArgs e) 29 { 30 fmA.Value1(); 31 fmA.Value2(); 32 fmA.Value3(); 33 34 // System.Windows.Forms.Timerなら(UIスレッドで呼ばれるので)何も考えないでよい 35 fmA.ResultOutput(); 36 } 37 } 38}

FormAFormBの関係がわからないので、回答コードではタイマーのイベント解除をしていません
そのため「スタートボタン」を押すたび、1ずつ・2ずつ・3ずつ...とカウントアップ数が増えていきます。

こういった方法で都度newしなければ、そのあたりを気にしないで済むかもしれません。
フォームの×ボタンで閉じないで、Hide にする。 - SN工房

追記

cs:FormA.cs

1using System; 2using System.Windows.Forms; 3 4 5namespace Qcqonfgr3inq2r5 6{ 7 public partial class FormA : Form 8 { 9 public FormA() 10 { 11 InitializeComponent(); 12 13 // FormCは閉じずにHide作戦なので、newは1回だけ 14 fmC = new FormC(this); 15 timer2.Tick += fmC.Timer_Tick; 16 } 17 18 #region FormB 19 private readonly Timer timer1 = new Timer { Interval = 500, }; 20 private FormB fmB; // 最初はnull 21 private int value1; 22 23 public void Value1() => value1++; 24 public void ResultOutput1() => textBox1.Text = value1.ToString(); 25 26 private void ButtonStart1(object sender, EventArgs e) 27 { 28 // もしFormBがすでにnewされていたら、イベント解除&閉じる 29 if (fmB != null) 30 { 31 timer1.Tick -= fmB.Timer_Tick; 32 fmB.Close(); 33 } 34 35 // FormBを作ってイベント登録&表示 36 fmB = new FormB(this); 37 timer1.Tick += fmB.Timer_Tick; 38 fmB.Show(); 39 40 timer1.Start(); 41 } 42 private void ButtonStop1(object sender, EventArgs e) => timer1.Stop(); 43 #endregion 44 45 46 #region FormC 47 private readonly Timer timer2 = new Timer { Interval = 500, }; 48 private readonly FormC fmC; 49 private int value2; 50 51 public void Value2() => value2++; 52 public void ResultOutput2() => textBox2.Text = value2.ToString(); 53 54 private void ButtonStart2(object sender, EventArgs e) 55 { 56 fmC.Show(); 57 fmC.Activate(); // ほかのウィンドウに隠れていたら気が付かないのでアクティブ化 58 fmC.WindowState = FormWindowState.Normal; // アイコン化してたら復帰 59 60 timer2.Start(); 61 } 62 private void ButtonStop2(object sender, EventArgs e) => timer2.Stop(); 63 #endregion 64 } 65}

cs:FormB.cs

1using System; 2using System.Windows.Forms; 3 4 5namespace Qcqonfgr3inq2r5 6{ 7 public partial class FormB : Form 8 { 9 private readonly FormA fmA; 10 11 public FormB(FormA formA) 12 { 13 fmA = formA; 14 InitializeComponent(); 15 } 16 17 public void Timer_Tick(object sender, EventArgs e) 18 { 19 fmA.Value1(); 20 fmA.ResultOutput1(); 21 } 22 } 23}

cs:FormC.cs

1using System; 2using System.Windows.Forms; 3 4 5namespace Qcqonfgr3inq2r5 6{ 7 public partial class FormC : Form 8 { 9 private readonly FormA fmA; 10 11 public FormC(FormA formA) 12 { 13 fmA = formA; 14 InitializeComponent(); 15 FormClosing += FormC_FormClosing; 16 } 17 18 public void Timer_Tick(object sender, EventArgs e) 19 { 20 fmA.Value2(); 21 fmA.ResultOutput2(); 22 } 23 24 private void FormC_FormClosing(object sender, FormClosingEventArgs e) 25 { 26 if (e.CloseReason == CloseReason.UserClosing) // ユーザーが閉じようとしたら... 27 { 28 e.Cancel = true; // 閉じるのを中止して 29 Hide(); // 非表示にする 30 } 31 } 32 } 33}

アプリ動画

投稿2024/03/15 09:58

編集2024/03/18 22:31
TN8001

総合スコア10112

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TAYAMA

2024/03/18 15:52

TN8001さん コメント頂き、ありがとうございます。 また、返信遅くなってしまい申し訳ございません。 私の知識不足だったのでSystem.Windows.Forms.Timerを使った方が良かった事知らなかったです。申し訳ございません。 仰る通り、私がやりたかった事が出来ました! 別のフォームから関数を呼び出し、1秒ずつカウントアップするというプログラムを作成し、C#の理解を深める事を目的として行なっています。 なので、FormAとFormBに分けて作成していました。 ただ、カウントアップ数は1ずつアップするで固定したいので、タイマーのイベント解除方法について、1度考えてみたいと思います!
TN8001

2024/03/18 22:14 編集

> 私の知識不足だったのでSystem.Windows.Forms.Timerを使った方が良かった事知らなかったです。申し訳ございません。 いえいえ謝る必要はありません(最初はみな初心者だったのですから) 使用していないusingが大量にあったので、「実際のコードではTimers.Timerを使う必要があるのかな?」と思っていました。 Forms.Timerならデザイナ上で設定もできます。 > ただ、カウントアップ数は1ずつアップするで固定したいので、タイマーのイベント解除方法について、1度考えてみたいと思います! ということは、「FormBは同時に複数開くことはない」という想定でしょうかね? 「複数開こうとしたら単に無視をする」でいいなら一番楽ですが、FormBを閉じてしまうと再度開く手段がなくなります。 2つ目を開こうとした場合は、1つ目をイベント解除して閉じれば常に1つになりますね。 そのかわり開くたびFormBの状態(位置やサイズ・TextBoxの内容等)がリセットされます。 回答内のリンクのような作りにすればイベント解除は不要ですが、FormClosingの処理が必要です(FormC) 個人的にはFormCのほうが、状態が保存されるので好みです。 回答に追記しました^^
TAYAMA

2024/03/20 12:08

TN8001さん ご回答頂き、ありがとうございます。 「FormBは同時に複数開くことはない」と言う想定です。 追記もありがとうございます^ ^ ご回答頂いた内容を参考にして、プログラムを修正し、私の希望通りの動作を得る事が出来ました! 本当にありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問