- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
953 :デフォルトの名無しさん[sage]:2017/04/21(金) 17:42:09.62 ID:MpBIOwvX - そういえば前から疑問なんだけど、フォームのイベント伝搬ってどう組むべきなんだ?
状況としてはよくある波形ビューワーで、表示先頭位置と倍率が変えられて、 カーソルが2つあって、フィットボタンを押すと画面にフィットするもの。 (カーソル0が画面の左端、カーソル1が画面の右端になるように、 自動的に表示先頭位置と倍率が調整され、再描画される) (A) 表示先頭位置の変更→画面再描画 (B) 倍率の変更→画面再描画 とした場合、 フィットボタンを押す→ 表示先頭位置と倍率が自動的に変更される=普通は上記(A)または(B)に当たる →自動的に再描画される となるのだが、ほとんどのケースで2回描画されてしまう。 そこで今は、倍率変更側はフォーカスを確認して (B') 倍率の変更→(フォーカスがある時のみ)→画面再描画 として1回描画にしているが、 これだとフィットボタンを押したとき、結果的に倍率のみの変更の場合は再描画されない (同じ値を上書きしてもChangedが発火しない)ので、フィットボタンのイベントの最後に if (!changed_start_pos) redraw(); を付けている。 ただしこれはリファクタ時に久々に見たら「はて?」と思ってしまった。 この場合って、どう組むべきなんだ? ちなみにJavaScriptの場合は、プログラムによる変更の場合はイベントが発火しないので、 全てのイベントの最後に redraw(); が必要になるが、全部書けば全く問題ない。 フォームの場合はプログラムによる変更でもイベントが発火するので、この問題が起きる。 ただ、特にレアケースでもないし、一般的なうまいやり方があるとは思うのだけど。 上手く繋げられれば記述が少なくて済むからこの仕様なんだろうし。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
954 :デフォルトの名無しさん[sage]:2017/04/21(金) 17:42:43.14 ID:MpBIOwvX - なお、今のコードのイメージは以下。(CLIだけど)
void numericUpDown_fitButton_Clicked(Object^ sender, EventArgs^ e) { // フィットボタンクリック // 倍率と表示先頭位置の再計算 numericUpDown_magnitude->Value = XXXX; numericUpDown_startPos->Value = YYYY; if (!changed_start_pos) redraw(); } void numericUpDown_magnitude_ValueChanged(Object^ sender, EventArgs^ e){ // 倍率変更 // スクロールバー等の増減量等、他機能の整備もここでやっている if (((NumericUpDown^)sender)->Focused) redraw(); } void numericUpDown_startPos_ValueChanged(Object^ sender, EventArgs^ e){ // 表示先頭位置変更 redraw(); } void redraw(); // 再描画 ぱっと思いつくのは全部 Focused を確認して redraw() だが、 それだとこの仕様(=フォームのイベントはプログラムによっても発火する)にした意味無いよね? (その場合は明らかにJavaScriptの仕様の方がマシって事になってしまう) 多分何らかの上手いやり方があると思うのだが。 色々奇妙なのは後付でごちゃごちゃやっているから勘弁で。 今までは問題なく動作していたから放置していたが、ついでなのでリファクタしようとしている。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
960 :デフォルトの名無しさん[sage]:2017/04/21(金) 18:55:13.11 ID:MpBIOwvX - >>955
別クラスのプロパティに分離しても本質的には同じだよね? 複数のプロパティが、 ・どれかは変更される ・全てが変更されるとは限らない ・必ず変更される物があるわけではない 状況においては、内部的にOR取ることが必要で、一番単純なのはキューで上書きしてしまう方法。 つまりそちらのようにタイマーに要求を出して、 timer->start()は何度打っても同じだからそこでOR取ってしまうとか。 しかしこれだと余分にこの構造が必要なんだよね。 (ただし他の部分ではこの方法も使っている) UIから変更された場合、60fpsとかにするくらいなら直接イベントで描画してもほぼ同じでしょ。 波形はwaveファイルで数百メガとかの場合もあり、このときは明らかにもたつくので2度描画はNG。 あるいは、独立クラスにフラグを持ってそこで上書き、 独立クラスのeventをサブスクライブしろ、ということ? それは理想的な構造なのだろうけど、話が膨らみすぎて面倒だ。 Application.Idleは初耳だが素晴らしい。(JavaScriptではアイドルが取れない) これってUIスレッドが、ってことで良いのか? (ただしこれは今回は使えそうにはないが) >>956 WM_PAINT見たがよく分からん。 システム側が再描画タイミング(おそらく60fps)を通知してくれるので、 それをサブスクライブして、そこで溜まっている再描画を掃くのか? それはゲームみたいに常に再描画する用で、 今回みたいにUIで変更された時のみの場合は常にイベントが呼ばれる分ウザくなる気が。 あるいは、自分で何かを再描画した時だけ、 システム側で60fps同期でWM_PAINTを打ってくるのなら、今回には使えない。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
961 :デフォルトの名無しさん[sage]:2017/04/21(金) 18:57:50.94 ID:MpBIOwvX - >>959
サンクス。まあみんな意見は同じか。 他に何か、「プログラムによる変更であってもイベントが発火する」という、 フォームの仕様を上手く使った方法はないかねえ?
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
964 :デフォルトの名無しさん[sage]:2017/04/21(金) 19:50:01.08 ID:MpBIOwvX - >>962
BeginUpdate/EndUpdateはいいとして、 SuspendLayout, ResumeLayoutは反応しなくないか? (というか>>958は俺宛ではなくEFの件なのか?と思っていた) 俺の理解ではSuspendLayoutはレイアウト時、つまり、Control.AddRangeを止めるもので、 Graphics.DrawLinesとかを止めるものではないと思っているのだが。 >>963 了解。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
968 :デフォルトの名無しさん[sage]:2017/04/21(金) 20:28:06.75 ID:MpBIOwvX - >>965
ちょっと話が噛み合って無い感があるから整理すると、 JavaScript: プログラムからの変更ではイベントが発火しないから、 全てのイベントハンドラは redraw(); で締めないといけないが、 イベントハンドラ内で他コントロールをどれだけ触ろうと何も考える必要なし。 .NET: プログラムからの変更でもイベントが発火するから、 関連しているコントロールのイベント先をすべて redraw() にしておけば再描画される。 だから単純な再描画についてはこっちの方が記述はすっきりする。 ただし、今回のように複数コントロールを触るイベントハンドラがあった場合、 その回数だけ再描画される可能性がでてくるからそこを対策しようとすると、途端に汚くなる。 (JavaScriptのコードは redraw(); を書くしかないし、再描画だとはっきり分かるが、 .NET のコードは俺が今やっている妙な対策法だと???なコードになる) 再描画されればいいのなら、.NETの方が良いけど、 2度描画禁止とかにしたい場合、.NETの方が記述が余計に必要になる。←これって俺の勘違いか? というのが今回の疑問。 > そういう設計だと、例えばプログラムで倍率を変更しても再描画されないよね。 違う。プログラムから各コントロールのValueを変更することによって、自動的に再描画させてる。 というか、波形表示画面内容と表示開始位置と倍率は当然同期してないといけなくて、 逆に、表示開始位置と倍率が変わらないのなら再描画の必要がない。 それはコントロールの値を変更する構造によって自動的に達成される。 (.NETは同じValueを上書きしてもイベントが発火しない為) だからFitボタン連打とかの場合の無駄な再描画はここで止められる。 多分.NETの仕様だとこういう事だと思うんだよ。(これがMVC的に云々というのはまた別の話) それで、2度描画禁止の場合はどう実装するべきだという想定なのだろう?という疑問なんだ。 普通はキューイングするから問題ない、ってことなのかな? (なお、明示的に再描画したい場合は redraw() を呼ぶだけだから問題ない)
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
969 :デフォルトの名無しさん[sage]:2017/04/21(金) 20:42:28.79 ID:MpBIOwvX - ちなみにMVCの場合はモデルがイベントソースで、
コントロールの値を変更→モデルの値を変更→再描画 という流れになるけど、モデルの値をプログラムから変更する場合、 コントロールの値の表示を手動で合わせてやる必要がでてくる。 これが面倒だから、WPFではバインディングってことで自動化してる。 これはこれで良いとして、 .NET作った時に今回のようなケースが想定されていないはずもなく、 彼等の想定実装があるはずで、 その通り実装すれば綺麗に実装出来るはずなんだが、というのが俺の疑問。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
972 :デフォルトの名無しさん[sage]:2017/04/21(金) 21:07:01.51 ID:MpBIOwvX - >>970
> コントロールのプロパティを値の入れ物として利用するのは普通はよくない作法だと思う。 MVC的にはそうだね。ただ、この部分のUIなんて変更はないからどっちでもいいのも事実。 ところでその場合、バインディングはどう実現する? (C) numericUpDownのValueChanged→モデルの値を変更 これはいい。ただFormの場合、 (D) モデルの値が変更された→イベント発火でnumericUpDownの表示値を変更 とすると、当然(D)の直後に(C)が発火して、 モデルの値を再度「同じ値」で上書きして、そこでイベントが止まる。 これって全くの無駄でしょ。 .NETの仕様を決めた時、これらが想定されていないはずもなく、 彼等なりの上手い使い方があったと思うんだよね。 (今現在それが良いとされる手法かどうかはともかく) 今のところ、表示とモデルの内容を同期するのに一番簡単な方法は、 「numericUpDownのValueをモデルの値として扱うこと」なんだよ。 そしてこれだと他クラスから見えないので、コピーを持ってる。 これは後付でこうなった、というのもある。 実装は、イベントハンドラに何個でも関連づけさせられるからそこでさせてる。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
973 :デフォルトの名無しさん[sage]:2017/04/21(金) 21:13:36.92 ID:MpBIOwvX - >>971
モデルをどこに置くか、という話なんだよ。 Formの仕様だと、numericUpDownのValueプロパティを「モデルの値」として扱えば すべてすっきり行く仕様になってる。だからそうしてる。 ところが2度描画禁止だとすっきり行かない。だからこれが疑問。 それならJavaScriptみたいに、最初から 「必ず1回redraw()を書かないとダメだけど、1回書けばいいだけです」の方が良かった。 だから、彼等なりの想定実装があったはずで、それを考えてる。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
977 :デフォルトの名無しさん[sage]:2017/04/21(金) 21:33:34.84 ID:MpBIOwvX - >>974
バインディングといったから分かりにくいが、放置した場合は表示が間違ってるんだよ。 これは完全にアウト。 Fitボタンが押された→ モデルの値が変更された→ 再描画された これで「波形表示」は最新になるけど、 「表示開始位置」と「倍率」の表示されている値が古いままでしょ。 そしてFormのイベントはそれ用になってないんだよ。 >>976 > イベントがダブりそうなときはイベントを-して値代入後+しなおしているなあ これってかなり面倒でしょ。 今のところタイマで遅らせるのが一番すっきりするからそうしようかと思っている。 (これは他部分で既に実装済みなのを流用出来るというのが大きいが) redraw()を呼んだら16ms後にredraw_implement()が呼ばれて実際に再描画とか。 ただこんなの.NET作った頃から想定してたのかな?という疑問はある。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
984 :デフォルトの名無しさん[sage]:2017/04/21(金) 22:29:56.50 ID:MpBIOwvX - >>983
え?WFPって描画はUIスレッドじゃなくていいのか? それはすごくいい。 それだとスピンコントロールのボタン連打で描画が追いつかない時にも、 イベントが溜まることなく最新が常に表示されるね。 何もしなくても。 まあ何だかんだで新しい物は改良されてるってことだね。
|
- C#, C♯, C#相談室 Part92 [無断転載禁止]©2ch.net
989 :デフォルトの名無しさん[sage]:2017/04/21(金) 23:07:08.58 ID:MpBIOwvX - >>987-988
うーむ、やはりイマイチか。 回答くれた皆さんありがとう。 俺は>>970ではないけど、次スレ俺が立ててもいいけど。(>>1)
|