簡體   English   中英

從PictureBox解鎖圖像

[英]Unlocking image from PictureBox

我目前正在開發一個應用程序,以幫助在我的工作中掃描和顯示圖像。

我的應用程序是用多種形式構建的,這里最重要的形式是我的mainForm用於顯示有關當前掃描和具有不同功能的菜單條的統計信息。 我也有一個帶有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))
        {
            ...

這樣,您將使用臨時文件實際加載圖片框,而保持原始文件不變(位於原始副本之外)。

加載位圖后,您將不再保持文件流,因此一切正常。 但是,如果您正在談論加載發生的瞬間,並且掃描嘗試覆蓋該文件,請始終掃描到“臨時”或垃圾名稱的文件(使用GUID作為名稱)。 掃描完成后,將該文件重命名為JPG-然后您的顯示表單將被拾取並正確顯示。

這樣,重新掃描將只涉及嘗試使用“等待”多次重命名臨時文件,以防止重疊的很小區域。

您的代碼對我來說很好。 我得到了一個精確的副本,並使用相同的圖像文件反復調用它。

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

其他原因正在鎖定文件。 可以共享您的呼叫代碼嗎?

我想您現在的工作已經完成。
不過,如果有人遇到相同問題,我仍會發帖。
我遇到了同樣的問題:我將圖片加載到PictureBox控件中

picture.Image = new Bitmap(imagePath);  

並嘗試移動它時

File.Move(source, destination);  

mscorlib引發異常:
該進程無法訪問該文件,因為它正在被另一個進程使用

我在這里找到了一個解決方案(盡管在VB.Net中而不是C#中),但是PictureBox“鎖定”文件,無法移動/刪除

該文章的作者將克隆原始圖像,並將克隆的圖像加載到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);  

這樣做,在移動文件時我沒有更多例外。
我認為這是一種很好的替代方法,您不需要臨時文件。

這是傑克代碼,但在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窗體找到解決方案時,我遇到了一篇很有幫助的文章,其中提到了如何在圖片框內加載圖片而不“鎖定”原始圖片本身,而是“鎖定”它的一個實例。 因此,如果您嘗試刪除,重命名或對原始文件執行任何操作,則不會收到錯誤消息“其他進程正在使用的文件”或其他任何內容來通知您!

這是對本文的引用

總結一下我認為,用圖片的少量的處理時,由於較大的數字應用這種方法可能會導致內存不足,這個解決方案是非常有用的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM