質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

Q&A

解決済

3回答

2528閲覧

同じボタンを押すたびに同じラベルに違う文字列を表示するには?

sgsgsg1983

総合スコア1

C#

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

0グッド

0クリップ

投稿2022/06/02 02:55

よろしくお願いします。
初心者なのですがC# において一つのフォームの中に一つのボタンと一つのラベルがあって、
例えばボタンを1回押すとラベルの表示が初期表示のaからbになる、2回目押すと
cになって、もう1度押すとaに戻ってきて、それを繰り返す、
という処理は出来ますか?
例えばボタンクリックの処理の中に

C#

1 2 private void button1_Click(object sender, EventArgs e) 3 { 4 5 label1.Text = "b"; 6 if (label1.Text == "b") 7 label1.Text = "c"; 8 9 10 }

と書くといきなりラベルにcが表示されます。
とりあえず書いてみた全体のコードです。

C#

1 2using System; 3using System.Collections.Generic; 4using System.ComponentModel; 5using System.Data; 6using System.Drawing; 7using System.Drawing.Text; 8using System.Linq; 9using System.Text; 10using System.Threading.Tasks; 11using System.Windows.Forms; 12 13namespace aiueo 14{ 15 16 17 public partial class Form1 : Form 18 { 19 20 21 public Form1() 22 { 23 InitializeComponent(); 24 } 25 26 private void button1_Click(object sender, EventArgs e) 27 { 28 29 label1.Text = "b"; 30 if (label1.Text == "b") 31 label1.Text = "c"; 32 33 34 } 35 36 } 37 38}

while(true) や配列の処理を使うのでしょうか?

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2022/06/02 03:05

質問する際は何を何で作っているかを質問の一行目に書きましょう。(例: Windows Forms アプリを Visual Studio 2022 でフレームワークを .NET Framework 4.8 として作っています・・・とか)
dodox86

2022/06/02 03:09

> と書くといきなりラベルにcが表示されます。 ... > label1.Text = "b"; > if (label1.Text == "b") > label1.Text = "c"; いや、いきなり自分で”b”をセットしているのですから当然なのですが、、、処理の流れを考えましょう。それとも自分では分からないので、「どのようにコードを書いたら要望通りの動きになりますか?」と言う質問でしょうか。
sgsgsg1983

2022/06/03 01:22

二人の追記者ありがとうございます。 Windows Forms アプリを Visual Studio 2022で作ってました。 舌足らずですみませんでした。 あと上記のような処理をそもそもできるのかと、出来れば実装のコードも 知りたくて質問させていただきました。
guest

回答3

0

ベストアンサー

まずなぜいきなりCが表示されかですが、
label1.Text = "b";
上記でlabel1.Textにbを設定しています。
if (label1.Text == "b")
上記では、label1.Textにbが設定されているため、必ずIF文が成立します。
label1.Text = "c";
よって、上記が必ず実行されます。
なので、毎回Cが表示されることになります。

べたな書き方ですが、理解しやすい実装としては下記になります。

C#

1 public Form1() 2 { 3 InitializeComponent(); 4 label1.Text = "a"; 5 } 6 7 private void button1_Click(object sender, EventArgs e) 8 { 9 if (label1.Text == "a"){ 10 label1.Text = "b"; 11 }else if(label1.Text == "b"){ 12 label1.Text = "c"; 13 }else{ 14 label1.Text = "a"; 15 } 16 }

投稿2022/06/02 04:29

kikukiku

総合スコア514

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

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

sgsgsg1983

2022/06/02 04:38

教えてくださったコードでとりあえず動きました。 ありがとうございました。
Zuishin

2022/06/02 05:09

低評価した時はちゃんと理由を書きましょう。
Zuishin

2022/06/02 05:12

ちゃんと低評価しやすいよう、隙のある回答をしたんだから書けるでしょう?
kikukiku

2022/06/02 05:48

ちなみにコメントせずに低評価することはしませんよ。 勝手に犯人扱いしないでください。 通報しますよ
Zuishin

2022/06/02 06:05

ああここはあなたの回答でしたか。自分のと間違えました。 しかし、ここには質問者もコメントしているのに、自分に言われたことと確信があるようですね。 だから何だというわけではありませんが。
sgsgsg1983

2022/06/03 01:23

>Zuishinさん ちなみに僕は低評価ボタン押してませんよ。
Zuishin

