Pregunta

Cuando intento cambiar la propiedad SourceRect de CroppedBitmap en tiempo de ejecución, no sucede nada. No hay error, y el valor de la propiedad en realidad no cambia.

Estoy tratando de hacer animación de sprites. Tengo un BitmapSource que contiene una hoja de sprites, que es un mapa de bits único que contiene una cuadrícula de diferentes poses para el sprite. Luego tengo un CroppedBitmap que tiene la hoja de sprites como su Fuente, y un SourceRect que saca una de las poses de la hoja de sprites. En tiempo de ejecución, cuando quiero animar, estoy tratando de cambiar la propiedad SourceRect de CroppedBitmap, para sacar una pose diferente del mapa de bits más grande; pero, como se señaló anteriormente, el nuevo valor de la propiedad simplemente no se mantiene. Es lo más raro.

Aquí hay algunos ejemplos de 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>

Y el código subyacente intenta hacer esto:

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

Eso me da esta salida de depuración:

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

Entonces, en realidad está asignando el nuevo rectángulo (con Y = 0) a la propiedad; no hay excepción; pero después, el valor de la propiedad simplemente no cambió (Y sigue siendo 640).

¿Alguna idea sobre por qué sucede esto y cómo solucionarlo?

¿Fue útil?

Solución

Finalmente encontré la respuesta. De la documentación para CroppedBitmap :

  

CroppedBitmap implementa la interfaz ISupportInitialize para optimizar la inicialización en múltiples propiedades. Los cambios de propiedad solo pueden ocurrir durante la inicialización del objeto. Llame a BeginInit para indicar que la inicialización ha comenzado y EndInit para indicar que la inicialización se ha completado. Después de la inicialización, los cambios de propiedad se ignoran. (énfasis mío)

Por diversión, intenté agregar llamadas BeginInit () .. EndInit () en mi método, para ver si eso lo haría modificable. No es sorprendente que haya recibido una InvalidOperationException (" No se puede establecer el estado de inicialización más de una vez ").

Entonces CroppedBitmap es efectivamente inmutable. (Pero ignoraron su propio sistema Freezable, que habría arrojado una excepción para decirme que estaba haciendo algo mal, e implementó algo más sorprendente en su lugar.)

Lo que significa que no debe seguir cambiando la propiedad SourceRect. Necesitaré crear una instancia de CroppedBitmap separada para cada subimagen dentro de la hoja de sprites.

Otros consejos

Aquí hay una forma alternativa de lidiar con esto:
En lugar de usar un CroppedBitmap , use la imagen de origen completa, pero:

  1. Configure la imagen image.RenderTransform para ajustar el área visible.
  2. Establezca un Image.Clip si es necesario, para evitar mostrar partes de la imagen que no son deseadas.

Esto significa que no necesita seguir haciendo nuevos CroppedBitmaps , solo puede ajustar la transformación.
En mis pruebas, no vi diferencia en la velocidad al hacerlo de cualquier manera.

Para completar, así es como ajustaría su código para hacer lo que sugiero:

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

Entonces la última llamada para hacer el equivalente de ajustar el SourceRect es:

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

Aquí hay una manera de usar 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();
    }
}

Rendimiento potencialmente pobre.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top