Рендеринг теней Win2D поверх StackPanel (или любого элемента UIElement вместо позади)

Я пытаюсь реализовать тени на StackPanel или в любом элементе UIElement с помощью Win2D API, но кажется, что тень отображается поверх элемента, а не позади того места, где тень должна быть.

Это мой 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);
    }

}

Результирующее приложение выглядит так:

Результирующее приложение UWP С тенью поверх StackPanel, а не сзади.


person Andrés Sánchez    schedule 07.05.2020    source источник


Ответы (1)


Я нашел ответ. Оказывается, порядок рендеринга очень важен в XAML.

Я получил ответ, взглянув на DropShadowPanel windows-toolkit.

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

Как видите, они используют отдельный элемент Border с теми же свойствами Width и Height, что и в данном случае 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);
    }

}

Конечно, это демонстрационный код, который должен быть тривиальным для привязки свойств Width и Height границы и StackPanel (или любого другого UIElement) к шаблонному элементу управления или пользовательскому элементу управления.

Конечный результат выглядит следующим образом: введите здесь описание изображения

person Andrés Sánchez    schedule 07.05.2020