2022/06/03 02:41 編集

最近、何の間違いもない回答に無言で嫌がらせの低評価をする人がいるので、時々こうして自分の回答を編集したりコメントしたりしてアピールしています。 今回は他のことをしている間にフォーカスの位置が変わったので間違えただけで、誰か特定の人のことを言っているわけではありません。 私の回答は、元々質問の回答として間違ってはいないが質問者向けではないとわかって書いたものですから、使えるよう勉強する必要もありません。「このような方法もある」と第三者に示したかっただけです。 色物ですが、応用範囲が広いことだけは間違いありません。 kikukiku さんの回答は確かに動きますが、あまりにも泥縄式で、実際に使うと笑われる類いのものだし、教科書で教えられることもないでしょう。 初心者がイベントドリブンを学ぶときには、プロパティやフィールドを使った状態管理から習うはずです。(fana さんの二つ目のコード) fana さんの一つ目のコードはそれより一歩進んだもので、GUI とロジックを分離したエレガントで実用的な解法です。私も高評価していますが、同じ学ぶならこれを学ぶべきです。これを志すか否かで、生産性が天と地ほど変わってきます。
guest

0

簡素なやつを.

例えばこんなのでも用意すれば…

csharp

1//順繰りに文字列をどうのする作業者 2class CyclicalStr 3{ 4 private readonly string[] m_Str; 5 private int m_CurrIndex; 6 7 public CyclicalStr( IEnumerable<string> strs, int init_index=0 ) 8 { m_Str = strs.ToArray(); m_CurrIndex = (init_index % m_Str.Length ); } 9 10 public string Current{ get{ return m_Str[m_CurrIndex]; } } 11 public CyclicalStr ToNext(){ m_CurrIndex = ( (m_CurrIndex+1) % m_Str.Length ); return this; } 12}

Form1 の実装はこんな感じか.

csharp

1private CyclicalStr m_StrDecider; 2 3public Form1() 4{ 5 InitializeComponent(); 6 7 m_StrDecider = new CyclicalStr( new string[]{ "a", "b", "c" } ); 8 label1.Text = m_StrDecider.Current; 9} 10 11private void button1_Click(object sender, EventArgs e) 12{ 13 label1.Text = m_StrDecider.ToNext().Current; 14}

[追記]
同じことを全部 Form1 に書いたらこうなる.

csharp

