UWP Composition-带有圆角的网格DropShadow [英] UWP Composition - Grid with rounded corners DropShadow

查看:79
本文介绍了UWP Composition-带有圆角的网格DropShadow的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个UWP应用程序,首先应该指出它使用的XAML很少.这些视图是根据从API接收到的JSON对象构建的.这意味着绝大多数事情都是用C#完成的,因此给我的问题增加了一点复杂性.

I have a UWP app, which I should start by pointing out that it uses very little XAML. The views are built from JSON object recieved from an API. This means that the vast majority of everything is done in C#, and therefore adds a little complexity to my problem.

我基本上希望有一个面板(例如Grid),该面板可以具有圆角并可以应用阴影.投影阴影还应该具有圆角,这可以在下面的示例中看到.

I basically want to have a panel (e.g. Grid) that can have rounded corners and have a drop shadow applied to it. The drop shadow should also have the rounded corners, this can be seen in the sample below.

我已经将DropShadowPanel视为 Windows社区工具包的一部分,但这是除非将内容更改为矩形或其他形状,否则我不会告诉您圆角.

I have looked at the DropShadowPanel as part of the Windows Community Toolkit, but this from what I can tell doesn't do the rounded corners unless I change the content to be a rectangle or some other shape.

将其用作解决方案将意味着XAML等同于以下内容:

To use this as a solution would mean the XAML equivalent of something like:

<Grid>
    <toolkit:DropShadowPanel>
         <Rectangle />
    <toolkit:DropShadowPanel>
    <Grid CornerRadius="30">
        <!-- My Content -->
    </Grid>
</Grid>

对我来说,这似乎对XAML的使用效率很低!

To me, this seems like an inefficient use of XAML!

我还发现了 Composition Pro工具包,对我来说,这看起来非常有趣,因为这一切后面的代码.特别是ImageFrame控件看起来可以满足我的需求-尽管比我的需求要先进得多.

I have also discovered the Composition Pro Toolkit, which to me looks bery interesting as it is all code behind. In particular the ImageFrame control looks to achieve the basis of what I require - although far more advanced than my needs.

以下内容基于ImageFrame,但不起作用(content是我的网格):

The below has been based on the ImageFrame, but doesn't work (content is my grid):

protected FrameworkElement AddDropShadow(FrameworkElement content)
{
    var container = new Grid { HorizontalAlignment = content.HorizontalAlignment, VerticalAlignment = content.VerticalAlignment, Width = content.Width, Height = content.Height };

    var canvas = new Canvas { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };

    content.Loaded += (s, e) =>
        {
            var compositor = ElementCompositionPreview.GetElementVisual(canvas).Compositor;

            var root = compositor.CreateContainerVisual();
            ElementCompositionPreview.SetElementChildVisual(canvas, root);

            var shadowLayer = compositor.CreateSpriteVisual();
            var frameLayer = compositor.CreateLayerVisual();
            var frameContent = compositor.CreateShapeVisual();

            root.Children.InsertAtBottom(shadowLayer);
            root.Children.InsertAtTop(frameLayer);

            frameLayer.Children.InsertAtTop(frameContent);

            var rectangle = root.Compositor.CreateRoundedRectangleGeometry();
            rectangle.Size = new Vector2((float)content.ActualWidth, (float)content.ActualHeight);
            rectangle.CornerRadius = new Vector2(30f);

            var shape = root.Compositor.CreateSpriteShape(rectangle);
            shape.FillBrush = root.Compositor.CreateColorBrush(Colors.Blue);

            //var visual = root.Compositor.CreateShapeVisual();
            frameContent.Size = rectangle.Size;
            frameContent.Shapes.Add(shape);

            //create mask layer
            var layerEffect = new CompositeEffect
            {
                Mode = Microsoft.Graphics.Canvas.CanvasComposite.DestinationIn,
                Sources = { new CompositionEffectSourceParameter("source"), new CompositionEffectSourceParameter("mask") }
            };

            var layerEffectFactory = compositor.CreateEffectFactory(layerEffect);
            var layerEffectBrush = layerEffectFactory.CreateBrush();


            //CompositionDrawingSurface
            var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, new Microsoft.Graphics.Canvas.CanvasDevice(forceSoftwareRenderer: false));
            var frameLayerMask = graphicsDevice.CreateDrawingSurface(new Size(0, 0), Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
            layerEffectBrush.SetSourceParameter("mask", compositor.CreateSurfaceBrush(frameLayerMask));
            frameLayer.Effect = layerEffectBrush;

            var shadow = root.Compositor.CreateDropShadow();
            //shadow.SourcePolicy = CompositionDropShadowSourcePolicy.InheritFromVisualContent;
            shadow.Mask = layerEffectBrush.GetSourceParameter("mask");
            shadow.Color = Colors.Black;
            shadow.BlurRadius = 25f;
            shadow.Opacity = 0.75f;
            shadow.Offset = new Vector3(0, 0, 0);
            shadowLayer.Shadow = shadow;

            content.Opacity = 0; //hiding my actual content to see the results of this
        };


    container.Children.Add(canvas);
    container.Children.Add(content);
    return container;
}

