情報科学屋さんを目指す人のメモ(FC2ブログ版)

何かのやり方や、問題の解決方法をどんどんメモするブログ。そんな大学院生の活動「キャッシュ」に誰かがヒットしてくれることを祈って。

ブログ内検索

スポンサーサイト このエントリーを含むはてなブックマーク

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

スポンサー広告 | 編集
このエントリーをはてなブックマークに追加 Clip to Evernote

(C#+.NET)描画内容をドラッグで移動して、ホイールで拡大縮小 このエントリーを含むはてなブックマーク

C#.NETの組み合わせで、ドラッグで移動(手のひらツールみたいな)して、描画内容をホイール回転により拡大縮小してみます。

別になんてことはないのですが。ちなみに、画面の中心を拡大縮小の中心にします

こういう基本的というか、よくあるものこそ一番需要があるのかなーと。(いや、こんなことしかできないだけですが…)

さて、以下、解説です。 まず、描画するクラス(Formとか、PictureBoxとか、独自クラスとか)に、表示倍率を表す変数・プロパティーを作ります。ここでは、描画する位置を変更することも考えて描画に使う原点の座標(originX, originY)も定義しておきます。

private double Bairitsu = 1.0; // 表示倍率
private double OriginX = 0.0;
private double OriginY = 0.0;


ここで注意。倍率は、標準が1です。パーセントではありません

次、ホイール回転にあわせて倍率を変えましょう。Form1が描画するクラスだとしたら、

void Form1_MouseWheel(object sender, MouseEventArgs e)
{
double prevBairitsu = this.Bairitsu; // 変更前の倍率を保存しておきます

// ▼ここから倍率変更
if (e.Delta > 0) // e.Delta > 0 は、ホイールを「向こう側」へ回転させたときtrue
{
// 拡大
if(this.Bairitsu > 1)
{
this.Bairitsu += 1; // 拡大中の時にさらに拡大
}
else
{
this.Bairitsu += 0.05 // 縮小中(0<Bairitsu<1)の時に拡大
}
}
else
{
// 縮小
if(this.Bairitsu > 1)
{
this.Bairitsu -= 1; // 拡大中の時に縮小
}
else
{
this.bairitsu = Math.Max(0.05, // 倍率ゼロはまずいので
this.Bairitsu - 0.05) // 縮小中(0<Bairitsu<1)にさらに縮小
}
}
// ▲ここまで倍率変更

// ▼ここから画面中央を中心に拡大・縮小させる工夫
// 工夫しないと、拡大縮小時にどんどん描画しているものが移動してしまいます
// つまり、中心を固定しています

double kakudaiRitsu = prevBairitsu / this.bairitsu; // 今回拡大(縮小)した量(率)
int W = this.Width; // 画面の幅
int H = this.Height; // 画面の高さ
originX = (originX - W/2) * kakudaiRitsu + W/2;
originY = (originY - H/2) * kakudaiRitsu + H/2;

// ▲ここまで画面中央を中心に拡大・縮小させる工夫
}



これによって、画面の中心に向かって拡大縮小するようになりました。
注意:拡大、縮小に使っている+1とか+0.05とかは適当です(表示するものによって工夫するべき)。自分でいろいろ試してみてください。


次に、ドラッグで移動させるところです。
①マウスボタンを押したとき
②ドラッグ中
③マウスボタンを放したとき

それぞれが

①void Form1_MouseDown(...){...}
②void Form1_MouseMove(...){...}
③void Form1_MouseUp(...){...}

によって処理されます。

1回のドラッグでこれら①②③が順に使われます。
ここで注意。確かに、ドラッグ中には、①が最初に呼ばれて最後に③が呼ばれるのですが、その間のドラッグ中、②が繰り返し何度も呼ばれ続けることに注意してください。

まず共通の変数を用意します。

private bool IsDragging; // 現在ドラッグ中かどうか
// どのボタンが押されているのか(右ボタンで別の処理をしたい時に、区別するため)
private MouseButtons DraggingButton;
// ②呼び出しの直前(前回の②の終了時)のマウスポインタのX座標
private double DraggingOldX;
// ②呼び出しの直前(同上)のマウスポインタのY座標
private double DraggingOldY;


では、以下、それぞれの処理内容です

// ①マウスボタンを押したとき
void Form1_MouseDown(object sender, MouseEventArgs e)
{
this.DraggingButton = e.Button;
this.DraggingOldX = e.X;
this.DraggingOldY = e.Y;
switch (this.DraggingButton)
{
case MouseButtons.Left:
this.IsDragging = true; // ドラッグ中であることを知らせる
// 通常の矢印の代わりをマウスポインタとして表示
this.Cursor = Cursors.SizeAll;
break;
}
}

// ②ドラッグ中
void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (this.IsDragging && this.DraggingButton == MouseButtons.Left)
{
// 移動した分、描画位置を変更
this.OriginX += e.X - this.DraggingOldX;
this.OriginY += e.Y - this.DraggingOldY;

// 変更直後のマウスポインタの位置を保存次の②の呼び出しで使う
// (っていうか、すぐ上の行で使います)
this.DraggingOldX = e.X;
this.DraggingOldY = e.Y;
}
}


// ③マウスボタンを放したとき
void Form1_MouseUp(object sender, MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left: // 左クリックの時
this.IsDragging = false; // ドラッグが終了していることを記録
this.Cursor = Cursors.Default; // マウスポインタを通常のものに戻す
break;
}
this.DraggingButton = 0;
}




はい、これでマウスによる移動と、拡大縮小の動作についてのイベント記述が終了しました。

最後に、肝心の描画イベントです。
TranslateTransformメソッド(平行移動)
ScaleTransformメソッド(拡大縮小)
がポイントで、そのほかはそのままでいいです。

