【问题标题】:Deleting a bitmap from local directory从本地目录中删除位图
【发布时间】:2012-09-14 15:08:56
【问题描述】:

好的,到此为止。我在许多其他网站中搜索了这个网站,似乎没有人可以给我一个直接的答案,所以我将尝试直接询问。在这个问题上呆了大约 3 天,我不能再浪费时间了。

目标:我正在构建的应用程序是在 WPF 中,将用作我的设计团队和我将很快开展的项目的错误跟踪器。由于我们将使用 C++ 构建游戏,因此发生的大多数错误都将具有视觉元素,因此我包含了在用户将错误添加到列表时提供相关错误图像的功能。然后我拍摄该图像并将其保存到本地目录(用于测试)。现在,Error 对象中的图像路径指向了通向本地目录的路径。此功能已经过测试并且工作正常。当我想从列表中删除一个错误时,我的问题就出现了。我收到了那个非常臭名昭著的“IO Exception”,说我要删除的图像正在被另一个进程使用。

到目前为止: 起初我尝试了非常优雅的解决方案,但与所有事情一样,你只是想看看你是否可以让事情完全发挥作用。所以我现在使用的大部分代码都是实验性的和激进的。因此,请在查看时注意所使用的代码是出于绝望,因此可能已经尝试过任何“简单”的解决方案(我确实对此进行了很多研究,因为我讨厌这样做)。我能想到的事情是荒谬的处置和强制垃圾收集被调用,所以请不要评论这种做法的负面性质,我很清楚:)。

守则

保存图片到本地目录

public void OnBrowseClick()
    {
        Microsoft.Win32.OpenFileDialog openBox = new Microsoft.Win32.OpenFileDialog();

        // Show dialog box to user and grab output
        Nullable<bool> result = openBox.ShowDialog();

        if (result == true)
        {
            // Create temp variable to hold local path string
            string localPath = Directory.GetCurrentDirectory();      

            // Grab the extension of the specified file path
            string extension = openBox.FileName.Substring(openBox.FileName.LastIndexOf("\\"));

            // Add extension to local path
            localPath += extension;

            // Create local copy of image at given file path (being ridiculous at this point)
            using (Stream stream = new FileStream(openBox.FileName, FileMode.Open, FileAccess.ReadWrite))
            {
                using (Bitmap bmp = LoadImage(stream))
                {
                    using (Bitmap temp = (Bitmap)bmp.Clone())
                    {
                        temp.Save(localPath);
                        temp.Dispose();
                    }

                    bmp.Dispose();
                }

                stream.Dispose();
            }

            // Set the URL in the image text box (UI stuff)
            LocalError.ImagePath = localPath;   
        }
    }

下面是上面函数中用到的LoadImage函数

private Bitmap LoadImage(Stream stream)
    {
        Bitmap retval = null;

        using (Bitmap bitmap = new Bitmap(stream))
        {
            retval = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);

            using (Graphics gdi = Graphics.FromImage(retval))
            {
                gdi.DrawImageUnscaled(bitmap, 0, 0); 
                gdi.Flush();
                gdi.Dispose();
                bitmap.Dispose();
            }
        }

        // Garbage collection here to be safe
        GC.WaitForPendingFinalizers();
        GC.Collect();

        return retval;
    } 

最后我们来到我尝试删除图像的地方

public void OnDeleteClick()
    {
        // Ask user to make sure they want to delete selected item(s)
        MessageBoxResult result = MessageBox.Show("Are you sure you want to delete selected item(s) from the list?",
                                "Delete", MessageBoxButton.YesNo);

        if (result == MessageBoxResult.Yes)
        {
            for( int i = 0; i < Parent.ErrorListControl.ErrorDataGrid.SelectedItems.Count; ++i)
            {
                // Get path to image
                string path = (Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i] as Error).ImagePath;

                // Even tried calling garbage collection here!!!!!
                System.GC.WaitForPendingFinalizers();
                System.GC.Collect();
                File.Delete(path);

                // Remove the error from the error list
                Parent.ErrorListVM.ErrorList.Remove((Error)Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i]);

                // Decrement counter because we altered the list while in a loop
                i--;
            }
        }
    }

注意事项:如果有人希望我进一步解释任何事情,或者如果您需要知道我遗漏的内容,请询问我会尽快回复您!在这一点上,任何建议都是有帮助的,我绝对不知道我做错了什么。我通常只在 C++ 环境中编程,所以我倾向于管理我自己的内存,这整个“垃圾收集”的事情真的给我们的项目带来了麻烦! (题外话:我不知道为什么我没有得到任何颜色突出显示,所以我向任何花时间阅读本文的人道歉)。

