using HalconDotNet; using LB_SmartVisionCameraDevice.PHM6000; using LB_VisionControl; using LB_VisionProcesses.Cameras.HRCameras; using LB_VisionProcesses.Cameras.LBCameras; using MVSDK_Net; using Newtonsoft.Json.Linq; using OpenCvSharp; using System; using System.Collections.Concurrent; using System.Data; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Text; using System.Windows.Forms; using System.Xml.Linq; namespace LB_VisionProcesses.Cameras { public partial class CameraForm : Form { Bitmap Bitmap = null; BaseCamera camera { get; set; } CameraConfig camConfig { get; set; } ConcurrentDictionary dicCameras { get; set; } string fullPath = string.Empty; public CameraForm() { InitializeComponent(); } public CameraForm(ConcurrentDictionary dicCameras, CameraConfig camConfig, string fullPath) { InitializeComponent(); //传入相机句柄后禁用连接/断开按键 //this.btnOpen.Enabled = false; //this.btnClose.Enabled = false; //this.cmbBrand.Enabled = false; this.panel_Picture.Controls.Clear(); this.dicCameras = dicCameras; this.fullPath = fullPath; this.camConfig = camConfig; foreach (var CamSN in dicCameras.Keys) cmbSN.Items.Add(CamSN); Enum.TryParse(camConfig.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource); if (TriggerSource == TriggerSource.Software) radioButtonSoft.Checked = true; else radioButtonSoft.Checked = false; this.Text = camConfig.strProcessName; txtExp.Text = camConfig.Params.Inputs["曝光时间"].ToString(); txtGain.Text = camConfig.Params.Inputs["增益"].ToString(); ckbLocalTest.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否本地取图"].ToString()); if (camConfig.Params.Inputs["本地取图路径"] is List) { List lstPaths = camConfig.Params.Inputs["本地取图路径"] as List; if (lstPaths != null && lstPaths.Count > 0) { foreach (var path in lstPaths) cmbImagesPath.Items.Add(path); cmbImagesPath.Text = lstPaths[0]; } } } UserPictureBox onlinePictureBox { get; set; } private System.Windows.Forms.Timer updateTimer; Total total = new Total { iImageCount = 0, iScanCount = 0 }; DateTime startGrabtime = DateTime.Now; private void CameraForm_Load(object sender, EventArgs e) { // 设置一个定时器,每 100 毫秒触发一次 updateTimer = new System.Windows.Forms.Timer(); updateTimer.Interval = 1000; // 设置定时器间隔为 1000 毫秒 updateTimer.Tick += (sender, e) => UpdateUI(); updateTimer.Start(); onlinePictureBox = new UserPictureBox(panel_Picture); this.panel_Picture.Controls.Clear(); this.panel_Picture.Controls.Add(onlinePictureBox); onlinePictureBox.Dock = DockStyle.Fill; // 使用 LINQ 获取所有枚举值并打印 var brands = Enum.GetValues(typeof(CameraBrand)).Cast(); foreach (var brand in brands) { cmbBrand.Items.Add(brand.ToString()); } //选择Cam会触发ValueChanged事件,没有输入相机的情况下选择到-1 if (camConfig != null) { string SN = camConfig.Params.Inputs["相机SN"].ToString(); int Index = cmbSN.FindString(SN); cmbSN.Text = SN; cmbSN.SelectedIndex = Index; // 如果没找到索引(可能是新增的),手动触发一次逻辑以更新UI if (Index == -1) { cmbSN_SelectedIndexChanged(null, null); } ckbLocalTest.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否本地取图"].ToString()); ckbUpParams.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否每次写入参数"].ToString()); ckbRegrab.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否失败重新取图"].ToString()); txtTimeout.Text = camConfig.Params.Inputs["超时时间"].ToString(); if (camConfig.OutputImage != null && camConfig.OutputImage is Bitmap bitmap) onlinePictureBox.Image = bitmap; lblCapTime.Text = $"{camConfig.RunTime}ms"; } } private void UpdateUI() { try { // 如果当前不是 UI 线程,则通过 Invoke 将操作调度到 UI 线程 if (this.InvokeRequired) { this.Invoke(new Action(() => { double fps = total.iImageCount / (DateTime.Now - startGrabtime).TotalSeconds; this.lblPicCount.Text = total.iImageCount.ToString(); })); } else { // 如果已经在 UI 线程上,直接更新 double fps = total.iImageCount / (DateTime.Now - startGrabtime).TotalSeconds; this.lblPicCount.Text = total.iImageCount.ToString(); } } catch { } } private void cmbSN_MouseDown(object sender, MouseEventArgs e) { if (dicCameras != null && dicCameras.Count > 0) return; cmbSN.Items.Clear(); // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { // 使用 switch 语句来判断枚举值 switch (brand) { case CameraBrand.HRCamera: camera = new HRCamera(); break; case CameraBrand.LBCamera: camera = new LBCamera(); break; default: Debug.WriteLine("未知品牌"); break; } foreach (var item in camera.GetListEnum()) { if (!cmbSN.Items.Contains(item)) cmbSN.Items.Add(item); } } else { Debug.WriteLine("无效的枚举值!"); } } private void cmbSN_SelectedIndexChanged(object sender, EventArgs e) { btnEdit.Enabled = false; //camConfig不为空(输入有相机),更改相机选择需要重新加载刷图事件 if (camConfig != null && camera != null) { camera.ImageGrabbed -= GetImageBllComplete; } //输入无camConfig,但是该相机已经打开过了需要释放原相机 else if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.Dispose(); } if (dicCameras != null && dicCameras.Count > 0 && dicCameras.ContainsKey(cmbSN.Text)) camera = dicCameras[cmbSN.Text]; //说明相机已经初始化成功 if (camera != null) { int Index = cmbBrand.FindString(camera.Brand.ToString()); if (Index >= 0) { cmbBrand.Text = camera.Brand.ToString(); cmbBrand.SelectedIndex = Index; } if (camera.isGrabbing) { camera.ImageGrabbed -= GetImageBllComplete; camera.ImageGrabbed += GetImageBllComplete; this.btnEdit.Enabled = true; } // 如果相机存在于字典中,说明是已连接的设备,允许编辑 else if (dicCameras != null && dicCameras.ContainsKey(camera.SN)) { this.btnEdit.Enabled = true; } } } private void btnOpen_Click(object sender, EventArgs e) { // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.Dispose(); } // 使用 switch 语句来判断枚举值 switch (brand) { case CameraBrand.LBCamera: camera = new LBCamera(); break; case CameraBrand.HRCamera: camera = new HRCamera(); break; default: Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】未知品牌"); return; } IntPtr displayHandle = onlinePictureBox.Handle; if (brand == CameraBrand.LBCamera) { onlinePictureBox.Visible = false; displayHandle = this.panel_Picture.Handle; } else { onlinePictureBox.Visible = true; } if (cmbSN.Items.Count > 0 && camera.InitDevice(cmbSN.Text.ToString(), displayHandle)) { camera.ImageGrabbed -= GetImageBllComplete; camera.ImageGrabbed += GetImageBllComplete; MessageBox.Show(camera.SN + "打开成功"); this.btnEdit.Enabled = true; } } else { Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】无效的枚举值!"); } if (camera != null) { this.BeginInvoke(new Action(() => { double exp = 0; double gain = 0; camera.GetExpouseTime(out exp); txtExp.Text = exp.ToString(); camera.GetGain(out gain); txtGain.Text = gain.ToString(); camera.GetTriggerMode(out TriggerMode mode, out TriggerSource source); if (source != TriggerSource.Software) radioButtonHard.Checked = true; else radioButtonSoft.Checked = true; })); } } private void btnClose_Click(object sender, EventArgs e) { if (camera == null) return; this.btnEdit.Enabled = false; if (camera.CloseDevice()) { onlinePictureBox.Visible = true; MessageBox.Show(camera.SN + "断开成功"); this.panel_Picture.Controls.Clear(); this.panel_Picture.Controls.Add(onlinePictureBox); } } private void btnEdit_Click(object sender, EventArgs e) { if (camera == null) { return; } using (Form editForm = new Form()) { editForm.Text = "高级参数设置 - " + camera.SN; editForm.Size = new System.Drawing.Size(400, 500); editForm.StartPosition = FormStartPosition.CenterParent; editForm.FormBorderStyle = FormBorderStyle.SizableToolWindow; PropertyGrid pg = new PropertyGrid(); pg.Dock = DockStyle.Fill; if (camera is LBCamera phmCamera) { pg.SelectedObject = phmCamera.GetSensorConfig(); pg.PropertyValueChanged += (s, ev) => { phmCamera.UpdateSensorConfig((PHM6000SensorConfig)pg.SelectedObject); }; } else { pg.SelectedObject = new CameraAdvancedSettings(camera); } editForm.Controls.Add(pg); editForm.ShowDialog(); } } /// /// 相机高级设置包装类 /// public class CameraAdvancedSettings { private BaseCamera _camera; public CameraAdvancedSettings(BaseCamera camera) { _camera = camera; } [System.ComponentModel.Category("触发设置"), System.ComponentModel.Description("触发源")] public TriggerSource TriggerSource { get { _camera.GetTriggerMode(out _, out TriggerSource s); return s; } set { _camera.GetTriggerMode(out TriggerMode m, out _); _camera.SetTriggerMode(m, value); } } [System.ComponentModel.Category("触发设置"), System.ComponentModel.Description("触发极性")] public TriggerPolarity TriggerPolarity { get { _camera.GetTriggerPolarity(out TriggerPolarity p); return p; } set { _camera.SetTriggerPolarity(value); } } [System.ComponentModel.Category("触发设置"), System.ComponentModel.Description("触发延时(us)")] public double TriggerDelay { get { _camera.GetTriggerDelay(out double d); return d; } set { _camera.SetTriggerDelay(value); } } [System.ComponentModel.Category("触发设置"), System.ComponentModel.Description("触发滤波(us)")] public double TriggerFilter { get { _camera.GetTriggerFliter(out double f); return f; } set { _camera.SetTriggerFliter(value); } } [System.ComponentModel.Category("图像设置"), System.ComponentModel.Description("自动白平衡")] public void AutoBalanceWhite() { _camera.AutoBalanceWhite(); } } /// /// 相机回调运行 /// 注意:对于LBCamera(3D线扫相机),使用SDK自动显示模式 /// SDK会自动将图像显示到对应的控件上,此回调只用于统计采集次数 /// /// /// private void GetImageBllComplete(object sender, CameraEventArgs e) { // 对于LBCamera(SDK自动显示模式),Bitmap为null,我们只需要统计 if (camera.Brand == CameraBrand.LBCamera) { if (e is LBCameraEventArgs args && args.IsComplete) { total.iImageCount++; // 不需要更新onlinePictureBox.Image,SDK会自动显示 } return; } // 对于2D相机(手动处理模式) if (e.Bitmap == null) return; lock (e.Bitmap) { if (this.InvokeRequired) // 检查是否需要在UI线程上调用 { this.Invoke(new Action(() => { onlinePictureBox.Image = e.Bitmap; })); // 递归调用自身,但这次在UI线程上 } else { onlinePictureBox.Image = e.Bitmap; } } total.iImageCount++; try { Bitmap = e.Bitmap.Clone() as Bitmap; } catch { } } private ulong ImageWidth; private ulong ImageHeight; private Pen pen = new Pen(Color.Blue, 3f); private System.Drawing.Point[] stPointList = new System.Drawing.Point[4]; private void btnGrabOnce_Click(object sender, EventArgs e) { DateTime StartTime = DateTime.Now; if (camera == null) return; Task.Factory.StartNew(() => { // 设置曝光和增益 camera.SetExpouseTime(Convert.ToDouble(txtExp.Text)); camera.SetGain(Convert.ToDouble(txtGain.Text)); bool success = false; Bitmap bitmap = null; // 调用基类接口的单次采集方法 // LBCamera重写了StartSingleGrab方法,其他相机调用返回false success = camera.StartSingleGrab(); if (success) { // 对于LBCamera(SDK自动显示模式),等待采集完成即可 // 对于2D相机,等待Bitmap事件 if (camera.Brand == CameraBrand.LBCamera) { // SDK自动显示模式下,等待采集完成 using (AutoResetEvent waitHandle = new AutoResetEvent(false)) { bool captured = false; EventHandler handler = (s, evt) => { if (evt is LBCameraEventArgs args && args.IsComplete) { captured = true; waitHandle.Set(); } }; camera.ImageGrabbed += handler; try { // 等待5秒超时 if (waitHandle.WaitOne(5000)) { // SDK自动显示模式,不返回Bitmap // 但采集已完成 } this.BeginInvoke(new Action(() => { this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; })); } finally { camera.ImageGrabbed -= handler; camera.StopGrabbing(); } } } else { // 对于2D相机,等待图像数据 using (AutoResetEvent waitHandle = new AutoResetEvent(false)) { Bitmap captured = null; EventHandler handler = (s, evt) => { // 对于2D相机,直接接收图像 if (!(evt is LBCameraEventArgs) && evt.Bitmap != null) { captured = evt.Bitmap.Clone() as Bitmap; waitHandle.Set(); } }; camera.ImageGrabbed += handler; try { // 等待5秒超时 if (waitHandle.WaitOne(5000)) { bitmap = captured; } } finally { camera.ImageGrabbed -= handler; camera.StopGrabbing(); } } this.BeginInvoke(new Action(() => { if (bitmap != null) { this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; onlinePictureBox.Image = bitmap; } else { this.lblCapTime.Text = "-1ms"; } })); } } else { // 如果StartSingleGrab失败,回退到传统的GetImageWithSoftTrigger camera.GetImageWithSoftTrigger(out bitmap); this.BeginInvoke(new Action(() => { if (bitmap != null) { this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; onlinePictureBox.Image = bitmap; } else { this.lblCapTime.Text = "-1ms"; } })); } }); } private void btnStartHard_Click(object sender, EventArgs e) { if (camera == null) return; total.Clear(); // 停止当前采集 camera.StopGrabbing(); // 调用基类接口的连续采集方法 // LBCamera会调用StartContinuousGrab方法,其他相机使用原有的StartWith_HardTriggerModel camera.StartContinuousGrab(); startGrabtime = DateTime.Now; cmbSN.Enabled = false; cmbBrand.Enabled = false; } private void btnStartGrab_Click(object sender, EventArgs e) { if (camera == null) return; total.Clear(); // 停止当前采集 camera.StopGrabbing(); // 调用基类接口的连续采集方法 // LBCamera会调用StartContinuousGrab方法,其他相机使用原有的StartWith_SoftTriggerModel camera.StartContinuousGrab(); startGrabtime = DateTime.Now; cmbSN.Enabled = false; cmbBrand.Enabled = false; } private void btnCloseGrab_Click(object sender, EventArgs e) { try { if (camera == null) return; // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { Task.Factory.StartNew(() => { camera.StopGrabbing(); if (brand != CameraBrand.LBCamera) { camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); camera.StartGrabbing(); } }); } cmbSN.Enabled = true; cmbBrand.Enabled = true; } catch { } } private void btnLocalGrab_Click(object sender, EventArgs e) { try { if (cmbImagesPath.Items.Count <= 0) return; DateTime StartTime = DateTime.Now; int index = cmbImagesPath.SelectedIndex; if (cmbImagesPath.Items.Count > index + 1) cmbImagesPath.Text = cmbImagesPath.Items[index + 1].ToString(); else cmbImagesPath.Text = cmbImagesPath.Items[0].ToString(); using (Mat src = Cv2.ImRead(cmbImagesPath.Text)) { if (src != null && !src.Empty()) { using (Bitmap bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(src)) { this.Invoke(new Action(() => { this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; })); onlinePictureBox.Image = bitmap; } } } } catch { } } private void txtExp_TextChanged(object sender, EventArgs e) { if (camera == null) return; double exp = 1000; if (double.TryParse(txtExp.Text.ToString(), out exp)) camera.SetExpouseTime(exp); } private void txtGain_TextChanged(object sender, EventArgs e) { if (camera == null) return; double gain = 10; if (double.TryParse(txtGain.Text.ToString(), out gain)) camera.SetGain(gain); } private void btnAddImages_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); // 设置文件对话框的属性 openFileDialog.Multiselect = true; // 不允许多选 // 设置文件过滤器,支持多种文件类型 openFileDialog.Filter = "Image Files (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|All Files (*.*)|*.*"; // 显示文件对话框 DialogResult result = openFileDialog.ShowDialog(); // 处理对话框返回结果 if (result == DialogResult.OK) { cmbImagesPath.Items.Clear(); // 获取用户选择的文件名 string[] selectedFiles = openFileDialog.FileNames; // 将文件路径添加到队列中 foreach (string file in selectedFiles) cmbImagesPath.Items.Add(file); if (selectedFiles.Length > 0) cmbImagesPath.Text = selectedFiles[0]; } } private void CameraForm_FormClosing(object sender, FormClosingEventArgs e) { //路径为空说明无需保存 if (string.IsNullOrEmpty(fullPath)) { e.Cancel = false; //确认关闭窗体 return; } DialogResult res = MessageBox.Show("是否保存?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); //保存结果信息 /// 参数1:显示文本,参数2:标题,参数3:按键类型,参数4:显示图标 if (res == DialogResult.Cancel) //取消 { e.Cancel = true; //取消关闭窗体 return; } if (res == DialogResult.Yes) //保存VPP { TriggerSource actualSource = TriggerSource.Line0; TriggerPolarity polarity = TriggerPolarity.RisingEdge; double delay = 0; double filter = 0; if (camera != null) { camera.GetTriggerMode(out _, out actualSource); camera.GetTriggerPolarity(out polarity); camera.GetTriggerDelay(out delay); camera.GetTriggerFliter(out filter); } camConfig.Params.Inputs.Add("相机SN", cmbSN.Text); camConfig.Params.Inputs.Add("触发模式", TriggerMode.On); camConfig.Params.Inputs.Add("触发方式", radioButtonSoft.Checked ? TriggerSource.Software : actualSource); camConfig.Params.Inputs.Add("触发极性", polarity); camConfig.Params.Inputs.Add("触发延时", delay); camConfig.Params.Inputs.Add("触发消抖", filter); camConfig.Params.Inputs.Add("是否本地取图", ckbLocalTest.Checked); camConfig.Params.Inputs.Add("本地取图路径", cmbImagesPath.Items.Cast().ToList()); camConfig.Params.Inputs.Add("是否失败重新取图", ckbRegrab.Checked); if (int.TryParse(txtTimeout.Text, out int timeout)) camConfig.Params.Inputs.Add("超时时间", timeout); if (float.TryParse(txtExp.Text, out float exp)) camConfig.Params.Inputs.Add("曝光时间", exp); if (float.TryParse(txtGain.Text, out float gain)) camConfig.Params.Inputs.Add("增益", gain); string filePath = Path.GetDirectoryName(fullPath); camConfig.Save(filePath); e.Cancel = false; //确认关闭窗体 return; } } private void CameraForm_FormClosed(object sender, FormClosedEventArgs e) { onlinePictureBox.Visible = true; this.panel_Picture.Controls.Clear(); this.panel_Picture.Controls.Add(onlinePictureBox); this.onlinePictureBox.Image = null; if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.StopGrabbing(); camera.GetTriggerMode(out _, out TriggerSource actualSource); if (radioButtonSoft.Checked) camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); else camera.SetTriggerMode(TriggerMode.On, actualSource); // LBCamera在StartGrabbing时会直接开启激光和采集,因此关闭窗口时不应自动重启采集 // 其他相机(如2D相机)通常需要保持Grabbing状态以接收触发 if (camera.Brand != CameraBrand.LBCamera) { camera.StartGrabbing(); } } //路径为空说明为测试模式,需要释放相机 if (string.IsNullOrEmpty(fullPath) && camera != null) camera.Dispose(); } private ImageFormat GetImageFormat(string extension) { return extension.ToLower() switch { ".jpg" or ".jpeg" => ImageFormat.Jpeg, ".png" => ImageFormat.Png, ".bmp" => ImageFormat.Bmp, ".gif" => ImageFormat.Gif, ".tiff" => ImageFormat.Tiff, _ => ImageFormat.Png // 默认格式 }; } private void btnSaveImage_Click(object sender, EventArgs e) { if (Bitmap == null) { MessageBox.Show("没有图像可保存!", "异常"); return; } // 创建 SaveFileDialog 实例 using (SaveFileDialog saveFileDialog = new SaveFileDialog()) { // 设置对话框标题 saveFileDialog.Title = "保存文件"; // 设置默认路径 saveFileDialog.InitialDirectory = Application.StartupPath; // 设置文件过滤器,支持多种文件类型 saveFileDialog.Filter = "Image Files (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|All Files (*.*)|*.*"; // 设置默认文件名 saveFileDialog.FileName = "output.jpg"; // 显示对话框并检查用户是否点击了保存按钮 if (saveFileDialog.ShowDialog() == DialogResult.OK) { // 获取用户选择的文件路径 string fullPath = saveFileDialog.FileName; Debug.WriteLine("选择的文件路径是: " + fullPath); if (!string.IsNullOrEmpty(fullPath)) { Bitmap.Save(fullPath, GetImageFormat(Path.GetExtension(fullPath))); } } } } private void radioButtonSoft_CheckedChanged(object sender, EventArgs e) { btnGrabOnce.Enabled = radioButtonSoft.Checked; btnStartGrab.Enabled = radioButtonSoft.Checked; btnCloseGrab.Enabled = radioButtonSoft.Checked; btnStartHard.Enabled = !radioButtonSoft.Checked; if (camera == null) return; camera.GetTriggerMode(out _, out TriggerSource currentSource); if (radioButtonSoft.Checked) { camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); } else { // 如果当前已经是硬件触发源,则保持;否则默认Line0 if (currentSource == TriggerSource.Software) camera.SetTriggerMode(TriggerMode.On, TriggerSource.Line0); else camera.SetTriggerMode(TriggerMode.On, currentSource); } } System.Windows.Forms.ToolTip ToolTip = new System.Windows.Forms.ToolTip(); private void cmbImagesPath_MouseHover(object sender, EventArgs e) { if (cmbImagesPath.SelectedIndex != -1 && camConfig != null && int.TryParse(camConfig.Params.Inputs["本地取图序号"]?.ToString(), out int indexPath)) { // 显示ToolTip(相对于控件的位置) ToolTip.Show($"当前索引序号:{indexPath}", (Control)sender, ((Control)sender).Width / 2, // x偏移 -20, // y偏移(负值表示上方) 2000); // 显示时间(ms) } } private void cmbImagesPath_SelectedIndexChanged(object sender, EventArgs e) { if (camConfig != null) camConfig.Params.Inputs["本地取图序号"] = cmbImagesPath.SelectedIndex; } private void controlBox_2DCameraForm_Click(object sender, EventArgs e) { } private void dungeonControlBox_2DCameraForm_Click(object sender, EventArgs e) { // this.Close(); } private void theme_2DCameraForm_Click(object sender, EventArgs e) { } } }