Попытка изменить SourceRect CroppedBitmap во время выполнения

StackOverflow https://stackoverflow.com/questions/1028519

  •  06-07-2019
  •  | 
  •  

Вопрос

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

Я пытаюсь сделать спрайтовую анимацию.У меня есть BitmapSource, который содержит таблицу спрайтов, которая представляет собой одно растровое изображение, содержащее сетку различных поз для спрайта.Затем у меня есть CroppedBitmap, источником которого является таблица спрайтов, и SourceRect, который извлекает одну из поз из таблицы спрайтов.Во время выполнения, когда я хочу анимировать, я пытаюсь изменить свойство SourceRect CroppedBitmap, чтобы получить другую позу из большего растрового изображения;но, как отмечалось выше, новое значение свойства просто не приживается.Это самая странная вещь.

Вот пример XAML:

<UserControl.Resources>
    <BitmapImage x:Key="spritesheet" UriSource="Sprites/elf.png"/>
</UserControl.Resources>
<Image>
    <Image.Source>
        <CroppedBitmap x:Name="image" Source="{StaticResource spritesheet}"
                       SourceRect="240 640 240 320"/>
    </Image.Source>
</Image>

И выделенный код пытается сделать это:

var newRect = new Int32Rect(...);
Debug.WriteLine("             Before: " + image.SourceRect);
Debug.WriteLine("Assigning new value: " + newRect);
image.SourceRect = newRect;
Debug.WriteLine("              After: " + image.SourceRect);

Это дает мне этот отладочный вывод:

             Before: 240,640,240,320
Assigning new value: 240,0,240,320
              After: 240,640,240,320

Таким образом, на самом деле свойству присваивается новый прямоугольник (с Y=0);нет никаких исключений;но после этого значение свойства просто не изменилось (Y по-прежнему равно 640).

Есть идеи, почему это происходит и как это исправить?

Это было полезно?

Решение

Я в конце концов нашел ответ. Из документации по CroppedBitmap :

  

CroppedBitmap реализует интерфейс ISupportInitialize для оптимизации инициализации нескольких свойств. Изменения свойств могут происходить только во время инициализации объекта. Вызовите BeginInit, чтобы сообщить, что инициализация началась, и EndInit, чтобы сообщить, что инициализация завершена. После инициализации изменения свойств игнорируются. (выделено мной)

Просто для забавы я попытался добавить вызовы BeginInit () .. EndInit () в мой метод, чтобы посмотреть, изменит ли это его. Неудивительно, что я получил исключение InvalidOperationException (" Невозможно установить состояние инициализации более одного раза).

Таким образом, CroppedBitmap эффективно неизменяем. (но они игнорировали свою собственную систему Freezable, которая вызвала бы исключение, чтобы сказать мне, что я делал что-то не так, и внедрила что-то более вместо этого .)

Это означает, что запрещается изменять свойство SourceRect. Мне нужно создать отдельный экземпляр CroppedBitmap для каждого подизображения в таблице спрайтов.

Другие советы

Вот альтернативный способ справиться с этим:
Вместо использования CroppedBitmap используйте полное исходное изображение, но:

<Ол>
  • Установите image.RenderTransform для настройки видимой области.
  • При необходимости установите Image.Clip , чтобы не отображать ненужные части изображения.
  • Это означает, что вам не нужно создавать новые CroppedBitmaps , вы можете просто настроить преобразование.
    В моем тестировании я не видел разницы в скорости, делающей это в любом случае.

    Для полноты, вот как я могу настроить ваш код, чтобы делать то, что я предлагаю:

    <Image RenderTransform="1, 0, 0, 1, -240, -640">
      <!-- Still include your Image.Source here, just not as a CroppedBitmap -->
      <Image.Clip>
        <RectangleGeometry Rect="0, 0, 240, 320" />
      </Image.Clip>
    </Image>
    

    Тогда более поздний вызов для эквивалента настройки SourceRect :

    image.RenderTransform = new MatrixTransform(1d, 0d, 0d, 1d, -240d, 0d);
    

    Вот способ использования IMultiValueConverter:

    <Image>
        <Image.Source>
            <MultiBinding Converter="{x:Static local:SourceAndRectToCroppedBitmapConverter.Default}">
                <Binding Path="FileName" />
                <Binding Path="CropRect" />
            </MultiBinding>
        </Image.Source>
    

    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    
    public class SourceAndRectToCroppedBitmapConverter : IMultiValueConverter
    {
        public static readonly SourceAndRectToCroppedBitmapConverter Default = new SourceAndRectToCroppedBitmapConverter();
    
        private static readonly ImageSourceConverter Converter = new ImageSourceConverter();
    
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values[0] is string text)
            {
                return new CroppedBitmap((BitmapSource)Converter.ConvertFrom(values[0]), (Int32Rect)values[1]);
            }
    
            return new CroppedBitmap((BitmapSource)values[0], (Int32Rect)values[1]);
        }
    
        object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    Потенциально плохая производительность.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top