在这些测试中,我对对象的使用效率同样低下,创建了一个既包含画布又包含网格的容器.如果可能的话,我想将合成直接应用于原始内容网格.

In these tests, I am doing the same inefficient use of object, creating another container that has both the composition canvas, and also the grid. If possible, I'd like to apply the composition directly to the original content grid.

我对作曲完全陌生,因此任何想法,指针,明显的错误或解决方案都将受到欢迎.

I am completely new to composition, so any thoughts, pointers, glaring errors or solutions would be most welcomed.

我已将我的方法更改为以下方法,在视觉上它可以工作-但这对吗?

I have changed my method to the following, visually it works - but is it right?

protected FrameworkElement AddDropShadow(FrameworkElement content)
{
    var container = new Grid { HorizontalAlignment = content.HorizontalAlignment, VerticalAlignment = content.VerticalAlignment };
    var rectangle = new Rectangle { Fill = new SolidColorBrush(Colors.Transparent) };

    content.Loaded += (s, e) =>
        {
            rectangle.Fill = new SolidColorBrush(Colors.Black);
            rectangle.Width = content.ActualWidth;
            rectangle.Height = content.ActualHeight;
            rectangle.RadiusX = 30;
            rectangle.RadiusY = 30;

            var compositor = ElementCompositionPreview.GetElementVisual(rectangle).Compositor;
            var visual = compositor.CreateSpriteVisual();
            visual.Size = new Vector2((float)content.ActualWidth, (float)content.ActualHeight);

            var shadow = compositor.CreateDropShadow();
            shadow.BlurRadius = 30f;
            shadow.Mask = rectangle.GetAlphaMask();
            shadow.Opacity = 0.75f;
            visual.Shadow = shadow;

            ElementCompositionPreview.SetElementChildVisual(rectangle, visual);
        };

    container.Children.Add(rectangle);
    container.Children.Add(content);
    return container;
}

这里的概念是我的container网格包含一个rectangle和我的content网格(或其他元素).

The concept here is that my container grid holds a rectangle and my content grid (or other element).

此方法的第一个错误是假定我的输入FrameworkElement将是矩形.我想可以通过创建content的位图呈现器来改善此效果,如本

The first error of this method is that is assumes my input FrameworkElement will be rectangular. I imagine that this could be improved upon by creating a bitmap render of the content as highlighted in this blog - but this will likely be quite costly. I also have to ensure that the rectangle size and shape exactly matches that of my main content!

在屏幕上绘制一个矩形(即使被我的主要内容隐藏了)感觉很不对劲.矩形纯粹是用于创建Alpha蒙版的,所以我想如果从内容的渲染中创建蒙版,则可以将其废弃.

It feels very wrong that there is a rectangle drawn on the screen (even though hidden by my main content). The rectangle is purely there to create the alpha mask so I guess it could be scrapped if the mask is created from the renderof the content.

我尝试将矩形的可见性设置为折叠以将其从可视树中删除.这意味着我可以将视觉对象附加到容器上:

I've tried setting the visibility of the rectangle to collapsed to remove it from the visual tree. This means that I can attach the visual to the container instead:

ElementCompositionPreview.SetElementChildVisual(container, visual)

但是,这样做意味着阴影显示在主要内容的前面,这意味着我也需要附加其他ui元素-也可能是矩形!

However, doing this means that the shadow displays in front of the main content, which means I need some other ui element to attach it too - may as well be the rectangle!

推荐答案

在需要在GridBorder下使用圆角阴影的任何地方,您要使用Rectangle的解决方案是我当前的解决方法.很简单,很简单,我为什么要抱怨:) 但是,如果不是您的选择,则可以绘制一个圆角矩形并对其进行模糊处理:

Your solution to use Rectangle is my current workaround everywhere I need rounded shadow under Grid or Border. It's simple and it's plain, why should I complain :) But if it's not your choice you can draw a rounded rectangle and blur it:

GraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(Compositor, CanvasDevice.GetSharedDevice());                

var roudRectMaskSurface = GraphicsDevice.CreateDrawingSurface(new Size(SurfaceWidth + BlurMargin * 2, SurfaceHeight + BlurMargin * 2), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
using (var ds = CanvasComposition.CreateDrawingSession(roudRectMaskSurface))
{
    ds.Clear(Colors.Transparent);
    ds.FillRoundedRectangle(new Rect(BlurMargin, BlurMargin, roudRectMaskSurface.Size.Width + BlurMargin, roudRectMaskSurface.Size.Height + BlurMargin), YourRadius, YourRadius, Colors.Black);
}

var rectangleMask = Compositor.CreateSurfaceBrush(roudRectMaskSurface);

现在,您可以在具有模糊效果的EffectBrush中应用此表面以获得自定义阴影.

Now you can apply this surface in the EffectBrush with blur effect to obtain custom shadow.

BlurMargin-对应于模糊量,您需要使用它,因为模糊的表面将大于初始源矩形(以避免模糊剪辑).

BlurMargin - corresponds to the blur amount, you need it because your blurred surface will be bigger than initial source rectangle (to avoid blur clip).

这篇关于UWP Composition-带有圆角的网格DropShadow的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