前回は矩形を挿入するために必要なレイヤーのクラスを作成しました。今回はダイアログを表示させて編集中の画像のなかに矩形を表示させる処理をおこないます。
まずダイアログのなかで発生するイベントはこれまでと同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class FormInsertRectangle : Form { public FormInsertRectangle() { InitializeComponent(); } public delegate void SetLayeredHandler(object sender, SetLayeredArgs args); public event SetLayeredHandler FixedLayer; public event SetLayeredHandler TemporaryFixingLayer; public event SetLayeredHandler MovedLayer; public event EventHandler EditCanceled; } |
アップダウンコントロールがたくさんあります。矩形そのものの幅、高さ、回転角、それから実際にこのレイヤーを全体のどの部分にセットするか、そのときレイヤーは縦横に伸縮するのかを設定する必要があります。
ひとつのアップダウンコントロールの値が変化することで別のアップダウンコントロールの値が変更される場合があります。このとき値の変更がエンドレスでおきることを防ぐためにアップダウンコントロールがダイアログ内でアクティブコントロールなのかで処理をわけています。
まずダイアログが生成されたら仮のレイヤーを作成します。これをつかって確定前の図形を表示させます。確定ボタンがおされたら仮のレイヤーのもつデータを真のレイヤーにコピーしてデータを反映させます。
RectangleLayer.Bitmapプロパティを取得しようとすると、RectangleLayer内のプロパティからBitmapが生成されます。そこでその前に適切な値をセットする必要があります。そのための処理をしているのがOnBitmapChanging()メソッドです。
OnBitmapChanging()メソッドはチェックボックスやアップダウンコントロールの値を取得してRectangleLayer.BitmapプロパティからBitmapを取得できるようにするためのものです。またOnBitmapChanged()はRectangleLayer.BitmapプロパティからBitmapを取得したあと、アップダウンコントロールの値を設定したり、仮のBitmapを表示するためのものです。もしIsSetLayer == trueのときはメインフォームのScroolPictureBoxにもデータを反映させます。
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
public partial class FormInsertRectangle : Form { // ダイアログが生成されたら仮のレイヤーを作成 RectangleLayer TempLayer = new RectangleLayer(); // 仮ではなく確定したデータが格納されているレイヤー(最初は null) public RectangleLayer Layer = null; public bool IsSetLayer = false; public bool IsReflect = true; void OnBitmapChanging() { if (checkBox1.Checked && !SelectedInnerColor.IsEmpty) TempLayer.SelectInnerColorArgb = SelectedInnerColor.ToArgb(); else if (checkBox1.Checked && TempLayer.SelectInnerColorArgb == 0) TempLayer.SelectInnerColorArgb = TempLayer.SelectColorArgb; else if (!checkBox1.Checked) TempLayer.SelectInnerColorArgb = 0; } void OnBitmapChanged() { Bitmap newBitmap; if (checkBox2.Checked) { newBitmap = TempLayer.Bitmap; scroolPictureBox1.Bitmap = newBitmap; numericUpDownWidth.Value = newBitmap.Width; numericUpDownHeight.Value = newBitmap.Height; } else { newBitmap = GetExtendBitmap(TempLayer.Bitmap, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); scroolPictureBox1.Bitmap = newBitmap; } if (TempLayer.Bitmap != null) { numericUpDownWidth.Value = (int)newBitmap.Width; numericUpDownHeight.Value = (int)newBitmap.Height; } else { numericUpDownWidth.Value = 1; numericUpDownHeight.Value = 1; } if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); MovedLayer?.Invoke(this, args); } } } |
ここから下はアップダウンコントロールの値が変更されたら新しく作成されたBitmapを表示します。
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 |
public partial class FormInsertRectangle : Form { private void numericUpDownRectangleWidth_ValueChanged(object sender, EventArgs e) { if (numericUpDownRectangleWidth == this.ActiveControl) { OnBitmapChanging(); TempLayer.RectangleWidth = (int)numericUpDownRectangleWidth.Value; OnBitmapChanged(); } } private void numericUpDownRectangleHeight_ValueChanged(object sender, EventArgs e) { if (numericUpDownRectangleHeight == this.ActiveControl) { OnBitmapChanging(); TempLayer.RectangleHeight = (int)numericUpDownRectangleHeight.Value; OnBitmapChanged(); } } private void numericUpDownAngle_ValueChanged(object sender, EventArgs e) { if (numericUpDownAngle == this.ActiveControl) { OnBitmapChanging(); TempLayer.Angle = (int)numericUpDownAngle.Value; OnBitmapChanged(); } } } |
ここからは表示位置や表示サイズや変更されたときに仮のレイヤーであるTempLayer.Bitmapを適切な位置やサイズに変更して表示させる処理をおこなうものです。
GetExtendBitmap(Bitmap bitmap, int width, int height)メソッドはTempLayer.Bitmapを適切なサイズに変更するためのものです。
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
public partial class FormInsertRectangle : Form { Bitmap GetExtendBitmap(Bitmap bitmap, int width, int height) { Bitmap newBitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(newBitmap); g.DrawImage(bitmap, new Rectangle(0, 0, width, height)); g.Dispose(); return newBitmap; } private void numericUpDownX_ValueChanged(object sender, EventArgs e) { if (numericUpDownX == this.ActiveControl) { if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); MovedLayer?.Invoke(this, args); } } } private void numericUpDownY_ValueChanged(object sender, EventArgs e) { if (numericUpDownY == this.ActiveControl) { if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); MovedLayer?.Invoke(this, args); } } } private void numericUpDownWidth_ValueChanged(object sender, EventArgs e) { if (numericUpDownWidth == this.ActiveControl) { scroolPictureBox1.Bitmap = GetExtendBitmap(TempLayer.Bitmap, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); MovedLayer?.Invoke(this, args); } } } private void numericUpDownHeight_ValueChanged(object sender, EventArgs e) { if (numericUpDownHeight == this.ActiveControl) { scroolPictureBox1.Bitmap = GetExtendBitmap(TempLayer.Bitmap, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); MovedLayer?.Invoke(this, args); } } } } |
表示させたい矩形には色を設定することができます。境界線だけでなかは透明とか境界線と内部の色を変えることもできます。これもSelectedColorプロパティやSelectedInnerColorプロパティを変えることで新しくBitmapが生成されます。
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 42 43 |
public partial class FormInsertRectangle : Form { Color SelectedColor = Color.Black; private void buttonColor_Click(object sender, EventArgs e) { ColorDialog dialog = new ColorDialog(); dialog.Color = SelectedColor; if (dialog.ShowDialog() == DialogResult.OK) { SelectedColor = dialog.Color; OnBitmapChanging(); TempLayer.SelectColorArgb = SelectedColor.ToArgb(); scroolPictureBox1.Bitmap = TempLayer.Bitmap; OnBitmapChanged(); } dialog.Dispose(); } Color SelectedInnerColor = Color.Empty; private void buttonInnerColor_Click(object sender, EventArgs e) { ColorDialog dialog = new ColorDialog(); dialog.Color = SelectedInnerColor; if (dialog.ShowDialog() == DialogResult.OK) { SelectedInnerColor = dialog.Color; if (checkBox1.Checked) { OnBitmapChanging(); TempLayer.SelectInnerColorArgb = SelectedInnerColor.ToArgb(); scroolPictureBox1.Bitmap = TempLayer.Bitmap; OnBitmapChanged(); } } dialog.Dispose(); } } |
checkBox1は内部を塗りつぶすか、checkBox1はオリジナルサイズで表示するかです。checkBox1がチェックされたらSelectedInnerColorを調べます。もし透明であれば境界線と同じ色が設定されます。そうでないならすでに設定されている色で内部が塗りつぶされます。チェックされていないのであれば内部は透明になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public partial class FormInsertRectangle : Form { private void checkBox1_CheckedChanged(object sender, EventArgs e) { if (checkBox1 == this.ActiveControl) { OnBitmapChanging(); if (!checkBox1.Checked) TempLayer.SelectInnerColorArgb = Color.Empty.ToArgb(); else { if (SelectedInnerColor.IsEmpty) SelectedInnerColor = SelectedColor; TempLayer.SelectInnerColorArgb = SelectedInnerColor.ToArgb(); } scroolPictureBox1.Bitmap = TempLayer.Bitmap; OnBitmapChanged(); } } } |
checkBox2はTempLayer.Bitmapで返された画像をnumericUpDownWidthの値とnumericUpDownHeightの値で拡大縮小するのか、それともTempLayer.Bitmapで返された画像にnumericUpDownWidthの値とnumericUpDownHeightの値を合わせるのかを設定します。実際の処理としてはチェックが入ったときにオリジナルサイズの値と画像に戻す処理をしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class FormInsertRectangle : Form { private void checkBox2_CheckedChanged(object sender, EventArgs e) { if (checkBox2.Checked) { scroolPictureBox1.Bitmap = TempLayer.Bitmap; numericUpDownWidth.Value = TempLayer.Bitmap.Width; numericUpDownHeight.Value = TempLayer.Bitmap.Height; if (IsSetLayer) { SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); TemporaryFixingLayer?.Invoke(this, args); } } } } |
[仮止め]をクリックするとIsSetLayerプロパティがtrueにかわります。これ以降はアップダウンコントロールの値などを変化させることでメインフォームのScroolPictureBoxに画像が反映されます。そして[レイヤーの位置を確定する]をクリックすると確定します。単に「×」をクリックしてダイアログを閉じると反映された変更は破棄され、もとの画像に戻ります。
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public partial class FormInsertRectangle : Form { public RectangleLayer Layer = null; private void ButtonTemporaryFix_Click(object sender, EventArgs e) { IsSetLayer = true; SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); TemporaryFixingLayer?.Invoke(this, args); } private void ButtonFix_Click(object sender, EventArgs e) { CloseWithLayerFix(); } void CloseWithLayerFix() { if (Layer == null) { Layer = new RectangleLayer() { Angle = (int)numericUpDownAngle.Value, SelectColorArgb = TempLayer.SelectColorArgb, SelectInnerColorArgb = TempLayer.SelectInnerColorArgb, RectangleWidth = (int)numericUpDownRectangleWidth.Value, RectangleHeight = (int)numericUpDownRectangleHeight.Value, X = (int)numericUpDownX.Value, Y = (int)numericUpDownY.Value, Width = (int)numericUpDownWidth.Value, Height = (int)numericUpDownHeight.Value, Name = textBoxLayerName.Text, }; } else { Layer.Angle = (int)numericUpDownAngle.Value; Layer.SelectColorArgb = TempLayer.SelectColorArgb; Layer.SelectInnerColorArgb = TempLayer.SelectInnerColorArgb; Layer.RectangleWidth = (int)numericUpDownRectangleWidth.Value; Layer.RectangleHeight = (int)numericUpDownRectangleHeight.Value; Layer.X = (int)numericUpDownX.Value; Layer.Y = (int)numericUpDownY.Value; Layer.Width = (int)numericUpDownWidth.Value; Layer.Height = (int)numericUpDownHeight.Value; Layer.Name = textBoxLayerName.Text; } SetLayeredArgs args = new SetLayeredArgs( Layer, textBoxLayerName.Text, TempLayer.Bitmap, (int)numericUpDownX.Value, (int)numericUpDownY.Value, (int)numericUpDownWidth.Value, (int)numericUpDownHeight.Value); FixedLayer?.Invoke(this, args); IsSetLayer = true; IsReflect = true; this.Close(); } } |
ダイアログを閉じようとしたときの処理です。これまでならこのようにしていたのですが・・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class FormInsertRectangle : Form { protected override void OnFormClosing(FormClosingEventArgs e) { if (!IsReflect) { DialogResult dr = MessageBox.Show( "このままダイアログを閉じると変更は反映されません。変更を反映させますか?", "確認", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (dr == DialogResult.Yes) CloseWithLayerFix(); else if (dr == DialogResult.No) EditCanceled?.Invoke(this, new EventArgs()); else if (dr == DialogResult.Cancel) { e.Cancel = true; return; } } base.OnFormClosing(e); } } |
上記の方法だとダイアログを閉じようとすると毎回確認のダイアログが表示されます。しかも親ウィンドウを閉じようとしたときも同じような現象がおきます。ちょっとうっとうしいので単純に以下のようにします。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class FormInsertRectangle : Form { protected override void OnFormClosing(FormClosingEventArgs e) { if (!IsReflect) { EditCanceled?.Invoke(this, new EventArgs()); } base.OnFormClosing(e); } } |
メニューから[レイヤーの追加] ⇒ [矩形を追加する]を選択したときはフィールド変数 Layerはnullです。もし既存のレイヤーを編集するときはLayerは非nullです。この場合、現在のデータをダイアログに読み込む必要があります。そのための処理はダイアログがロードされたときです。
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 |
public partial class FormInsertRectangle : Form { protected override void OnLoad(EventArgs e) { // もし既存の確定したデータが格納されているレイヤーがあるなら・・・ if (Layer != null) { // 既存の確定したデータをダイアログのコントロールにセット numericUpDownAngle.Value = Layer.Angle; numericUpDownHeight.Value = Layer.Height; numericUpDownWidth.Value = Layer.Width; numericUpDownX.Value = Layer.X; numericUpDownY.Value = Layer.Y; numericUpDownRectangleWidth.Value = Layer.RectangleWidth; numericUpDownRectangleHeight.Value = Layer.RectangleHeight; textBoxLayerName.Text = Layer.Name; scroolPictureBox1.Bitmap = GetExtendBitmap(Layer.Bitmap, Layer.Width, Layer.Height); SelectedColor = Color.FromArgb(Layer.SelectColorArgb); SelectedInnerColor = Color.FromArgb(Layer.SelectInnerColorArgb); checkBox1.Checked = Layer.SelectInnerColorArgb != 0; // 仮のレイヤーに既存の確定したデータをコピーする TempLayer.CopyLayer(Layer); } base.OnLoad(e); } } |
あとはメインフォームからダイアログを表示して追加したいレイヤーを編集できるようにするだけです。
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 |
public partial class Form1 : Form { private void MenuItemAddRectangle_Click(object sender, EventArgs e) { FormInsertRectangle form2 = new FormInsertRectangle(); form2.FixedLayer += Form2_FixedLayer; form2.MovedLayer += Form2_TemporaryFixingLayer; form2.TemporaryFixingLayer += Form2_TemporaryFixingLayer; form2.EditCanceled += Form2_EditCanceled; form2.Show(); } private void Item_Click(object sender, EventArgs e) { ToolStripItem item = (ToolStripItem)sender; Layer layer = (Layer)item.Tag; if (layer is StringLayer) ShowStringLayerForm((StringLayer)layer); else if (layer is ImageLayer) ShowImageLayerForm((ImageLayer)layer); else if (layer is RectangleLayer) ShowRectangleLayerForm((RectangleLayer)layer); } public void ShowRectangleLayerForm(RectangleLayer layer) { FormInsertRectangle form2 = new FormInsertRectangle(); form2.Layer = (RectangleLayer)layer; form2.IsSetLayer = true; form2.FixedLayer += Form2_FixedLayer; form2.TemporaryFixingLayer += Form2_TemporaryFixingLayer; form2.MovedLayer += Form2_TemporaryFixingLayer; form2.EditCanceled += Form2_EditCanceled; form2.Show(this); } } |
コンテキストメニューからも編集できるように以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class ContextMenuEx1 { void Item_Click1(object sender, EventArgs e) { ToolStripMenuItem mi = (ToolStripMenuItem)sender; Layer layer = (Layer)mi.Tag; if (layer is StringLayer) _form.ShowStringLayerForm((StringLayer)layer); else if (layer is ImageLayer) _form.ShowImageLayerForm((ImageLayer)layer); // 以下2行を追加 else if (layer is RectangleLayer) _form.ShowRectangleLayerForm((RectangleLayer)layer); } } |
またシリアル化できるように以下の処理も必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { // シリアル化のときにXmlSerializerのコンストラクタに渡す必要がある // XmlSerializer xml = new XmlSerializer(typeof(Doc), GetExtraTypes()); Type[] GetExtraTypes() { List<Type> types = new List<Type>(); types.Add(typeof(TrimingInfo)); types.Add(typeof(RotateInfo)); types.Add(typeof(ImageLayer)); types.Add(typeof(StringLayer)); types.Add(typeof(RectangleLayer)); return types.ToArray(); } } |