C# 从PictureBox解锁图像

C# 从PictureBox解锁图像,c#,.net,.net-4.0,picturebox,C#,.net,.net 4.0,Picturebox,我目前正在开发一个应用程序来帮助扫描和显示我工作中的图像 我的应用程序是用多个表单构建的,这里最重要的表单是MymainForm,用于显示当前扫描的统计信息和具有不同功能的菜单提示。我还有一个imageviewperform,带有一个PictureBox,显示在辅助监视器上,用于查看当前扫描的图像 我正在使用计时器轮询图像扫描到的文件夹。扫描新图像并解锁图像后,我会将其绘制到FileStream中,并在PictureBox中显示,如下所示: public static void SetPictu

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

我的应用程序是用多个表单构建的,这里最重要的表单是My
mainForm
,用于显示当前扫描的统计信息和具有不同功能的菜单提示。我还有一个
imageviewperform
,带有一个
PictureBox
,显示在辅助监视器上,用于查看当前扫描的图像

我正在使用
计时器
轮询图像扫描到的文件夹。扫描新图像并解锁图像后,我会将其绘制到
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;
}

一旦加载位图,您就不再持有文件流,因此一切都应该正常工作。但是,如果您谈论的是加载发生的瞬间,并且扫描试图覆盖该文件,请始终扫描到“临时”或垃圾命名的文件(使用GUID作为名称)。扫描完成后,将该文件重命名为JPG-然后您的显示表单将拾取并正确显示该文件


这样,重新扫描只需要多次尝试用“等待”来重命名临时文件,以防止出现小范围的重叠。

您的代码对我来说很好。我拿了一份精确的副本,用同一个图像文件反复调用它

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

其他东西正在锁定该文件。您能分享您的调用代码吗?

这里的问题是,映像是从文件流加载的,该文件流被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))
        {
            ...

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

我想您现在已经完成了工作。
尽管如此,我仍在发帖,以防其他人也有同样的问题。
我也有同样的问题:我在PictureBox控件中加载了一个图像

picture.Image = new Bitmap(imagePath);  
当试图移动它时

File.Move(source, destination);  
mscorlib引发异常:
进程无法访问该文件,因为另一进程正在使用该文件

我在这里找到了一个解决方案(虽然是在VB.Net而不是C#)

文章作者克隆原始图像并将克隆的图像加载到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)
谢谢你,杰克,

对我来说,一切正常

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窗体找到解决方案时,我遇到了一篇有用的文章,其中提到如何在图片框中加载图片,而不“锁定”原始图片本身,而是它的一个实例。 因此,如果您试图删除、重命名或执行任何您想对原始文件执行的操作,您将不会收到一条错误消息的通知,其中提到“该文件正由另一个进程使用”或其他任何内容

这是对文章的补充


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

很抱歉反应太晚,我会尝试使用调用方法更新我的帖子。很难判断问题出在哪里。试着把它复制到一个文件夹中,然后发布。如果它太长,无法恰当地回答这个问题,请将它贴在上面,并给我们一个指向要点的链接。
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);
        }
    });
}