では、その描画部分であるOnPaintメソッドです。


protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);

Graphics g = pe.Graphics;

// 原点を移動
g.TranslateTransform((float)this.OriginX, (float)this.OriginY);

// 拡大縮小
g.ScalseTransform((float)this.Bairitsu, (float)this.Bairitsu);

// たとえば、円を表示してみる
Brush brush = new SolidBrush(Color.Blue);
g.FillEllipse(brush, 10.0f, 10.0f, 2.0f, 2.0f);
g.FillEllipse(brush, 30.0f, 50.0f, 3.0f, 3.0f);
}



はい、これで完成です。


動きますかねぇ?

実は、私がプログラムの中で使用したコードを変数名を変えたりしながら写しただけなので、実行してません…この記事はあくまでもヒント!ということで許してください。

何かありましたら、コメントをお願いいたします。疑問等に答えられるかもしれません…

.NET | コメント:6 | トラックバック:0 | 編集
このエントリーをはてなブックマークに追加 Clip to Evernote

この記事のコメント

いろいろなところを検索して、ここにたどり着きました。
とても参考になりましたが、管理人様がVBもご理解しているということで、質問させていただきます。VB2008で同じことを実装しようと思い、解読・移植をしていたのですがどうもうまくいきません。
OnPaintイベントですが、Paintイベントと異なるものなのでしょうか。
2009-06-03 Wed 17:27 |  はとりん
はとりんさん、コメントありがとうございます。

VB2008ならば.NETを利用しているので、C#と同様にOnPaintメソッドが利用可能のはずです。

本題のうまくいかないとのことですが、すみません、コードおよび解説が不十分でした。

実は、OnPaintメソッドを用意すると、たしかに再描画時にOnPaintメソッドが呼び出されるのですが、この"再描画"は、どこかで呼び出してあげないといけません。ホイールの回転を行うときだけではなく、何か描画されているものが変化するたびに呼び出さなければなりません。私の作成したコードでは、タイマーで、定期的に呼び出しています(操作しなくても、描画内容が変化するプログラムだったので)。

今回の場合は、操作するたびに更新されるようにします。まず、最初の描画のために、起動時にOnPaintメソッドを呼んでください。ここで、OnPaintメソッドを呼ぶ際には、直接ではなく、this.Refresh()を呼んでください。Refreshメソッド内部でOnPaintメソッドが呼び出されます。
続いて、Form1_MouseMoveの内部にもthis.Refresh();を追加してください。これで、マウスを画面上で移動させる(たとえばドラッグ)するたびに描画内容が更新されますし、Form1_MouseWheelの末尾にthis.Refresh()を追加すれば、ホイールを回転させるたびに描画内容が更新されます。このように、描画し直して欲しいところにthis.Refresh();を追加してください。

掲載のコードは、手元にあるコードを直接コピーしたものではないので、また別の問題があるかもしれません(もともと、Formではなく、描画しているのが自作コントロールなので・・・)。また質問があればコメントをよろしくお願いいたします。

参考:MSDN Refreshメソッド(http://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.refresh.aspx)
2009-06-03 Wed 21:47 |  でぃどでぃど
わたしも勉強になりました。
疑問点があります。
オブジェクトをドラッグするときと、クリックするときで、処理をわけたいのですが、現在は(それぞれ別に実装しているつもりなのに)ドラッグが終了すると、クリックしたイベントが発生してクリックの処理を行ってしまいます。
ドラッグ(MouseDown-Move-UP)と、クリックを区別するには、どのようにしたらよいでしょう?
this.IsDragging = true;
で判定できるかと思ったらできませんでした。
アドバイスお願いします。
2009-09-01 Tue 09:41 |  PassingBy
> わたしも勉強になりました。
> 疑問点があります。
> オブジェクトをドラッグするときと、クリックするときで、処理をわけたいのですが、現在は(それぞれ別に実装しているつもりなのに)ドラッグが終了すると、クリックしたイベントが発生してクリックの処理を行ってしまいます。
> ドラッグ(MouseDown-Move-UP)と、クリックを区別するには、どのようにしたらよいでしょう?
> this.IsDragging = true;
> で判定できるかと思ったらできませんでした。
> アドバイスお願いします。

ちょうど投稿があったときに忙しく、やろうやろうと思っているうちに時間がたってしまいました。。。申し訳ありません。これからは、コメントに対しては基本1日以内に返事だけでもしようと思います。

ドラッグとクリックについてですが、クリックって、移動しないドラッグな気がしませんか?

どういう方法が正しいのか調べていないのですが(あ、調べるべきですよね…)、クリックもまずはドラッグとして考え、移動距離が"極端に"小さいドラッグを「クリック」とし、移動距離が大きいドラッグを本来の「ドラッグ」としてみてはいかがでしょうか。

また、Mouse-DownとMouse-Upの感覚が短いものをクリックとしてみるというのも可能だと思います。Mouse-Down時に時刻を記録し、Mouse-Upまでにかかった時間を計測するという方法です。

以上、2つがまず思いつく方法です。用途を考慮しながら試してみてください。
本当に返答が遅くなってしまい申し訳ありませんでした。
2009-11-14 Sat 00:03 |  でぃどでぃど
フォームのプロパティにマウスホイールイベントがないところにつまずきましたが…。
というか、まさか、あれ、なかったっけ?まだ、ないの?みたいな驚きです。
2010-06-03 Thu 15:36 |  ゆっか [ 編集]
> ゆっかさん
コメントありがとうございます。
意外に移動&拡大縮小のコードが見当たらなかったことを思い出しました。
役に立てたようでよかったです^^
2010-06-03 Thu 21:02 |  did2

コメントの投稿 エントリの新旧に関わらず、極力18時間中に返信します。














この記事のトラックバック

トラックバックURL:
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。