Удалить изображение, привязанное к элементу управления

Я пишу приложение Image Manager WPF. У меня есть ListBox со следующим ItemsTemplate:

        <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>

Обратите внимание, что элемент управления «Изображение» привязан к свойству «Полное имя», которое представляет собой строку, представляющую абсолютный путь к файлу JPG.

Некоторые функции приложения требуют, чтобы я изменил файл JPG (переместил, переименовал или удалил). Когда я пытаюсь это сделать (в настоящее время пытаюсь переместить файл), я получаю исключение IOException: «Процесс не может получить доступ к файлу, поскольку он используется другим процессом». Процесс, блокирующий файл, — это мое приложение WPF.

Я провел поиск в Интернете и нашел несколько сообщений, указывающих на то, что у изображений, в частности, есть проблемы с освобождением своих ресурсов. Я пробовал следующее:

  1. Установка для ListBox.Source значения null
  2. Добавление 10-секундного времени ожидания перед попыткой перемещения.
  3. Выдача GC.Collect().
  4. Перенос операции в другой поток.

Что еще я могу попробовать? Я думал найти ссылку на объект Image в ItemsTemplate и попытаться избавиться от изображения, но не могу понять, как получить ссылку.

Одно из возможных решений, о котором я читал, состояло в том, чтобы создавать копии изображений, а не фактические изображения, но, поскольку привязка относится к имени файла, а не к фактическому изображению, я не знаю, смогу ли я это сделать.

Любая помощь или предложения будут высоко оценены.


person Joel Cochran    schedule 27.03.2009    source источник


Ответы (4)


Мое приложение Intuipic также позволяет пользователям удалять изображения. Мне пришлось написать этот преобразователь, чтобы добиться этого. Соответствующий код:

//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();
person Kent Boogaart    schedule 27.03.2009
comment
Я пробовал это (без DecodePixelWidth/Height), но файл все равно заблокирован. Должно быть что-то еще происходит - мне придется начать искать другие проблемы. Будет ли объект FileInfo создавать блокировку? - person Joel Cochran; 27.03.2009
comment
Я думаю, вам придется устранять возможные причины одну за другой. - person Kent Boogaart; 27.03.2009
comment
Я исчерпал все, что смог найти. Далее я перестрою приложение, шаг за шагом проверяя виновника. - person Joel Cochran; 06.05.2009

Я отметил ответ Кента как ответ, и я бы также отметил ответ Бендеви, потому что я использовал их обоих в окончательном решении.

Файл был определенно заблокирован, потому что связывалось только имя файла, поэтому элемент управления Image открывал фактический файл для создания изображения.

Чтобы решить эту проблему, я создал преобразователь значений, как предложил Бендеви, а затем я использовал (большую часть) кодовую форму, предложенную Кентом, чтобы вернуть новый 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. 3) Всплывающее окно DeleteImage, которое было связано с помощью конвертера.

Выяснилось, что проблема была в №2. Привязываясь к SelectedItem, я ошибочно предположил, что привязываюсь к вновь визуализируемому изображению (на основе преобразователя). На самом деле объект SelectedItem фактически был именем файла. Это означало, что основное изображение снова создавалось путем прямого доступа к файлу.

Таким образом, решение состояло в том, чтобы привязать основной элемент управления Image к свойству SelectedItem И использовать Converter.

person Joel Cochran    schedule 04.08.2009

Проверьте этот пост здесь.

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.");
  }
}
person bendewey    schedule 27.03.2009
comment
Я пробовал это, но все еще не могу переместить файл. Я также добавил использование() вокруг растрового изображения, думая, что, возможно, оно еще не удалено, но результаты те же. - person Joel Cochran; 27.03.2009
comment
Это кажется странным. Можно ли переместить или удалить файл, когда приложение не запущено? Возможно ли, что файл все еще заблокирован из какого-то предыдущего экземпляра? - person bendewey; 27.03.2009

Это очень старо, но структура изменилась, и решение для этого намного проще, по крайней мере, в .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>
person Travis    schedule 09.09.2020