删除绑定到控件的图像

时间:2009-03-27 15:12:33

标签: c# wpf file-io

我正在编写一个Image Manager WPF应用程序。我有一个带有以下ItemsTemplate的ListBox:

        <Grid x:Name="grid" Width="150" Height="150" Background="{x:Null}">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="27.45"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"/>
            </Grid.ColumnDefinitions>
            <Border Margin="5,5,5,5.745" Grid.RowSpan="2" Background="#FF828282" BorderBrush="{DynamicResource ListBorder}" CornerRadius="5,5,5,5" BorderThickness="1,1,2,2" x:Name="border">
                <Grid>
                    <Viewbox Margin="0,0,0,21.705">
                        <Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName}" />
                    </Viewbox>
                    <TextBlock Height="Auto" Text="{Binding Path=Name}" TextWrapping="Wrap" x:Name="PictureText" HorizontalAlignment="Left" Margin="70,0,0,0" VerticalAlignment="Bottom" />
                </Grid>
            </Border>
        </Grid>

请注意,“Image”控件绑定到“FullName”属性,该属性是表示JPG绝对路径的字符串。

多个应用程序功能要求我更改JPG文件(移动,重命名或删除)。当我尝试这样做(当前尝试移动文件)时,我收到一个IOException:“进程无法访问该文件,因为它正被另一个进程使用。”锁定文件的过程是我的WPF应用程序。

我做了一些在线搜索,发现了一些帖子,表明图片特别是放下他们的资源有困难。我尝试过以下方法:

  1. 将ListBox.Source设置为null
  2. 之前添加10秒的等待时间 试图搬家。
  3. 发布GC.Collect()。
  4. 将操作移至其他位置 线程。
  5. 我还能尝试什么?我想在ItemsTemplate中找到对Image对象的引用并尝试处理Image,但我无法弄清楚如何获取引用。

    我读到的一个可能的解决方案是创建图像的副本而不是实际的图像,但由于绑定是文件名而不是实际的图像我不知道我是否可以使这个工作。

    非常感谢任何帮助或建议。

4 个答案:

答案 0 :(得分:6)

我的Intuipic应用程序也允许用户删除图像。我不得不写this converter来实现它。相关代码:

//create new stream and create bitmap frame
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read);
bitmapImage.DecodePixelWidth = (int) _decodePixelWidth;
bitmapImage.DecodePixelHeight = (int) _decodePixelHeight;
//load the image now so we can immediately dispose of the stream
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();

//clean up the stream to avoid file access exceptions when attempting to delete images
bitmapImage.StreamSource.Dispose();

答案 1 :(得分:4)

我将Kent的回答标记为答案,我也会标记bendewey,因为我在最终解决方案中使用了它们。

文件肯定是锁定的,因为文件名是所有被绑定的,因此Image控件打开了实际文件以生成图像。

为了解决这个问题,我创建了一个像bendewey建议的值转换器,然后我使用(大部分)代码形式Kent的建议来返回一个新的BitmapImage:

    [ValueConversion(typeof(string), typeof(BitmapImage))]
public class PathToBitmapImage : IValueConverter
{
    public static BitmapImage ConvertToImage(string path)
    {
        if (!File.Exists(path))
            return null;

        BitmapImage bitmapImage = null;
        try
        {
            bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read);
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.StreamSource.Dispose();
        }
        catch (IOException ioex)
        {
        }
        return bitmapImage;
    }

    #region IValueConverter Members

    public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || !(value is string))
            return null;

        var path = value as string;

        return ConvertToImage(path);
    }

    public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

然而,正如上面的评论所示,这并没有解决问题。我已经离开了其他项目,最近又回到了这个项目,重新振作起来寻找解决方案。

我创建了另一个仅测试此代码的项目,当然它也有效。这告诉我原计划中有更多不妥。

长话短说,图像是在三个地方生成的,我认为已经解决了这个问题:

1)ImageList,现在使用Converter绑定。 2)绑定到ImageList SelectedItem属性的主Image。 3)DeleteImage弹出窗口,使用Converter绑定。

事实证明问题出在#2。通过绑定到SelectedItem,我错误地认为我绑定到新渲染的Image(基于Converter)。实际上,SelectedItem对象实际上是文件名。这意味着通过直接访问文件再次构建主Image。

因此,解决方案是将主Image控件绑定到SelectedItem属性并使用Converter。

答案 2 :(得分:2)

在这里查看这篇文章。

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1/

<强>示例

基本上你必须使用流预加载图像。我会创建一个PreLoadImageConverter,就像这样,我还没有测试过它。

<Grid>
  <Grid.Resources>
    <local:PreLoadImageConverter x:Key="imageLoadingConverter" />
  </Grid.Resources>
  <Image Width="Auto" Height="Auto" x:Name="picture" Source="{Binding Path=FullName, Converter={StaticResource imageLoadingConverter}}" />
</Grid>

<强> PreLoadImageConverter.cs

public class PreLoadImageConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value == null) return null;
    string imagePath = value.ToString();

    ImageSource imageSource;
    using (var stream = new MemoryStream())
    {
      Bitmap bitmap = new Bitmap(imagePath);
      bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);   
      PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
      imageSource = bitmapDecoder.Frames[0];
      imageSource.Freeze();
    }
    return imageSource;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new Exception("The method or operation is not implemented.");
  }
}

答案 3 :(得分:0)

这很老了,但是框架已经改变,并且解决起来要容易得多,至少在.NET Core中是如此。

根据我的收集,BitmapImage.UriSource以前不是可绑定的。就是现在。只需在xaml中明确指定图像源即可。绑定您的UriSource并将缓存模式设置为OnLoad。做完了无需转换器。

<Image Grid.Row="1">
    <Image.Source>
        <!-- specify the source explicitly and set CacheOption to OnLoad to avoid file locking-->
        <BitmapImage UriSource="{Binding ImagePath}" CacheOption="OnLoad" />
    </Image.Source>
</Image>