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

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

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

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

3回答

1188閲覧

C# テキストの文字変更が一度しか行われない

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

1クリップ

投稿2019/03/12 05:04

現在、C#,visual studioで開発しています。
ボタンをクリックすると処理を開始するのですが、その処理に時間がかかるため、何か処理中とわかるものを考えました。
ボタンが押される → ボタンのテキストを「処理中」に変更 → 処理 → ボタンのテキストを「ボタン」に変更

これで処理を行っている間は、ボタンのテキストが「処理中」になると思ったのですが、
1回目のみ、テキストが変更され、それ以降はテキストが変わりません。
なぜ、このようなことが起こるのでしょうか?

C#

1private void csvButton_MouseClick(object sender, MouseEventArgs e) { 2 if(e.Button == MouseButtons.Left) { 3 switch (docComboBox.SelectedIndex) { 4 case 0: 5 outputButton.Text = "処理中"; 6 Aggr01_Output();   //処理を行うメソッド 7 outputButton.Text = "ボタン"; 8 break9 } 10 }

実際の処理の流れとしては上記の通りです。
「Aggr01_Output」ではSQLに接続し、データを抽出し結果をdataGridViewに表示する。といった処理を行っています。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2019/03/12 05:19

デバッガを使ってステップ実行し、1 回目と 2 回目のフローが同じになっているか、調べてください。
退会済みユーザー

退会済みユーザー

2019/03/12 05:29

デバッガでステップ実行してみたところ、値としては変わっていました。 しかし、実際の表示は変わっていませんでした。
退会済みユーザー

退会済みユーザー

2019/03/12 05:37 編集

そうすると、2 回目は Aggr01_Output が瞬時に完了してしまうので、実際は変わっているが、見かけ変わってないように見えるということぐらいしか思い当たることがないです。 その疑いがあるなら、Aggr01_Output は実際の処置ではなく Thread.Sleep(5000) とかにして試すとかしてみませんか。
退会済みユーザー

退会済みユーザー

2019/03/12 06:18

私もその可能性を疑いましたが、処理を行う度にUIが止まるので可能性としては低いのかなと思います。 私なりに調べたところ、重たい処理によってその前の描画処理も止まってしまう。というのが可能性として考えられるのかなと思います。 また、他の回答者様からその解決につながるヒントを頂けましたので参考程度にし、もう少し調べてみようと思います。 どうもありがとうございました。
guest

回答3

0

テキストを変更した直後、Application.DoEvents を呼んでください。

投稿2019/03/12 05:37

Zuishin

総合スコア28660

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

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

退会済みユーザー

退会済みユーザー

2019/03/12 05:57 編集

ありがとうございます。こちらの方法で解決したのですが、Application.DoEventsは注意して使用すべき。とありました。理由も一通り目を通したつもりですが、いまいちピンと来ていません。 質問が変わってしまうようで申し訳ないのですが、このApplication.DoEventsは多用しても問題ないのでしょうか?(ボタンクリックイベントが発生する度にこの処理を行おうと思っているため。)
Zuishin

2019/03/12 06:01

質問が変わるなら別に質問を立ててください。 その際には、注意すべきとどこにどんな意味で書いてあったかを明示してください。
guest

0

★の行を入れてください。

csharp

1 outputButton.Text = "処理中"; 2 outputButton.Update(); // ★これを入れてください 3 Aggr01_Output();   //処理を行うメソッド 4 outputButton.Text = "ボタン";

なぜ、このようなことが起こるのでしょうか?

ちゃんとした説明は詳しい人におまかせしますが、
テキストの更新依頼を掛けると、コントロール内部に文字列自体はすぐに適用されるのですが、
画面上への再描画はすぐには実行されず、
ボタンの表示が無効ですよ(再描画が必要ですよというフラグを立てて)という設定を行い、
WM_PAINTなどの再描画イベントがメッセージキューに追加されます。

このメッセージキューに溜まったメッセージはメッセージループが処理された段階で処理されますので、
何か関数内などで連続して処理を行っている最中には描画処理は行われません。

これをメッセージループを待たずにすぐに(無効な場所の)再描画を行わせるのが、outputButton.Update();です。

#ただWinFormsの場合は例外があり、マルチスレッド周りでイベント待ちなどを行っている間は
#再描画イベントだけは処理されるというような仕様が有った気がします。

投稿2019/03/12 08:01

takabosoft

総合スコア8356

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

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

Zuishin

2019/03/12 08:26 編集

Update メソッドは Win32API の UpdateWindow を呼んでいるだけです。 https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,12421 そして UpdateWindow は WM_PAINT メッセージをメッセージループをバイパスして送信するものです。 https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-updatewindow バイパスするので確かにすぐに表示は変わりますが、ボタンのテキストが変更された時すでに WM_PAINT が送信されているので冗長だと思います。また、ボタンの表示はできても他のウィンドウが未処理のままなので、やはり他の部分で似たような不具合が起こります。
Zuishin

2019/03/12 08:16

この場合なぜ画面上で変更が行われなかったのかの理由ですが、UI スレッドがビジーだったため、メッセージは届いていてもそれを処理するメッセージループが働かなかったからです。 Application.DoEvents は未処理のメッセージがなくなるまでメッセージを処理せよという命令ですから、ここで WM_PAINT が処理されて描画が行われたということです。
takabosoft

2019/03/12 08:43

> 冗長だと思います。 冗長だとしても無視できるレベルだと思います。 > また、ボタンの表示はできても他のウィンドウが未処理のままなので、やはり他の部分で似たような不具合が起こります。 他の部分での不具合って何でしょうか?
Zuishin

2019/03/12 08:53

ウィンドウの大きさを変えると表示が乱れたりなどです。
takabosoft

2019/03/12 08:57

そもそもメッセージループが回っていないところ(関数内で重たい処理をしている所)でウィンドウのリサイズは出来ないですよね? Update()で部分的に再描画を掛けた時の不具合とは言えないと思いますが。
Zuishin

2019/03/12 08:59

リサイズできないという不具合もあるかもしれませんね。
fana

2019/03/13 02:00 編集

「特定のボタンの表示だけをその瞬間に更新したい」のでしょうから, 少なくとも現状の質問内容に対する解決策としては Update()は意味が明確ですし,且つ,DoEventsで起こり得る副作用も無いし,良いと思う. (というかリサイズできない等を問題視するならDoEventsだろうが全く一緒なのでは)
Zuishin

2019/03/13 02:23

DoEvents は重い処理の中に挟めばすべてのメッセージを処理するので一緒ではないですね。 起こりうる副作用というのは何を心配されていますか?
fana

2019/03/13 02:42

> テキストを変更した直後、Application.DoEvents を呼んでください。 これだと,重い処理の中に挟んで「ない」ですよね. そういう話をするのであれば, Aggr01_Output(); ← これが重い処理でしょうから,この中で定期的にDoEventsを呼ぶくらいしないとダメなのでは? 副作用: 例えばボタンが2つあって,一方を押した際のイベントハンドラ内に書かれたDoEventsによって,他方のボタンを押した際のイベントハンドラ処理が走ったりすることになり得ますよね. (そういうことを目的としているのであれば「利点」と呼べるのでしょうけど,今回のケースでは,たかだか1つのボタンの表示を更新したいだけであって,わざわざ余計な動作の可能性を持ちこみたくないと思うから「副作用」と称した.)
Zuishin

2019/03/13 02:52

重い処理の中に挟むくらいのことは質問者もするでしょうし、そのような発言があります。 そして副作用ではなく、それこそが期待する動作です。 むしろボタンの表示以外はすべて副作用と思うことが乱暴なのでは?
Zuishin

2019/03/13 02:57 編集

この件で私が最も良い解決法と思うのは、非同期処理です。そして次に良いと思うのがすべてのメッセージを処理することです。非同期は他の部分もいじらなければならないので、荷が重いかもしれないと思い、遠慮しました。 質問がボタンの表示だからそこさえ良ければ良いというのは、やっつけに感じます。ボタンで問題が起こったように、他のコントロールで問題が次々に出てくることは十分予想できます。
fana

2019/03/13 02:59

既述ですが,その振る舞いこそが「質問者が」期待する動作なのであれば副作用と呼びません.
Zuishin

2019/03/13 03:02

質問者が前もって期待したものはわかりませんが、ウィンドウの大きさ変更もできなくなる方がいいという期待はしていないと思いますが。
fana

2019/03/13 03:16

この質問というのが,まさにその「やっつけ」方法に関するものだと私は思ったので,Update()が良いと思うと述べました.それだけです. 加えて質問者様がDoEventsの多用が安全かどうか(?)を危惧されておられる様子に見受けたので副作用云々の話も書いたのですが,どうやら外野が余計なことをしてしまったようですね.
Zuishin

2019/03/13 03:41

いえ、私も外野ですから。 似たような件でトラブルがあったので、ついつい余計なことを書いてしまいました。
guest

0

以下 2 点を確認してみてください。

  • 他のイベントでoutputButton.Textの値が変更されていないか?
  • 2 回目以降もoutputButton.Text = "処理中";を処理を通っているのか?また値は変わっているのか?(ブレークポイントを貼って確認する)

投稿2019/03/12 05:13

nskydiving

総合スコア6500

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

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

退会済みユーザー

退会済みユーザー

2019/03/12 05:31

ありがとうございます。2点とも確認しましたが、問題ありませんでした。 デバッガで確認すると値は変わっているのですが、実際動かしてみるとうまく文字が切り替わっていませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問