1public partial class Form1 : Form 2{ 3 //m_Str は,ラベルに表示する3つの文字列を持っています. 4 //m_CurrIndex は,現在ラベルに何番目の文字列を表示しているのかを覚えておく変数です. 5 //(つまり,ラベルには m_Str[ m_CurrIndex ] を表示するということ) 6 private readonly string[] m_Str = new string[3]{ "a", "b", "c" }; 7 private int m_CurrIndex = 0; 8 9 public Form1() 10 { 11 InitializeComponent(); 12 13 //ラベルに初期の文字列をセット 14 label1.Text = m_Str[ m_CurrIndex ]; 15 } 16 17 private void button1_Click(object sender, EventArgs e) 18 { 19 //m_CurrIndexを更新 20 //(ここを実行するたびに 0->1->2->0->1->2->0-> ... と変わる) 21 m_CurrIndex = (m_CurrIndex+1) % 3; 22 //ラベルの文字列をセット 23 label1.Text = m_Str[ m_CurrIndex ]; 24 } 25}

投稿2022/06/02 04:18

編集2022/06/03 01:43
fana

総合スコア11654

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

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

sgsgsg1983

2022/06/03 01:24

回答ありがとうございました。 自分はまだここまで複雑なコードを理解できません。 勉強してみます。
fana

2022/06/03 01:46 編集

最も「複雑じゃない」形を目指して書いたつもりだったのだが… CyclicalStrクラスが持っているデータやメソッドを全部ダイレクトに Form1 クラス側に持たせる形の方がわかりやすいのだろうか? ↓ 一応,そういうのも追記しておいた.
fana

2022/06/03 01:50

何を「複雑」と述べているのか,的な意味合いが違うのだろう. 追記した版は「文法や文字数の観点では平易(=複雑じゃない)」形と言えるかな.
BeatStar

2022/06/03 05:36 編集

fanaさん 横から失礼します。 多分、なぜ追記のようなコードになったのか書いた方がいいかもしれません。 私も入門者の頃は何も考えずに丸暗記しようとしていました。理由を説明するとなおわかりやすいかもしれません…
fana

2022/06/03 08:28 編集

えっと…理由……? 「本当にやりたいこと」はこうかな?と推測し,→それを素直に実装したから…かな. 例えば,「本当にやりたいこと」が, ・【ボタンを押す毎にラベルの表示を順繰りに変えたい】 ・【「現在のラベルの表示内容に基づいて次のラベルの表示内容を決定して」ラベルの表示を変える】 のどちらなのかによって実装は変わるでしょう. 私が推測したのは前者の側だったから, 別の回答にあるような > if( label1.Text == "a" ) という実装にはならなかった.(この形は後者の側に対応している実装である) 要は(?),「現在のラベルの表示内容」というのを「アプリケーションの状態を表すデータ」だとして扱うかどうか,というところが違う. 私は「ラベルとはいわゆる View でしかなく,アプリケーションのデータを保持する存在ではない」…という考えの元にコードを書いた,とでも言えばよいのかな.
fana

2022/06/03 08:36 編集

そういう話ではなくて, 「配列とその配列のindex用の値 みたいなデータを持っている形」だとか「なにやら剰余を用いている」だとかいう実装詳細についての理由を問うているのであれば, それは単に「これが最も短く書けるように思ったから」程度の話でしかないので,そこは「こだわらなくていい/別の実装形態でもよい」部分かと. 別の回答内で述べられている > プロパティやフィールドを作って状態管理する という例を提示しているにすぎないので.
BeatStar

2022/06/03 09:31

すみません、紛らわしかったですね… 私が意図していたのは「なぜ余剰や配列を用いているのか」です。流れというか、なぜそうなるのかがわかりにくいのです。初心者って。 たとえば数学的に証明するとかでしょうか。
fana

2022/06/04 02:51 編集

ボタンが押された回数を数えるカウンタでも用意して,その値に基づいて表示文字列を決めることを考えるとする. カウンタ値は最初は0で,ボタンを押すごとに 1,2,3,... と増えていく,と. ↓ カウンタ値に対する表示文字列を決める if でも何でも書けばいいよね.じゃあそこの条件ってどうなるのか? 0のときに"a", 1のときに"b", 2のときに"c" と表示するとして,次の3からはどうするのか? また "a" から繰り返したいということは… ↓ ・0, 3, 6, 9, ... のときは"a" ・1, 4, 7, 10, ... のときは"b" ・2, 5, 8, 11, ... のときは"c" っていう話だ. これが「ボタンを押した回数カウンタの値を3で割った余り が{0,1,2}のどれなのか? っていう場合分けである」ということはさすがにちょっと考えればわかるっしょ,としか言えないなぁ.小学生レベルの算数の話なのだし. (示したサンプルでは,保持している値(この話で言うところのカウンタ値に相当するもの)自体を常に3で割った余りにしているけども,話の内容的には一緒) 配列を使う/使わない は本当にどうでもいい実装形態の話でしかない.使いたくないなら使わなければいい.3パターンの分岐程度なら if や swith を使って書いてもいい. 何で使ったか?って問われても困る.「0,1,2,... っていう形の素直な値に対して文字列を決めるのに利用できる最もシンプルなデータ構造だと思ったから…??」
fana

2022/06/04 02:43

「剰余」に考えが及ばないとしても,そこで詰む話でもないよね. 【0,1,2,0,1,2,0, ... って値を繰り返すような実装があれば事足りる】のだから,素朴に m_CurrIndex++; if( m_CurrIndex>2 )m_CurrIndex=0; みたく書いたっていい. コレよりも剰余を使う記述が優れているとも思わないし. (むしろコレの方が素直な表現である分だけ良いかもしれない.)
fana

2022/06/04 02:47

正直,どうでもいい誤差程度の話に理由を問われても困る. (せめてもうちょっと,アルゴリズム的な,というか,やり方で優劣的な差が出る内容じゃないと…)
guest

0

実行できるサンプルコードを記載します。

イベントを await で受け取る」に記載のコードを使うと、イベントスパゲティを簡単にほどき、次のように for 文で扱うことができるようになります。

なお、初心者の学習向けのコードではありません。
誰か他の人が回答してくれるでしょうが、フォームにプロパティやフィールドを作って状態管理するのが定石です。

csharp

1using System; 2using System.Threading.Tasks; 3using System.Windows.Forms; 4using Zuishin.WaitEvent; 5 6namespace WindowsFormsApp1 7{ 8 public partial class Form1 : Form 9 { 10 public Form1() 11 { 12 InitializeComponent(); 13 14 // ToggleButtonTextWithClick に button1 を登録 15 _ = ToggleButtonTextWithClick(button1); 16 } 17 18 // 引数で指定されたボタンが押されるたびにテキストを変える 19 private async Task ToggleButtonTextWithClick(Button button) 20 { 21 var buttonTexts = new[] { "a", "b", "c" }; 22 23 for (int i = 0; ; i = (i + 1) % buttonTexts.Length) 24 { 25 button.Text = buttonTexts[i]; 26 await button.WaitEvent<EventArgs>(nameof(button.Click)); 27 } 28 } 29 } 30}

これを動かすためには、リンク先で記載されているコードが必要です。
プロジェクトに新しいクラスを追加して次のように書き換えてください。

csharp

1using System; 2using System.Collections.Concurrent; 3using System.Linq.Expressions; 4using System.Reflection; 5using System.Threading; 6using System.Threading.Tasks; 7 8namespace Zuishin.WaitEvent 9{ 10 static class WaitEventExtensions 11 { 12 private static readonly ConcurrentDictionary<Type, Delegate> handlerFactoryCache = new ConcurrentDictionary<Type, Delegate>(); 13 14 public static Task<(object Sender, TEventArgs EventArgs)> WaitEvent<TEventArgs>( 15 this object sender, 16 string eventName, 17 CancellationToken? cancellationToken = null) 18 { 19 if (sender is null) throw new ArgumentNullException(nameof(sender)); 20 if (eventName is null) throw new ArgumentNullException(nameof(eventName)); 21 22 var targetEvent = sender 23 .GetType() 24 .GetEvent(eventName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 25 ?? throw new ArgumentException($"{nameof(sender)} has no event named {eventName}."); 26 27 var completionSource = new TaskCompletionSource<(object, TEventArgs)>(); 28 cancellationToken?.Register(() => completionSource.SetCanceled()); 29 30 var handler = GetHandler(targetEvent.EventHandlerType, completionSource); 31 targetEvent.AddEventHandler(sender, handler); 32 33 return completionSource.Task.ContinueWith(task => 34 { 35 targetEvent.RemoveEventHandler(sender, handler); 36 return task.Result; 37 }); 38 } 39 40 private static Delegate GetHandler<TEventArgs>(Type eventHandlerType, TaskCompletionSource<(object, TEventArgs)> completionSource) 41 { 42 var factory = handlerFactoryCache.GetOrAdd(eventHandlerType, _ => 43 { 44 var taskCompletionSourceType = typeof(TaskCompletionSource<(object, TEventArgs)>); 45 var taskCompletionSourceExpression = Expression.Parameter(taskCompletionSourceType); 46 var setResult = taskCompletionSourceType.GetMethod(nameof(TaskCompletionSource<(object, TEventArgs)>.SetResult)); 47 var sExpression = Expression.Parameter(typeof(object)); 48 var eExpression = Expression.Parameter(typeof(TEventArgs)); 49 var tupleConstructor = typeof(ValueTuple<object, TEventArgs>).GetConstructor(new[] { typeof(object), typeof(TEventArgs) }); 50 var expression = Expression.Lambda<Func<TaskCompletionSource<(object, TEventArgs)>, Delegate>>( 51 Expression.Lambda( 52 eventHandlerType, 53 Expression.Call( 54 taskCompletionSourceExpression, 55 setResult, 56 Expression.New(tupleConstructor, sExpression, eExpression)), 57 sExpression, 58 eExpression), 59 taskCompletionSourceExpression); 60 return expression.Compile(); 61 }); 62 return ((Func<TaskCompletionSource<(object, TEventArgs)>, Delegate>)factory)(completionSource); 63 } 64 } 65}

投稿2022/06/02 03:55

Zuishin

総合スコア28660

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

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

Zuishin

2022/06/02 04:07

ここではラベルではなくボタンのテキストを変更していますが、応用は容易でしょう。
sgsgsg1983

2022/06/03 01:25

回答ありがとうございました。 自分はまだここまで複雑なコードを理解できません。 勉強してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問