4

当我尝试在运行时更改 CroppedBitmap 的 SourceRect 属性时,没有任何反应。没有错误,并且属性值实际上并没有改变。

我正在尝试做精灵动画。我有一个包含精灵表的 BitmapSource,它是一个包含精灵不同姿势网格的单个位图。然后我有一个以 spritesheet 作为其源的 CroppedBitmap,以及一个从 spritesheet 中拉出其中一个姿势的 SourceRect。在运行时,当我想制作动画时,我试图更改 CroppedBitmap 的 SourceRect 属性,以从较大的位图中拉出不同的姿势;但是,如上所述,新的属性值根本不成立。这是最奇怪的事情。

这是一些示例 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)。

关于为什么会发生这种情况以及如何解决它的任何想法?

4

3 回答 3

16

我终于找到了答案。从CroppedBitmap的文档中:

CroppedBitmap 实现 ISupportInitialize 接口以优化多个属性的初始化。属性更改只能在对象初始化期间发生。调用 BeginInit 表示初始化已经开始,调用 EndInit 表示初始化已经完成。初始化后,属性更改将被忽略。 (强调我的)

只是为了好玩,我尝试在我的方法中添加 BeginInit()..EndInit() 调用,看看是否可以修改它。毫不奇怪,我得到了一个 InvalidOperationException(“不能多次设置初始化状态”)。

所以 CroppedBitmap 实际上是不可变的。(但是他们忽略了他们自己的 Freezable 系统,它会抛出一个异常来告诉我我做错了什么,而是实现了一些更令人惊讶的东西。)

这意味着,不要继续更改 SourceRect 属性。我需要为 spritesheet 中的每个子图像创建一个单独的 CroppedBitmap 实例。

于 2009-06-23T01:21:01.723 回答
6

这是处理此问题的另一种方法:
不使用 a CroppedBitmap,而是使用完整的源图像,但是:

  1. 设置image.RenderTransform以调整可视区域。
  2. 如有必要,设置一个Image.Clip,以避免显示不需要的图像部分。

这意味着您不需要不断制作 new 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);
于 2011-07-25T14:31:35.240 回答
0

这是一种使用方法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();
    }
}

性能可能很差。

于 2017-06-01T09:29:39.287 回答