从本地目录中删除位图

时间:2012-09-14 15:08:57

标签: c# wpf bitmap

好吧,它已经到了这个地步。我在其他许多网站上搜索了这个网站,没有人能给我一个直接的答案,所以我会试着直接问。在这个问题已经持续了3天,我不能再浪费时间了。

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

到目前为止:起初我尝试了非常优雅的解决方案,但是就像所有事情一样,你只是想知道你是否可以让它完全工作。所以我正处于我使用的大部分代码都是实验性和激进的地步。所以请在查看它时注意所使用的代码是绝望的,所以任何“简单”的解决方案可能已经尝试过(我做了很多研究,因为我讨厌不得不这样做)。我能想到的最重要的事情就是大量的处置和强制垃圾收集,所以请不要评论这种做法的消极性,我很清楚:)。

守则

将图像保存到本地目录

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 ++环境中编程,所以我倾向于管理我自己的内存,这整个“垃圾收集”的事情真的在我们的项目中挥了挥手! (非主题说明:我不知道为什么我没有得到任何颜色突出显示,所以我向任何花时间阅读此内容的人道歉。)

2 个答案:

答案 0 :(得分:0)

由于您的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.

答案 1 :(得分:0)

这是一种简单的方法来做你想要的。在这个例子中,我使用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();