- ベストアンサー
C#でフォームをなめらかに移動する方法は?
- C#でフォームを移動させる際にアニメーションを付ける方法について説明します。
- 移動するスピードの変化を防ぐためには、Timerを使用して一定の間隔でフォームを移動させることが重要です。
- .NET Framework 4.0およびVisual Studio 2010を使用している場合、以下のコードを参考にしてください。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
新しいコードではTimerは使っていないのですね? 私の環境には2005 Proしか入っていないのでTaskについては実行環境がありません。 代わりに補足に提示されたコードを使って2005 Proで動くコードを書いてみました。 ※2010で動くかどうかはわかりません。 以下Form1というFormにbutton1というButtonを貼り付けたコードです。 using System; using System.Drawing; using System.Windows.Forms; using System.Threading; namespace WindowsApplication1 { public partial class Form1 : Form { Thread subThread; ThreadStart subThreadStart; EventWaitHandle subButtonEvent; EventWaitHandle subExitEvent; public Form1() { InitializeComponent(); subThreadStart = new ThreadStart(threadRun); subThread = new Thread(subThreadStart); subButtonEvent = new EventWaitHandle(false, EventResetMode.AutoReset); subExitEvent = new EventWaitHandle(false, EventResetMode.AutoReset); subThread.Start(); } protected override void OnClosed(EventArgs e) { subExitEvent.Set(); // スレッド終了通知 } private void button1_Click(object sender, EventArgs e) { subButtonEvent.Set(); // button1が押されたときの処理 } void threadRun() { while (true) { int index = EventWaitHandle.WaitAny(new WaitHandle[] { subButtonEvent, subExitEvent }); if (index == 0) { // 貴方のソースコード Point BeforeLocation = this.DesktopLocation; int StartTime = System.Environment.TickCount; int Time; int EndTime; int dx; for (int i = 0; i <= this.Width - 3; i += dx) { EndTime = System.Environment.TickCount; Time = EndTime - StartTime; StartTime = System.Environment.TickCount; dx = (int)(Time / 2 + 1); Invoke((MethodInvoker)delegate() { this.DesktopLocation = new Point(this.DesktopLocation.X - dx, this.DesktopLocation.Y); this.Refresh(); Application.DoEvents(); }); } Invoke((MethodInvoker)delegate() { this.DesktopLocation = new Point(BeforeLocation.X - this.Width + 3, this.DesktopLocation.Y); }); } else if (index == 1) { // スレッド終了 break; } } } } }
その他の回答 (2)
- tsukasa-12r
- ベストアンサー率65% (358/549)
>Intervalを20程にしたTimerを使う方法でも試してみましたが、あまり変わりませんでした。 もしかして、 for (int i = 0; i <= this.Width; i += 15) { this.DesktopLocation = new Point(this.DesktopLocation.X - 15, this.DesktopLocation.Y); this.Refresh(); } を、Timer の tick イベントにそのまま記述しただけ、ってことでしょうか? Time を使用すると、PC の性能にはほとんど依存しないはずだと思います。 using System; using System.Drawing; using System.Windows.Forms; namespace MoveForm { public partial class Form1 : Form { private int _destination; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { _destination = this.Location.X - this.Width; this.timer1.Interval = 20; this.timer1.Start(); } private void timer1_Tick(object sender, EventArgs e) { if (_destination < this.DesktopLocation.X) { this.DesktopLocation = new Point(this.DesktopLocation.X - 15, this.DesktopLocation.Y); this.Refresh(); } else { this.timer1.Stop(); } } } }
お礼
ありがとうございます。 一回の移動で使う時間が20msecを超えているのかと思い、TimerのIntervalを長くしてみましたら、ダメでした。 なぜでしょう…
- sha-girl
- ベストアンサー率52% (430/816)
>これをどのPCでもスピードの変化がなくアニメーションできるようにするにはどうすればよいでしょうか? どのPCでもというのは不可能です。 CPUだけに注目してもATOMとCorei7では処理速度が異なりますし、例え同じPC環境であっても バックグラウンドで動いているアプリケーション(例えばウイルス対策ソフト)もあり Windowsの場合一つのプロセスがCPUを占有することは出来ないからです。 >Intervalを20程にしたTimerを使う方法でも試してみましたが、あまり変わりませんでした。 あまり変わらなくみえるのは、少なくとも遅いほうのPCで20msecを大きく超える時間がかかっているからだと思います。(処理がまったく間に合っていない) 例えばIntervalを1000msecに設定すれば、どちらのPCの動作も見た目はほとんど変わらなくなるでしょう。 ゲーム等でよく使う手法としては、時間差(あるいは前回のフレームからの時間差)でスクロール量を決定します。 (固定フレームレートで動かす事が保障出来ないからです。) System.Environment.TickCountで起動してからの経過時間を取得できます。 http://msdn.microsoft.com/ja-jp/library/system.environment.tickcount(v=VS.100).aspx int startTime; int startX; private void Form1_Load(object sender, EventArgs e) { startTime = System.Environment.TickCount; startX = this.DesktopLocation.X; } private void timer1_Tick(object sender, EventArgs e) { int elapsedTime = startTime - System.Environment.TickCount; int xScroll = elapsedTime / 100; // 1秒あたり10ドットスクロール this.DesktopLocation = new Point(startX + xScroll, this.DesktopLocation.Y); }
お礼
ゲーム等でよく使う手法で解決しました。ありがとうございました。ですが、一回目は正しくアニメーションされるんですが、2回目以降がアニメーションしてくれません。 遅く移動させたところ、for文が終わってから一気に描画されるようです。 どうにかなりませんか? 一応改善したコード載せて起きます。 Point BeforeLocation = this.DesktopLocation; int StartTime = System.Environment.TickCount; int Time; int EndTime; int dx; for (int i = 0; i <= this.Width - 3; i += 1) { EndTime = System.Environment.TickCount; Time = EndTime - StartTime; StartTime = System.Environment.TickCount; dx = (int)(Time / 2 + 1); Invoke((MethodInvoker)delegate(){ this.DesktopLocation = new Point(this.DesktopLocation.X - 1, this.DesktopLocation.Y); this.Refresh(); Application.DoEvents(); }); System.Threading.Thread.Sleep(10); } Invoke((MethodInvoker)delegate() { this.DesktopLocation = new Point(BeforeLocation.X - this.Width + 3, this.DesktopLocation.Y); }); InvokeがあるのはTaskを使って別スレッドで実行しているためです。
補足
ごめんなさい。お礼で書いたコードが間違ってたので、補足に書いてもいいですか。 Point BeforeLocation = this.DesktopLocation; int StartTime = System.Environment.TickCount; int Time; int EndTime; int dx; for (int i = 0; i <= this.Width - 3; i += dx) { EndTime = System.Environment.TickCount; Time = EndTime - StartTime; StartTime = System.Environment.TickCount; dx = (int)(Time / 2 + 1); Invoke((MethodInvoker)delegate(){ this.DesktopLocation = new Point(this.DesktopLocation.X - dx, this.DesktopLocation.Y); this.Refresh(); Application.DoEvents(); }); } Invoke((MethodInvoker)delegate() { this.DesktopLocation = new Point(BeforeLocation.X - this.Width + 3, this.DesktopLocation.Y); }); これが正しいコードです。
お礼
解決しました! 丁寧なご回答本当にありがとうございました。