ふたつの写真を合成してそのあとに位置関係を変更したい。しかしそのときには画像は一体化しているので2枚の写真の位置関係を変更することはできません。しかしレイヤーにしてしまえば双方は独立しているのでいつでも位置を変更することができます。
重なった2つの長方形が表示されていますが、後ろ側にある長方形を移動させることもできます。
レイヤーを実装するためにはどうすればよいのでしょうか? ここではレイヤークラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class Layer { public Bitmap Bitmap { get; set; } public string Name { get; set; } public int X { get; set; } = 0; public int Y { get; set; } = 0; } |
次にデザイナで以下のようなものをつくります。フォームの中央にあるのはピクチャーボックスです。Anchorプロパティを設定しているのでフォームの大きさが変更されるとピクチャーボックスの大きさも変更されます。
レイヤーメニューの下側に作成されたレイヤーの名前が並びます。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); // フォームの大きさが変更されるとピクチャーボックスの大きさも変更される pictureBox1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; } } |
これはメニューの[新しいレイヤーをつくる]を選択すると新しいレイヤーが生成されます。そしてメニューの[レイヤー]の下に作成されたレイヤーの名前が表示されるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { int LayerNumber = 0; List<Layer> Layers = new List<Layer>(); Layer SelectedLayer = null; private void CreateNewLayerMenuItem_Click(object sender, EventArgs e) { // Layersオブジェクトを生成してリストに追加する Layer layer = new Layer(); LayerNumber++; layer.Name = "レイヤー " + LayerNumber.ToString(); Layers.Insert(0, layer); // 生成されたLayerが選択されている状態にする SelectedLayer = layer; } } |
実際にメニューが生成されるのはメニューの[レイヤー]がドロップダウンされたときです。[レイヤー]メニューにはセパレーターをふたつ作成し、レイヤーの名前はこのあいだに表示されるようにします。そのためには古い項目を削除して現在存在するレイヤーの名前を新しい項目として追加するという方法をとります。
メニューのなかで作成されたレイヤーが選択されたらそれを検知できるようにする必要があります。そこでメニューの項目を作成するときにToolStripMenuItem.Tagプロパティに値をセットして、この項目はどのレイヤーと関連付けられているかがわかるようにしています。
ItemTagクラスを作成してこのインスタンスをToolStripMenuItem.Tagプロパティにセットすることでメニューが選択されたらどのレイヤーが選択されたかわかるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); LayerMenuItem.DropDownOpening += LayerMenuItem_DropDownOpening; } private void LayerMenuItem_DropDownOpening(object sender, EventArgs e) { // ふたつのセパレーターのあいだの項目をいったん削除する int start = LayerMenuItem.DropDownItems.IndexOfKey("LayerListSeparatorItem"); int end = LayerMenuItem.DropDownItems.IndexOfKey("LayerListSeparatorItem2"); if(start == -1 || end == -1) return; int count = end - start - 1; for(int i=0; i< count; i++) LayerMenuItem.DropDownItems.RemoveAt(start +1); // 現在存在するレイヤーの名前を新しい項目として追加する int index = 0; foreach(Layer layer in Layers) { // メニューがクリックされたらEventHandlerWhenSelectLayerが呼ばれる ToolStripMenuItem item = new ToolStripMenuItem(layer.Name, null, EventHandlerWhenSelectLayer); index++; LayerMenuItem.DropDownItems.Insert(start + index, item); // メニューが選択されたらどのレイヤーが選択されたかわかるようにする ItemTag itemTag = new ItemTag(); itemTag.Layer = layer; item.Tag = itemTag; // 現在選択状態にあるのであればチェックマークをつける if(SelectedLayer == layer) item.Checked = true; } } } |
これがItemTagクラスです。
1 2 3 4 5 6 7 8 |
public class ItemTag { public Layer Layer { get; set; } } |
レイヤーの名称に対応するメニューが選択されたら選択されてたレイヤーがどれか調べます。みつかったら選択されているレイヤーに対応するメニュー項目にチェックマークをつけます。そして選択されているレイヤーをフィールド変数に保存しておきます。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { void EventHandlerWhenSelectLayer(object sender, EventArgs e) { ToolStripMenuItem item = (ToolStripMenuItem)sender; ItemTag itemTag = (ItemTag)item.Tag; SelectedLayer = itemTag.Layer; // これが選択されているレイヤーである } } |
メニューの[選択されているレイヤーに画像を読み込む]が選択されたら選択されているレイヤーに画像を読み込みます。そしてPictureBox.Invalidate()メソッドをよびます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public partial class Form1 : Form { private void LoadToSelectedLayerMenuItem_Click(object sender, EventArgs e) { if(SelectedLayer == null) { MessageBox.Show("レイヤーが選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } OpenFileDialog dialog = new OpenFileDialog(); if(dialog.ShowDialog() == DialogResult.OK) { string filePath = dialog.FileName; try { Image img = Image.FromFile(filePath); Bitmap bmp = new Bitmap(img); img.Dispose(); SelectedLayer.Bitmap = bmp; pictureBox1.Invalidate(); } catch(Exception ex) { MessageBox.Show("選択されたファイルは画像ファイルではありません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } finally { dialog.Dispose(); } } } } |
PictureBox.Invalidate()メソッドが呼ばれたときにおこなわれる処理です。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form1 : Form { private void pictureBox1_Paint(object sender, PaintEventArgs e) { foreach(Layer layer in Layers) { if(layer.Bitmap != null) e.Graphics.DrawImage(layer.Bitmap, new Point(layer.X, layer.Y)); } } } |
レイヤーを動かすことができないのであればレイヤーをつくった意味がありません。方向キーで移動させることができるようにしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public partial class Form1 : Form { private void Form1_KeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Up) MoveLayer(0, -1); if(e.KeyCode == Keys.Down) MoveLayer(0, 1); if(e.KeyCode == Keys.Left) MoveLayer(-1, 0); if(e.KeyCode == Keys.Right) MoveLayer(1, 0); } void MoveLayer(int x, int y) { if(SelectedLayer != null) { SelectedLayer.X += x; SelectedLayer.Y += y; pictureBox1.Invalidate(); } } } |