Silverlight 将UIElement渲染为可写BitMap时不考虑投影

Silverlight 将UIElement渲染为可写BitMap时不考虑投影,silverlight,silverlight-4.0,uielement,writeablebitmap,Silverlight,Silverlight 4.0,Uielement,Writeablebitmap,我使用的是Silverlight 4,无法从应用了投影的UIElements正确生成位图 在我的特定情况下,我使用WriteableBitmap类捕获应用了Matrix3DProjection的图像控件的位图。该投影表示用于将图像变换为任意四边形的非仿射变换。然后保存生成的位图供以后使用 不幸的是,当WriteableBitmap捕获UIElement时,投影似乎被忽略。对于任何可能附加的RenderTransform,情况似乎也是如此,尽管这可以通过使用接受转换的WriteableBitmap

我使用的是Silverlight 4,无法从应用了投影的
UIElements
正确生成位图

在我的特定情况下,我使用
WriteableBitmap
类捕获应用了
Matrix3DProjection
图像
控件的位图。该投影表示用于将图像变换为任意四边形的非仿射变换。然后保存生成的位图供以后使用

不幸的是,当
WriteableBitmap
捕获
UIElement
时,投影似乎被忽略。对于任何可能附加的RenderTransform,情况似乎也是如此,尽管这可以通过使用接受
转换的
WriteableBitmap
构造函数上的重载来解决。对于预测,似乎没有这样的考虑

下面的人为代码示例说明了这一点。它转换的是
按钮
而不是
图像
组件,但效果相同

XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="button" Grid.Column="0" Width="100" Height="50" Click="button_Click">Render me</Button>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200" Stretch="Uniform"/>
</Grid>
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border x:Name="border" Width="200" Height="200" Grid.Column="0">
        <Button x:Name="button" Width="120" Height="50" Click="button_Click">Render me</Button>
    </Border>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200"/>
</Grid>
运行时,您会发现按钮的图像被渲染到
图像
组件中,但没有原始按钮本身明显的失真

我尝试过在
WriteableBitmap
上使用构造函数和
Render
方法,但没有成功地使用它们的各种重载。我还尝试将源
UIElement
嵌套在其他元素中,并在树的不同级别应用投影,以查看
WriteableBitmap
是否能够正确渲染投影,如果它是更大的复合结构的一部分。那里也没有运气

希望我只是错过了一些简单的东西,但我怀疑这在当前版本的Silverlight中是不可能的

如果这确实是一条死胡同,那么对我来说,一个可行的替代方案是能够直接将矩阵变换应用于位图,完全绕过整个
ui元素。然而,我真的不知道从哪里开始拍摄源位图,通过这样的转换为其提供像素,并收集结果输出。转换是非仿射的,这一事实向我表明,将涉及某种程度的插值,并且这种解决方案的数学要求可能非常重要


非常感谢您的帮助或建议。

尝试使用Silverlight Control Toolkit(System.Windows.Controls.Layout.Toolkit assembly)中的
LayoutTransformer


解决我问题的办法原来很简单。只需将源
UIElement
设为另一个元素的子元素,然后将投影应用于子元素,但在渲染到
WriteableBitmap
时使用父元素。下面修改的代码示例:

XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="button" Grid.Column="0" Width="100" Height="50" Click="button_Click">Render me</Button>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200" Stretch="Uniform"/>
</Grid>
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border x:Name="border" Width="200" Height="200" Grid.Column="0">
        <Button x:Name="button" Width="120" Height="50" Click="button_Click">Render me</Button>
    </Border>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200"/>
</Grid>

感谢@foson让我更加认真地看待这一点。

非常有效-非常感谢!我还不清楚到底是什么让这起作用,所以我要检查一下LayoutTransformer代码,看看是否能找到答案。当我完成后,我会把我的发现发回到这里。嗯,看起来我在其他控件中嵌套源元素的初始实验没有我想象的那么彻底。结果表明,将投影应用于子对象,但从父对象渲染到可写Bitmap的效果与预期一样。布置变压器工作,但不是必需的。一个简单的父边框也可以达到同样的效果。我将保留这个答案作为已接受的答案,但将添加另一个与相关细节。再次感谢。
<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Border x:Name="border" Width="200" Height="200" Grid.Column="0">
        <Button x:Name="button" Width="120" Height="50" Click="button_Click">Render me</Button>
    </Border>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200"/>
</Grid>
public MainPage()
{
    InitializeComponent();

    button.Projection = new Matrix3DProjection()
    {
        ProjectionMatrix = new Matrix3D()
        {
            M11 = 0.825, M12 = -0.513, M13 = 0, M14 = 0.001,
            M21 = 0.023, M22 = 0.986, M23 = 0, M24 = -0.002,
            M31 = 0, M32 = 0, M33 = 1, M34 = 0,
            OffsetX = 0, OffsetY = 0, OffsetZ = 0, M44 = 1
        }
    }; 
}

private void button_Click(object sender, RoutedEventArgs e)
{
    // Render the button as a bitmap and display
    WriteableBitmap bitmap = new WriteableBitmap(border, null);  
    image.Source = bitmap;
}