为何触发‘Parameter is not valid’异常?WinForms PictureBox问题
问题场景
选中ListBox中的「Free Roam Shape」项时,代码执行到pictureBoxCroppedImages.Image = composedImage;,通过断点确认composedImage是非空的Bitmap对象,但执行该行后并未进入catch块,直接在Program.cs的Application.Run(new Form1())处抛出以下异常:
System.ArgumentException HResult=0x80070057 Message=Parameter is not valid. Source=System.Drawing StackTrace: at System.Drawing.Image.get_Width() at System.Drawing.Image.get_Size() at System.Windows.Forms.PictureBox.ImageRectangleFromSizeMode(PictureBoxSizeMode mode) at System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe) at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer) at System.Windows.Forms.Control.WmPaint(Message& m) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
关联代码
ListBox选中索引变更事件
private void listBoxCroppedImages_SelectedIndexChanged(object sender, EventArgs e) { if (!isRealTimeCropping) { var si = ((ListBox)sender).SelectedIndex; if (si >= 0 && si < MyItems.Count) { var selectedItem = MyItems[si]; // 检查选中项是否为自由漫游形状 if (selectedItem.Message == "Free Roam Shape") { // 显示自由漫游形状 DisplayFreeRoamShape(); } else { // 显示矩形裁剪图 var selectedKey = FileList.Keys.ElementAt(si); // 获取对应索引的键 var selectedValue = FileList.Values.ElementAt(si); RichTextBoxController(si, selectedKey, selectedValue); if (!string.IsNullOrEmpty(selectedKey) && FileList.ContainsKey(selectedKey)) { var filePath = FileList[selectedKey]; if (File.Exists(filePath)) { // 从选中键中提取位置和尺寸 Point location = ExtractLocation(selectedKey); Size size = ExtractSize(selectedKey); // 加载图片 Image croppedImage = System.Drawing.Image.FromFile(filePath); // 创建带透明背景的新位图 Bitmap composedImage = new Bitmap(pictureBoxCroppedImages.Width, pictureBoxCroppedImages.Height); using (Graphics g = Graphics.FromImage(composedImage)) { g.Clear(Color.Transparent); // 在指定位置绘制裁剪后的图片 g.DrawImage(croppedImage, new Rectangle(location, size)); } // 将合成图片设置给PictureBox pictureBoxCroppedImages.Image = composedImage; } } } } } else { var si = ((ListBox)sender).SelectedIndex; if (si >= 0 && si < FileList.Count) { var selectedKey = FileList.Keys.ElementAt(si); // 获取对应索引的键 var selectedValue = FileList.Values.ElementAt(si); RichTextBoxController(si, selectedKey, selectedValue); } } }
自由漫游形状显示方法
private void DisplayFreeRoamShape() { var si = listBoxCroppedImages.SelectedIndex; if (si >= 0 && si < MyItems.Count) { var selectedItem = MyItems[si]; if (selectedItem.Message == "Free Roam Shape") { try { // 加载并显示对应的自由漫游形状 if (FreeRoamShapes.Count > 0) { var freeRoamShape = FreeRoamShapes[0]; // 假设只有一个自由漫游形状 using (Bitmap composedImage = new Bitmap(pictureBoxCroppedImages.Width, pictureBoxCroppedImages.Height)) using (Graphics g = Graphics.FromImage(composedImage)) { g.Clear(Color.Transparent); using (Pen pen = new Pen(freeRoamShape.Color, 2f)) { g.DrawPath(pen, freeRoamShape.Path); } try { pictureBoxCroppedImages.Image = composedImage; } catch(Exception ex) { // 处理显示自由漫游形状时的异常 MessageBox.Show("Error displaying the free roam shape: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } catch (Exception ex) { // 处理显示自由漫游形状时的异常 MessageBox.Show("Error displaying the free roam shape: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }
问题原因
核心问题出在DisplayFreeRoamShape方法中:composedImage被包裹在using语句中,using块结束时会自动调用Bitmap.Dispose()释放其底层GDI+资源。
虽然你在using块内把composedImage赋值给了pictureBoxCroppedImages.Image,但PictureBox只是持有这个Bitmap对象的引用。当using块执行完毕,Bitmap资源被销毁,后续PictureBox触发重绘(比如OnPaint方法)时,尝试访问已释放资源的Width/Size属性,就会抛出“Parameter is not valid”异常。
另外,异常没有被你的catch块捕获,是因为这个异常并非在赋值语句执行时抛出,而是在后续UI线程的消息循环中处理重绘消息时才触发,超出了当前catch块的作用范围。
解决方案
有两种可行的修复方式:
移除
composedImage的using声明
手动管理Bitmap的生命周期,注意在设置新Image前释放旧的资源,避免内存泄漏:// 替换DisplayFreeRoamShape中的对应代码段 var freeRoamShape = FreeRoamShapes[0]; Bitmap composedImage = new Bitmap(pictureBoxCroppedImages.Width, pictureBoxCroppedImages.Height); using (Graphics g = Graphics.FromImage(composedImage)) { g.Clear(Color.Transparent); using (Pen pen = new Pen(freeRoamShape.Color, 2f)) { g.DrawPath(pen, freeRoamShape.Path); } } // 先释放旧的Image资源 pictureBoxCroppedImages.Image?.Dispose(); pictureBoxCroppedImages.Image = composedImage;创建Bitmap的副本
在赋值给PictureBox前,创建一个composedImage的副本,这样即使原Bitmap被using释放,副本仍能正常使用:// 在using块内修改赋值语句 pictureBoxCroppedImages.Image = new Bitmap(composedImage);同样需要注意在设置新Image前释放旧资源。
内容的提问来源于stack exchange,提问作者Daniel Lip

