Отображение изображения из массива байтов в WPF - проблемы с памятью - PullRequest
1 голос
/ 15 июня 2010

Я разработал приложение для захвата и сохранения изображений в базе данных, но у меня проблема с использованием памяти.В моем доменном объекте у меня есть 3 свойства:

Изображение - байтовый массив, содержимое которого является jpg

RealImageThumb - байтовый массив, преобразованный в BitmapImage и сжатый, отображается пользователю в виде сетки сдругие миниатюры

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

У меня возникает проблема, еслипользователь наводит курсор на каждое изображение, в свою очередь спирали использования памяти.Я понимаю, что когда пользователь наводит указатель мыши на источники растрового изображения и память не освобождается, я попытался присвоить RealImage свойство поддержки и присвоить ему значение null после, но снова память не освобождается (ожидание мусора).коллекционер?).

edit:

Это то, что ты имел в виду, Рэй?Я не получаю ничего, что показано во всплывающей подсказке, как показано ниже, но если я пытаюсь определить WeakReference<BitmapImage>, я получаю System.WeakReference не имеет ошибки параметров типа.

  private WeakReference _realImage;
        public virtual BitmapImage RealImage
        {
            get
            {
                if (_realImage == null || _realImage.Target == null)
                {

                    if (Image == null) return null;
                    var newBitmapImage = new BitmapImage();
                    newBitmapImage.BeginInit();
                    newBitmapImage.CacheOption = BitmapCacheOption.None;
                    newBitmapImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
                    newBitmapImage.StreamSource = new MemoryStream(Image);
                    newBitmapImage.EndInit();
                    _realImage = new WeakReference(newBitmapImage);
                }

                return (BitmapImage)_realImage.Target;
            }
        }

1 Ответ

3 голосов
/ 16 июня 2010

Вам нужно будет сделать три вещи:

  1. При создании BitmapImage используйте StreamSource для предоставления данных. Не используйте UriSource и не передавайте Uri в конструктор, что приведет к добавлению изображения в кэш изображений.

  2. В реализации RealImage вашего доменного объекта сохраните WeakReference для вашего BitmapImage, а не для самого BitmapImage. При получении RealImage, если WeakReference или WeakReference.Target имеет значение null, создайте новый BitmapImage и новую WeakReference.

  3. Используйте DataTrigger с переключением шаблонов, чтобы включать ваш элемент управления Image в визуальное дерево только тогда, когда он видим

Вот шаблоны, необходимые для шага 3, включая шаблон с DataTrigger:

<DataTemplate x:Key="EmptyTemplate">
</DataTemplate>

<DataTemplate x:Key="RealImageTemplate">
  <Image Source="{Binding RealImage.Target}" Width="300" Height="300" />
</DataTemplate>

<DataTemplate x:Key="RealImageWhenVisible">

  <!-- Use EmptyTemplate when I am not visible -->
  <ContentPresenter x:Name="Presenter"
                    Content="{Binding}"
                    ContentTemplate="{StaticResource EmptyTemplate}"/>

  <!-- Switch to RealImageTemplate when I am visible -->
  <DataTemplate.Triggers>
    <DataTrigger Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}"
                 Value="True">
      <Setter TargetName="Presenter"
              Property="ContentPresenter.ContentTemplate"
              Value="{StaticResource RealImageTemplate}" />
    </DataTrigger>
  </DataTemplate.Triggers>
</DataTemplate>

Теперь вы можете определить всплывающую подсказку следующим образом:

<Rectangle Width="40" Height="40" Fill="Blue">
  <Rectangle.ToolTip>
    <ContentPresenter Content="{Binding}"
                      ContentTemplate="{StaticResource RealImageWhenVisible}" />
  </Rectangle.ToolTip>
</Rectangle>

Как это работает: два ContentPresenters находятся внутри друг друга:

  • Когда внешний ContentPresenter невидим, внутренний ContentPresenter будет иметь EmptyTemplate, поэтому изображение не будет загружено.
  • Когда внешний ContentPresenter виден, внутренний ContentPresenter будет иметь RealImageTemplate, поэтому изображение будет загружено и отображено.

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

Обновление

Код, который вы опубликовали для RealImage, должен работать, и это почти то, о чем я думал. Сегодня утром я понял, что на самом деле нет необходимости устанавливать BitmapCacheOption или BitmapCreateOption, если не указан SourceUri. Я обновил свой ответ, чтобы отразить это, а также прояснить проблему WeakReference. Я также исправил ошибку в шаблоне: я привязывался к «RealImage», когда должен был привязываться к «RealImage.Target».

...