ドラッグ&ドラッグで画像を移動させて連結したいの続きです。今回は画像をそのままではなくトリミングされた画像を連結させます。
まずTreeNodeをダブルクリックしたらダイアログを表示させます。それ以外の部分は前回と同じです。
| 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 Form1 : Form {     public Form1()     {         InitializeComponent();         // アイテムをダブルクリックしたら編集用のフォームを表示する         TreeView1.MouseDoubleClick += TreeView1_MouseDoubleClick;         // その他は前回と同じ     }     List<FormForTrimming> FormForTrimmings = new List<FormForTrimming>();     [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]     private static extern int ShowWindow(IntPtr hWnd, uint Msg);     // アイテムをダブルクリックしたら編集用のフォームを表示する     private void TreeView1_MouseDoubleClick(object sender, MouseEventArgs e)     {         ShowFormForTrimming();     } } | 
さてどのようなダイアログを表示するかですがScrollPictureBoxを左右に配置して左側のScrollPictureBoxをドラッグするとそこに描画されているBitmapがトリミングされます。またNumericUpDownコントロールの値を変更することでもトリミングできます。トリミングされたBitmapは右側のScrollPictureBoxに表示されます。

最初に見た目の問題に関する部分です。
コンストラクタ内で自作メソッド InitNumericUpDowns()を実行してNumericUpDownコントロールに自由に値を設定できるようにしています。またイベントハンドラ Form1_Load内で左右に同じ大きさのScrollPictureBoxを表示するための処理をしています。
| 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | public partial class FormForTrimming : Form {     public FormForTrimming()     {         InitializeComponent();         // 見た目の問題         Load += Form1_Load;         Panel1.SizeChanged += Panel1_SizeChanged;         // NumericUpDownの上限下限を取り除く         InitNumericUpDowns();         // セットされたBitmapを表示する         Load += FormForTrimming_Load;         // ドラッグ&ドロップで画像をトリミングする         ScrollPictureBox1.PictureBoxMouseDown1 += ScrollPictureBox1_PictureBoxMouseDown1;         ScrollPictureBox1.PictureBoxMouseUp1 += ScrollPictureBox1_PictureBoxMouseUp1;         ScrollPictureBox1.FinishedDragRectangle += ScrollPictureBox1_FinishedDragRectangle;         // トリミングされようとしている領域に矩形を描画させるための設定         ScrollPictureBox1.DrawByDrag.Shape = Shape.DrawRectangle;         ScrollPictureBox1.DrawByDrag.LineWidth = 1;         ScrollPictureBox1.DrawByDrag.LineColor = Color.Black;         // アップダウンコントロールで値が指定された場合のトリミング処理         UpDownMarginLeft.ValueChanged += UpDownMarginLeft_ValueChanged;         UpDownMarginTop.ValueChanged += UpDownMarginTop_ValueChanged;         UpDownMarginRight.ValueChanged += UpDownMarginRight_ValueChanged;         UpDownMarginBottom.ValueChanged += UpDownMarginBottom_ValueChanged;         // アップダウンコントロールで値が指定された場合の拡大縮小処理         UpDownHeight.ValueChanged += UpDownHeight_ValueChanged;         UpDownWidth.ValueChanged += UpDownWidth_ValueChanged;         // このフォームにおける独自のイベント         ButtonApply.Click += ButtonApply_Click;     }     // 見た目の問題     private void Form1_Load(object sender, EventArgs e)     {         UpDownMarginLeft.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         UpDownMarginTop.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         UpDownMarginRight.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         UpDownMarginBottom.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         UpDownPosX.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         UpDownPosY.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelMarginLeft.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelMarginTop.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelMarginRight.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelMarginBottom.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelPosX.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         LabelPosY.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         ButtonApply.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;         Panel1.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;         Panel1_SizeChanged();     }     private void Panel1_SizeChanged(object sender, EventArgs e)     {         Panel1_SizeChanged();     }     void Panel1_SizeChanged()     {         // 左右に同じ大きさのScrollPictureBoxを表示する         int splitHalf = 5;         int leftWidth = Panel1.Width / 2 - splitHalf;         ScrollPictureBox1.Location = new Point(0, 0);         ScrollPictureBox1.Size = new Size(leftWidth, Panel1.Height);         ScrollPictureBox2.Location = new Point(Panel1.Width / 2 + splitHalf, 0);         ScrollPictureBox2.Size = new Size(leftWidth, Panel1.Height);     }     void InitNumericUpDowns()     {         UpDownPosX.Minimum = -10000;         UpDownPosY.Minimum = -10000;         UpDownPosX.Maximum = 10000;         UpDownPosY.Maximum = 10000;         UpDownWidth.Minimum = 0;         UpDownHeight.Minimum = 0;         UpDownWidth.Maximum = 10000;         UpDownHeight.Maximum = 10000;         UpDownMarginBottom.Minimum = 0;         UpDownMarginLeft.Minimum = 0;         UpDownMarginRight.Minimum = 0;         UpDownMarginTop.Minimum = 0;         UpDownMarginBottom.Maximum = 10000;         UpDownMarginLeft.Maximum = 10000;         UpDownMarginRight.Maximum = 10000;         UpDownMarginTop.Maximum = 10000;     } } | 
次にプロパティにかんする部分です。プロパティでアップダウンコントロールの値を変更したり取得できるようにします。アップダウンコントロールだけでなくチェックボックスやScrollPictureBoxに描画される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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | public partial class FormForTrimming : Form {     public int BitmapMarginLeft     {         get { return (int)UpDownMarginLeft.Value; }         set { UpDownMarginLeft.Value = value; }     }     public int BitmapMarginRight     {         get { return (int)UpDownMarginRight.Value; }         set { UpDownMarginRight.Value = value; }     }     public int BitmapMarginTop     {         get { return (int)UpDownMarginTop.Value; }         set { UpDownMarginTop.Value = value; }     }     public int BitmapMarginBottom     {         get { return (int)UpDownMarginBottom.Value; }         set { UpDownMarginBottom.Value = value; }     }     public int BitmapX     {         get { return (int)UpDownPosX.Value; }         set { UpDownPosX.Value = value; }     }     public int BitmapY     {         get { return (int)UpDownPosY.Value; }         set { UpDownPosY.Value = value; }     }     public int BitmapWidth     {         get { return (int)UpDownWidth.Value; }         set { UpDownWidth.Value = value; }     }     public int BitmapHeight     {         get { return (int)UpDownHeight.Value; }         set { UpDownHeight.Value = value; }     }     public bool EnableWidth     {         get { return CheckBoxEnableWidth.Checked; }         set { CheckBoxEnableWidth.Checked = value; }     }     public bool EnableHeight     {         get { return CheckBoxEnableHeight.Checked; }         set { CheckBoxEnableHeight.Checked = value; }     }     public bool Hidden     {         get { return CheckBoxHidden.Checked; }         set { CheckBoxHidden.Checked = value; }     }     public Bitmap SourceBitmap     {         get { return ScrollPictureBox1.FullScaleBitmap; }         set { ScrollPictureBox1.FullScaleBitmap = value; }     }     public Bitmap TrimmedBitmap     {         get { return ScrollPictureBox2.FullScaleBitmap; }         private set         {             ScrollPictureBox2.FullScaleBitmap = value;             if (value == null)                 return;             BitmapWidth = value.Width;             BitmapHeight = value.Height;         }     } } | 
ダイアログがLoadされたらTrimmedBitmapプロパティにSourceBitmapをトリミングして得られた画像を表示します。
GetTrimmedBitmap()メソッドはアップダウンコントロールの値からSourceBitmapをトリミングして得られるBitmapを取得するためのもので、GetEnlargedBitmap(Bitmap srcBitmap)メソッドはアップダウンコントロールの値とチェックボックスの状態からトリミングして得られたBitmapを拡大縮小した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 | public partial class FormForTrimming : Form {     private void FormForTrimming_Load(object sender, EventArgs e)     {         Bitmap bitmap = GetTrimmedBitmap();         TrimmedBitmap = GetEnlargedBitmap(bitmap);         bitmap.Dispose();     }     Bitmap GetTrimmedBitmap()     {         if (SourceBitmap == null)             return null;         Bitmap bitmap = SourceBitmap;         Rectangle rect = GetTrimmedRectangle(new Rectangle(0, 0, bitmap.Width, bitmap.Height));         if (rect.Width <= 0 || rect.Height <= 0)             return null;         Bitmap newBitmap = new Bitmap(rect.Width, rect.Height);         Graphics g = Graphics.FromImage(newBitmap);         g.DrawImage(bitmap, new Rectangle(0, 0, rect.Width, rect.Height), rect, GraphicsUnit.Pixel);         g.Dispose();         return newBitmap;     }     Rectangle GetTrimmedRectangle(Rectangle rect)     {         int left = (int)UpDownMarginLeft.Value;         int top = (int)UpDownMarginTop.Value;         int right = (int)UpDownMarginRight.Value;         int bottom = (int)UpDownMarginBottom.Value;         return new Rectangle(left, top, rect.Width - left - right, rect.Height - top - bottom);     }     Bitmap GetEnlargedBitmap(Bitmap srcBitmap)     {         if (srcBitmap == null)             return null;         int newWidth = BitmapWidth;         int newHeight = BitmapHeight;         double d = 1d * srcBitmap.Width / srcBitmap.Height;         if (CheckBoxEnableWidth.Checked && !CheckBoxEnableHeight.Checked)         {             newHeight = (int)(BitmapWidth / d);         }         if (!CheckBoxEnableWidth.Checked && CheckBoxEnableHeight.Checked)         {             newWidth = (int)(BitmapHeight * d);         }         if (!CheckBoxEnableWidth.Checked && !CheckBoxEnableHeight.Checked)         {             return new Bitmap(srcBitmap);         }         Bitmap newBitmap = new Bitmap(newWidth, newHeight);         Graphics g = Graphics.FromImage(newBitmap);         g.DrawImage(srcBitmap, new Rectangle(0, 0, newWidth, newHeight));         g.Dispose();         return newBitmap;     } } | 
ドラッグされたら画像をトリミングします。トリミングされる部分の境界線も描画されるようにScrollPictureBox.DrawByDrag.DoesShowをtrueに変更します。
ドラッグ&ドロップが終わったらScrollPictureBox.FinishedDragRectangleイベントが発生するのでイベントハンドラScrollPictureBox1_FinishedDragRectangle内で適切な処理をおこないます。ここではトリミングで得られたBitmapを拡大縮小してTrimmedBitmapプロパティにセットするとともに切り捨てられた上下左右の値をアップダウンコントロールにセットしています。
| 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 FormForTrimming : Form {     private void ScrollPictureBox1_PictureBoxMouseDown1(object sender, PictureBoxMouseEventArgs e)     {         ScrollPictureBox1.DrawByDrag.DoesShow = true;     }     private void ScrollPictureBox1_PictureBoxMouseUp1(object sender, PictureBoxMouseEventArgs e)     {         // トリミングしようとしている部分の矩形描画はマウスボタンが離されたときに終了する         ScrollPictureBox1.DrawByDrag.DoesShow = false;     }     private void ScrollPictureBox1_FinishedDragRectangle(object sender, TrimmedBitmapArgs e)     {         TrimmedBitmap = GetEnlargedBitmap(e.Bitmap);         UpDownMarginLeft.Value = e.MarginLeft;         UpDownMarginTop.Value = e.MarginTop;         UpDownMarginRight.Value = e.MarginRight;         UpDownMarginBottom.Value = e.MarginBottom;     } } | 
またドラッグ&ドロップではなくアップダウンコントロールで値が変更された場合もトリミング処理がおこなわれます。この処理がおこなわれるのは値が変更されたアップダウンコントロールと this.ActiveControl が同じときだけ(ユーザーによって値が変更された場合だけ)です。アップダウンコントロールで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 | public partial class FormForTrimming : Form {     private void UpDownMarginBottom_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownMarginBottom)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     }     private void UpDownMarginRight_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownMarginRight)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     }     private void UpDownMarginTop_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownMarginTop)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     }     private void UpDownMarginLeft_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownMarginLeft)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     }     private void UpDownWidth_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownWidth)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     }     private void UpDownHeight_ValueChanged(object sender, EventArgs e)     {         if (this.ActiveControl == UpDownHeight)         {             Bitmap bitmap = GetTrimmedBitmap();             TrimmedBitmap = GetEnlargedBitmap(bitmap);             bitmap.Dispose();         }     } } | 
[適用]ボタンが押されたときは変更を反映させなければならないのですが、その通知をする必要があります。そこでApplyイベントを定義します。これで[適用]ボタンが押されたときにはイベントが発生します。
// [適用]ボタンが押されたときの処理とイベント
| 1 2 3 4 5 6 7 8 9 | public partial class FormForTrimming : Form {     public event EventHandler Apply;     private void ButtonApply_Click(object sender, EventArgs e)     {         Apply?.Invoke(this, new EventArgs());     } } | 
×ボタンがおされたらフォームを閉じるのではなく非表示にします。
| 1 2 3 4 5 6 7 8 9 | public partial class FormForTrimming : Form {     protected override void OnClosing(CancelEventArgs e)     {         e.Cancel = true;         this.Visible = false;         base.OnClosing(e);     } } | 
Form1でTreeNodeがダブルクリックされたら自作メソッド ShowFormForTrimming()が呼び出されます。
FormForTrimmingが生成されたらそれをリストに格納して次に同じTreeNodeがダブルクリックされたら再利用します。同じTreeNodeが何度ダブルクリックされてもダイアログはひとつしか生成されないようにしています。
ShowFormForTrimming()メソッドのなかですでに生成されているダイアログがあるか調べます。最小化されている場合やほかのウィンドウの下に隠れている場合は最前面に表示させます。この処理のためにAPI関数 ShowWindow(IntPtr hWnd, uint Msg)を使っています。
FormForTrimmingオブジェクトを生成したらApplyイベントが処理できるようにイベントハンドラ FormForTrimming_Applyを追加します。そのあと必要なプロパティを設定してf.Show()を実行します。
| 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 | public partial class Form1 : Form {     List<FormForTrimming> FormForTrimmings = new List<FormForTrimming>();     [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]     private static extern int ShowWindow(IntPtr hWnd, uint Msg);     private void ShowFormForTrimming()     {         Data data = (Data)TreeView1.SelectedNode.Tag;         // すでに生成されているフォームがあるならそれを使う         FormForTrimming f0 = FormForTrimmings.FirstOrDefault(x => x.SourceBitmap == data.SourceBitmap);         if(f0 != null)         {             // 最小化されているならもとに戻す             const uint SW_RESTORE = 0x09;             if (f0.WindowState == FormWindowState.Minimized)                 ShowWindow(f0.Handle, SW_RESTORE);             f0.Show();             f0.Focus();             return;         }         FormForTrimming f = new FormForTrimming();         f.Apply += FormForTrimming_Apply;  // 編集用のフォームにおけるイベントを処理できるようにする         f.SourceBitmap = data.SourceBitmap;         f.BitmapMarginLeft = data.MarginLeft;         f.BitmapMarginTop = data.MarginTop;         f.BitmapMarginRight = data.MarginRight;         f.BitmapMarginBottom = data.MarginBottom;         f.BitmapX = data.X;         f.BitmapY = data.Y;         f.BitmapWidth = data.Width;         f.BitmapHeight = data.Height;         f.EnableWidth = data.EnableWidth;         f.EnableHeight = data.EnableHeight;         f.Hidden = data.Hidden;         FormForTrimmings.Add(f);         f.Show();     } } | 
次にApplyイベントが発生したときの処理ですが、FormForTrimmingオブジェクトから必要なプロパティを取得してShowBitmaps()メソッドを実行します。これで変更が反映されます。
| 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 | public partial class Form1 : Form {     private void FormForTrimming_Apply(object sender, EventArgs e)     {         List<Data> datas = GetDatas();         FormForTrimming f = ((FormForTrimming)sender);         Data data = datas.FirstOrDefault(x => x.SourceBitmap == f.SourceBitmap);         if (data != null)         {             data.X = f.BitmapX;             data.Y = f.BitmapY;             data.MarginLeft = f.BitmapMarginLeft;             data.MarginTop = f.BitmapMarginTop;             data.MarginRight = f.BitmapMarginRight;             data.MarginBottom = f.BitmapMarginBottom;             data.Width = f.BitmapWidth;             data.Height = f.BitmapHeight;             data.EnableWidth = f.EnableWidth;             data.EnableHeight = f.EnableHeight;             data.BitmapForShow = f.TrimmedBitmap;             data.Hidden = f.Hidden;         }         if (datas.Count > 0)         {             ShowBitmaps();         }     } } | 
新しくファイルを読み込むときやTreeNodeを削除するときは対応するFormForTrimmingオブジェクトが存在する場合に使われることはないのでMenuItemOpenFile_Click(object sender, EventArgs e)やMenuItemdDeleteNode_Click(object sender, EventArgs e)のなかでDisposeしています(前回の記事参照)。
