尝试在运行时更改CroppedBitmap的SourceRect

时间:2009-06-22 17:39:22

标签: wpf dependency-properties

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

我正在尝试做精灵动画。我有一个包含spritesheet的BitmapSource,它是一个包含精灵不同姿势网格的单个位图。然后我有一个CroppedBitmap,它有spritesheet作为Source,还有一个SourceRect,它从spritesheet中拉出一个姿势。在运行时,当我想要设置动画时,我正在尝试更改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)。

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

3 个答案:

答案 0 :(得分:15)

我终于找到了答案。来自CroppedBitmap的文档:

  

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

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

所以CroppedBitmap实际上是不可变的。(但是他们忽略了他们自己的Freezable系统,这会抛出一个例外,告诉我我做错了什么,并实现了更多surprising代替。)

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

答案 1 :(得分:6)

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

  1. 设置image.RenderTransform以调整可视区域。
  2. 如有必要,请设置Image.Clip,以避免显示图像中不需要的部分。
  3. 这意味着您无需继续制作新的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);
    

答案 2 :(得分: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();
    }
}

潜在的差劲。