【问题讨论】:

  • 能否说明原图的出处?是您的应用程序创建它,还是用户通过单击“浏览”按钮提供他们选择的任何图像? Delete函数,是不是应该删除用户使用OpenFileDialog指向的原始图像?
  • 您好,感谢您抽出宝贵时间帮助我。
  • 对不起,按回车键 原始图像是由用户通过浏览按钮选择的。我所做的只是将其加载并存储在其他地方(本地文件夹,bin -> 调试) 目的是删除图像的本地副本,而不是原始图像。这只会删除保存到我项目中“Bin->Debug”文件夹中的图像(仅使用此目录进行测试)。
  • 难道不能只使用 File.Copy 将文件复制到本地存储吗?
  • 可能还值得一提的是,我正在使用 MVVM 设计模式——有点失落。

标签: c# wpf bitmap


【解决方案1】:

这里有一个简单的方法来做你想做的事。在此示例中,我使用 Path.GetTempFileName() 在本地用户的临时目录中生成随机文件名。如果您不需要保留文件,那么这是临时存储它们的好地方。此外,理论上用户可以导入两个同名文件。所以你想使用某种随机文件名生成或其他机制来避免冲突。

private void browseButton_Click(object sender, RoutedEventArgs e)
{
    var openFileDialog = new Microsoft.Win32.OpenFileDialog();

    if (openFileDialog.ShowDialog(this) == true)
    {
        using (Bitmap originalImage = new Bitmap(openFileDialog.FileName))
        {
            string tempFileName = System.IO.Path.GetTempFileName();

            originalImage.Save(tempFileName);

            // LocalError.LocalPath
            LocalPath = tempFileName;
        }
    }
}

private void deleteButton_Click(object sender, RoutedEventArgs e)
{
    if (File.Exists(LocalPath))
    {
        File.Delete(LocalPath);
    }
}

尽管只要您有正确的路径,一个简单的 File.Copy 就足够了,但我只是提供了一个与您的问题相匹配的解决方案。

编辑: 实际上,OpenFileDialog 似乎没有更改当前目录。我可以发誓它在某个时候做到了。所以我不认为这是你的问题。无论如何,这段代码仍然适用于我,你不应该需要比这更复杂的东西。

编辑#2: 似乎锁定实际上是由图像数据绑定到视图并可能由 BitmapSource 锁定引起的。您应该能够在不锁定文件的情况下创建它。一般来说,这比较慢,所以除非您需要能够修改或删除文件,否则不要这样做。

bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.CacheOption = BitmapCacheOption.OnLoad;
bitmapSource.CreateOption = BitmapCreateOptions.IgnoreImageCache;
bitmapSource.UriSource = new Uri(ImagePath, UriKind.Absolute);
bitmapSource.EndInit();

【讨论】:

  • 我确实需要保留图像,但现在我只想让这个功能正常工作。我会尝试您的解决方案并回复您。
  • 编辑了我的帖子。经过测试,似乎 OpenFileDialog 不会更改当前目录。我认为它在某个时候做到了。
  • 您是否在其他任何地方访问该图像,例如在 WPF 视图中显示它?
  • 我确实尝试了您的示例,但遇到了同样的错误;莫哲刚才说的,是我没有考虑到的。是的,我将它显示在扩展器内。控件为,绑定到当前选中的item图片文件路径。
  • 创建 BitmapSource 的代码就是锁定文件的原因。您可以以不锁定文件的方式创建它。参考stackoverflow.com/questions/1688545/…
【解决方案2】:

既然您的LoadImage 方法可以简单地复制图像,为什么不使用File.Copy(source, dest) 并避免使用所有位图、绘图等?您的目标可能是在创建本地位图后对其进行修改,但在复制后仍然可以完成。

此外,当使用 using 块时,不需要显式的 .Dispose(),因为 using 块会为您执行此操作:

using (var obj = new SomeDisposableObject())
{
    // code here
    // obj.Dispose(); <-- not needed, since...
} // ...at this point obj.Dispose is called automatically.

【讨论】:

  • 您好,不过,谢谢您的帮助;如上所述,我知道需要“不”调用 Dispose()。只是在这一点上尝试任何事情。我还在上面的评论中提到 File.copy() 确实正确保存了图像,但是;当我尝试删除它时仍然抛出相同的错误!
  • 如果文件被锁定,这个 SO 问题可能会有所帮助:image-file-locked-after-loading-in-wpf
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-24
  • 2017-06-24
  • 1970-01-01
  • 2021-02-15
  • 2019-11-26
相关资源
最近更新 更多