从PictureBox解锁图像

我目前正在开发一个应用程序,以帮助在我的工作中扫描和显示图像。

我的应用程序是使用多种forms构建的,这里最重要的forms是我的mainForm用于显示当前扫描的统计信息和具有不同function的menustrip。 我还有带有PictureBox ImageViewerForm ,它在辅助监视器上显示以查看当前扫描的图像。

我正在使用Timer来轮询扫描图像的文件夹。 扫描新图像并解锁图像后,我会将其绘制到FileStream并在PictureBox显示,如下所示:

 public static void SetPicture(string filename, PictureBox pb) { try { Image currentImage; //currentImage = ImageFast.FromFile(filename); using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height)); if (pb.InvokeRequired) { pb.Invoke(new MethodInvoker( delegate() { pb.Image = currentImage; })); } else { pb.Image = currentImage; } } } catch (Exception imageEx) { throw new ExceptionHandler("Error when showing image", imageEx); } } public static Image ScaleImage(Image imgToResize, Size size) { int sourceWidth = imgToResize.Width; int sourceHeight = imgToResize.Height; float nPercent = 0; float nPercentW = 0; float nPercentH = 0; nPercentW = ((float)size.Width / (float)sourceWidth); nPercentH = ((float)size.Height / (float)sourceHeight); if (nPercentH < nPercentW) nPercent = nPercentH; else nPercent = nPercentW; int destWidth = (int)(sourceWidth * nPercent); int destHeight = (int)(sourceHeight * nPercent); Bitmap b = new Bitmap(destWidth, destHeight); using (Graphics g = Graphics.FromImage(b)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(imgToResize, 0, 0, destWidth, destHeight); } return b; } 

这样, PictureBox显示的图像不应该被锁定,但它是。 问题是扫描的图像可能必须重新扫描,如果我这样做,我会在尝试从扫描软件覆盖图像文件时收到共享冲突错误。

任何能够回答我能做什么的人?

感谢@SPFiredrake我有一个解决方案来创建一个临时文件显示在PictureBox中,保持原始图像解锁。

 public static void SetPicture(string filename, PictureBox pb) { try { Image currentImage; //currentImage = ImageFast.FromFile(filename); using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height)); if (pb.InvokeRequired) { pb.Invoke(new MethodInvoker( delegate() { pb.Image = currentImage; })); } else { pb.Image = currentImage; } } } catch (Exception imageEx) { throw new ExceptionHandler("Error when showing image", imageEx); } } public static string CreateTempFile(string fileName) { if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException("fileName"); if (!File.Exists(fileName)) throw new ArgumentException("Specified file must exist!", "fileName"); string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName)); File.Copy(fileName, tempFile); Log.New("Temp file created: " + tempFile); return tempFile; } 

这里的问题是图像是从FileStream加载的,它被PictureBox锁定,因为它持有对流的引用。 你应该做的是首先将图片加载到本地内存(通过byte []数组),然后从MemoryStream加载图像。 在您的SetPicture方法中,您应该尝试以下更改并查看它是否有效:

 public static void SetPicture(string filename, PictureBox pb) { try { Image currentImage; byte[] imageBytes = File.ReadAllBytes(filename); using(MemoryStream msImage = new MemoryStream(imageBytes)) { currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height)); .... } 

编辑:在聊天中进行对话后,使用您最终使用的修复程序进行更新:

 public static void SetPicture(string filename, PictureBox pb) { try { Image currentImage; string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename)); File.Copy(filename, tempFile); //currentImage = ImageFast.FromFile(filename); using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { ... 

这样,您使用临时文件实际加载图片框,保持原始文件不变(在初始副本之外)。

一旦加载了Bitmap,你就不再保留文件流,所以一切都应该有效。 但是,如果您正在谈论正在进行加载并且扫描尝试覆盖该文件的瞬间 – 请始终扫描到“临时”或垃圾命名文件(使用GUID作为名称)。 扫描完成后,将该文件重命名为JPG – 然后您的显示forms将正确显示并显示。

这样,重新扫描将仅涉及尝试使用“等待”多次重命名临时文件以防止该重叠的小区域。

你的代码对我来说很好。 我拿了一个精确的副本并用相同的图像文件重复调用它。

 SetPicture(@"c:\temp\logo.png", pictureBox1); 

其他东西锁定文件。 你能分享你的通话代码吗?

我想你现在已经完成了你的工作。
不过,我发帖是为了防止其他人遇到同样的问题。
我遇到了同样的问题:我在PictureBox控件中加载了一个图像

 picture.Image = new Bitmap(imagePath); 

当试图移动它

 File.Move(source, destination); 

mscorlib引发exception:
该进程无法访问该文件,因为它正在被另一个进程使用

我找到了一个解决方案(虽然在VB.Net而不是C#)这里PictureBox“锁定”文件,无法移动/删除

post的作者克隆原始图像并将克隆的图像加载到PictureBox控件。
我轻微改变了代码并想出了这个:

 private Bitmap CloneImage(string aImagePath) { // create original image Image originalImage = new Bitmap(aImagePath); // create an empty clone of the same size of original Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height); // get the object representing clone's currently empty drawing surface Graphics g = Graphics.FromImage(clone); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed; // copy the original image onto this surface g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height); // free graphics and original image g.Dispose(); originalImage.Dispose(); return clone; } 

所以,我们的代码将是:

 picture.Image = (Image)CloneImage(imagePath); 

这样做,移动文件时我没有例外。
我认为这是一种很好的替代方法,而且您不需要临时文件。

这是Jack代码,但在Visual Basic .NET中,转换进入函数内部

  Private Function CloneImage(aImagePath As String) As Image ' create original image Dim originalImage As Image = New Bitmap(aImagePath) ' create an empty clone of the same size of original Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height) ' get the object representing clone's currently empty drawing surface Dim g As Graphics = Graphics.FromImage(clone) g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed ' copy the original image onto this surface g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height) ' free graphics and original image g.Dispose() originalImage.Dispose() Return CType(clone, Image) End Function 

所以打电话会

 picture.Image = CloneImage(imagePath) 

谢谢杰克,

从MS回答这个问题……

对我来说工作好吧…

 internal void UpdateLastImageDownloaded(string fullfilename) { this.BeginInvoke((MethodInvoker)delegate() { try { //pictureBoxImage.Image = Image.FromFile(fullfilename); //Bitmap bmp = new Bitmap(fullfilename); //pictureBoxImage.Image = bmp; System.IO.FileStream fs; // Specify a valid picture file path on your computer. fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read); pictureBoxImage.Image = System.Drawing.Image.FromStream(fs); fs.Close(); } catch (Exception exc) { Logging.Log.WriteException(exc); } }); } 

在试图找出我的C#Windows窗体的解决方案时,我遇到了一篇有用的文章,提到了如何加载图片框中的图片,而没有“锁定”原始图片本身,而是一个实例。 因此,如果您尝试删除,重命名或对原始文件执行任何操作,则不会通过提示“其他进程正在使用的文件”或其他任何内容的错误消息通知您!

这是对该文章的参考 。

总结我相信这个解决方案在处理大量图片时非常有用,因为大量使用这种方法可能会导致内存不足。