Win2D 阴影渲染在 StackPanel 顶部(或任何 UIElement 而不是后面)

Win2D Shadows rendering on top of StackPanel (or any UIElement instead of behind)

我正在尝试在 StackPanel 或任何使用 Win2D API 的 UIElement 中实现投影,但似乎阴影呈现在榆树的顶部而不是在它有意义的地方一个影子。

这是我的XAML:

<Page
    x:Class="ShadowsDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:ShadowsDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid
        Background="LightBlue">
        <StackPanel
            x:Name="RootHolder"
            Width="400"
            Height="524"
            Margin="8"
            Background="White"
            Orientation="Vertical">
            <Image Width="400" Height="200" Source="Assets/LockScreenLogo.scale-200.png" />
            <StackPanel Orientation="Vertical">
                <TextBlock Margin="20,20,0,0" TextWrapping="Wrap">
                    <Underline>
                        <Run
                            FontSize="28"
                            Foreground="#333366"
                            Text="Some title text" />
                    </Underline>
                </TextBlock>
                <TextBlock
                    Margin="20"
                    FontSize="16"
                    Foreground="Black"
                    Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
                    TextWrapping="Wrap" />
            </StackPanel>
        </StackPanel>
    </Grid>
</Page>

还有我背后的代码:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        var rootHolder = RootHolder;

        var visualHost = ElementCompositionPreview.GetElementVisual(rootHolder);
        var compositor = visualHost.Compositor;

        var dropShadow = compositor.CreateDropShadow();
        dropShadow.Offset = new Vector3(14, 16, 48);
        dropShadow.BlurRadius = 24.0f;
        dropShadow.Color = Color.FromArgb(128, 0, 0, 0);

        var spriteVisual = compositor.CreateSpriteVisual();
        spriteVisual.Size = new Vector2((float) rootHolder.Width, (float) rootHolder.Height);
        spriteVisual.Shadow = dropShadow;

        ElementCompositionPreview.SetElementChildVisual(rootHolder, spriteVisual);
    }

}

生成的应用程序如下所示:

阴影在 StackPanel 而不是后面。

我找到了答案。事实证明渲染顺序在 XAML.

中非常重要

我通过查看 windows-toolkit 的 DropShadowPanel

得到了答案

DropShadowPanel.xaml: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/DropShadowPanel/DropShadowPanel.xaml

DropShadowPanel.cs: https://github.com/windows-toolkit/WindowsCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/DropShadowPanel/DropShadowPanel.cs

如您所见,他们使用的是具有相同 WidthHeight 属性的独立 Border 元素,在本例中为 ContentPresenter,在我们的例子中为StackPanel.

我们的新代码应该是这样的:

XAML:

<Page
    x:Class="ShadowsDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:ShadowsDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid Background="LightBlue">
        <Border 
            x:Name="RootHolder" 
            Width="400"
            Height="524"/>
        <StackPanel
                x:Name="Sp"
                Width="400"
                Height="524"
                Margin="8"
                Background="White"
                Orientation="Vertical">
            <Image
                    Width="400"
                    Height="200"
                    Source="Assets/LockScreenLogo.scale-200.png" />
            <StackPanel Orientation="Vertical">
                <TextBlock Margin="20,20,0,0" TextWrapping="Wrap">
                        <Underline>
                            <Run
                                FontSize="28"
                                Foreground="#333366"
                                Text="Some title text" />
                        </Underline>
                </TextBlock>
                <TextBlock
                        Margin="20"
                        FontSize="16"
                        Foreground="Black"
                        Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
                        TextWrapping="Wrap" />
            </StackPanel>
        </StackPanel>

    </Grid>
</Page>

后面的代码:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        var rootHolder = RootHolder;//var rootHolder = (StackPanel) GetTemplateChild("RootHolder");

        var visualHost = ElementCompositionPreview.GetElementVisual(rootHolder);
        var compositor = visualHost.Compositor;

        var dropShadow = compositor.CreateDropShadow();
        dropShadow.Offset = new Vector3(14, 16, 48);
        dropShadow.BlurRadius = 24.0f;
        dropShadow.Color = Color.FromArgb(128, 0, 0, 0);

        var spriteVisual = compositor.CreateSpriteVisual();

        spriteVisual.Size = new Vector2((float) Sp.Width, (float) Sp.Height);
        spriteVisual.Shadow = dropShadow;

        ElementCompositionPreview.SetElementChildVisual(rootHolder, spriteVisual);
    }

}

当然这是演示代码,绑定边框的 WidthHeight 属性以及 StackPanel(或任何其他 UIElement)应该很简单模板化控件或用户控件。

最后的结果是这样的: