Pergunta

Quando tento alterar a propriedade SourceRect um CroppedBitmap em tempo de execução, nada acontece. Não há nenhum erro, eo valor da propriedade na verdade não se mudou.

Eu estou tentando fazer a animação do sprite. I têm um BitmapSource que contém um spritesheet, que é um único mapa de bits que contém uma grade de diferentes posturas para o sprite. Então eu tenho um CroppedBitmap que tem o spritesheet como sua fonte, e uma SourceRect que puxa uma das poses fora do spritesheet. Em tempo de execução, quando eu quero animar, eu estou tentando alterar a propriedade SourceRect do CroppedBitmap, para puxar uma pose diferente fora do bitmap maior; mas, como mencionado acima, o novo valor da propriedade simplesmente não cola. É a coisa mais estranha.

Eis alguns XAML amostra:

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

E as tentativas codebehind para fazer isso:

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

Isso me dá essa saída de depuração:

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

Por isso é realmente atribuir o novo retângulo (com Y = 0) para a propriedade; não há nenhuma exceção; mas depois, o valor da propriedade simplesmente não mudar (Y ainda é 640).

Todas as ideias sobre por que isso acontece e como corrigi-lo?

Foi útil?

Solução

Eu finalmente encontrei a resposta. A partir da documentação para CroppedBitmap :

implementos CroppedBitmap a interface ISupportInitialize a inicialização otimizar em várias propriedades. alterações de propriedade só pode ocorrer durante a inicialização do objeto. Chamada BeginInit para sinalizar que a inicialização começou e EndInit para sinalizar que a inicialização foi concluída. Após a inicialização, alterações de propriedade são ignoradas. (grifo meu)

Apenas por diversão, eu tentei adicionar BeginInit () .. EndInit () chama em meu método, para ver se isso iria torná-lo modificável. Não surpreendentemente, eu tenho um InvalidOperationException ( "Não é possível definir o estado de inicialização mais de uma vez").

Assim CroppedBitmap é efetivamente imutável. (Mas eles ignoraram seu próprio sistema de Freezable, que teria jogado uma exceção para me dizer que eu estava fazendo algo errado, e implementado algo mais surpreendendo vez)

O que significa, no-go sobre como alterar a propriedade SourceRect. Vou precisar para criar uma instância CroppedBitmap separado para cada sub-imagem dentro da spritesheet.

Outras dicas

Aqui está uma forma alternativa de lidar com isso:
Em vez de usar um CroppedBitmap, use a imagem de origem completo, mas:

  1. Defina o image.RenderTransform para ajustar a área visível.
  2. Definir um Image.Clip se necessário, para evitar mostrar partes da imagem que não são desejadas.

Isso significa que você não precisa continuar a fazer nova CroppedBitmaps, você pode simplesmente ajustar a transformar.
Em meus testes, eu não vi nenhuma diferença na velocidade de fazê-lo de qualquer forma.

Para completar, aqui está como eu ajustar seu código para fazer o que eu estou sugerindo:

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

Em seguida, a chamada mais tarde para fazer o equivalente a ajustar o SourceRect é:

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

Aqui está uma maneira usando 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();
    }
}

Potencialmente pobre perf.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top