CommunityToolkit / WindowsCommunityToolkit

The Windows Community Toolkit is a collection of helpers, extensions, and custom controls. It simplifies and demonstrates common developer tasks building .NET apps with UWP and the Windows App SDK / WinUI 3 for Windows 10 and Windows 11. The toolkit is part of the .NET Foundation.
https://docs.microsoft.com/windows/communitytoolkit/
Other
5.9k stars 1.37k forks source link

[Feature] Add DropShadowPanel Support for Grid & Border content with Translucent Brushes & Round Corners #3607

Closed seanocali closed 3 years ago

seanocali commented 3 years ago

Describe the problem this feature would solve

DropShadowPanel only supports obtaining an alpha mask from certain types of content (Image, Shape, TextBlock, and ImageEx). If you try to use content such as a Grid or Border that has either a background brush or border brush with rounded corners, you do not get a matching drop shadow. You can substitute a Rectangle to get rounded corners, however Rectangles don't stretch and must be a fixed size which can introduce UI design difficulties. Also if you want a translucent brush then the reduced opaqueness you set on the content will be applied to the drop shadow. Having semi-opaque content while also having a clearly defined dropshadow is basically not possible. On top of this, if you add an AcrylicBrush it fails and reverts to a fallback solid color.

Describe the solution

I've created an extended control which doesn't affect the existing functionality for Image, Shape, TextBlock, and ImageEx. When the content is a Grid or Border, a matching CompositionRoundedRectangleGeometry is generated. This is used to create a SpriteShape, which is added to a ShapeVisual, which is used to create a CompositionSurfaceBrushto provide the dropshadow mask. The Size, Fill, Stroke, and CornerRadius are mapped and updated accordingly.

The same support can be added for StackPanel, RelativePanel, etc. I'm more than happy to submit a pull request.

Additional context & Screenshots

Here's a screenshot of what my extended version of DropShadowPanel can do that the original cannot: Screenshot 2020-12-04 142631

ghost commented 3 years ago

Hello, 'seanocali! Thanks for submitting a new feature request. I've automatically added a vote 👍 reaction to help get things started. Other community members can vote to help us prioritize this feature in the future!

Kyaa-dost commented 3 years ago

@seanocali Thanks for the feature request and for taking an initiative in this area. I really like the idea as it seems more reliable but let's see whats other community members have to say about this and if approved then you can proceed to submit the PR 😊

mdtauk commented 3 years ago

It would be good if there was a mode added which would "cutout" the area above the shadow, so the shadow only shows outside of the shape. This would allow a semi-transparent fill to not show the shadowed area below.

It could take the CompositionRoundedRectangleGeometry generated, and use it to mask out the shadow

image

Kyaa-dost commented 3 years ago

Thanks for the input @mdtauk!

@seanocali any insight on the advice above? ⬆️

seanocali commented 3 years ago

@mdtauk Yeah I like that idea.

seanocali commented 3 years ago

Also note my solution just takes the Top-Left CornerRadius and applies it to the rectangle's X and Y radii. Having 4 independent corners could be done with a Path instead of a Rectangle but I haven't dove into that yet.

mdtauk commented 3 years ago

Also note my solution just takes the Top-Left CornerRadius and applies it to the rectangle's X and Y radii. Having 4 independent corners could be done with a Path instead of a Rectangle but I haven't dove into that yet.

Is there any idea of what (if any) performance implications there may be implementing it?

Are there ways to optimise DropDownShadows applied to paths, like cloning the data instead of constructing a new shape?

seanocali commented 3 years ago

I haven't done tests, but it wouldn't surprise me if creating a new shape is less expensive than calling GetAlphaMask() on an element in the visual tree.

Speaking of performance the DropShadowPanel is currently riddled with redundant casting. For example:

 if (Content is Image)
      {
          mask = ((Image)Content).GetAlphaMask();
      }

should be

 if (Content is Image image)
      {
          mask = image.GetAlphaMask();
      }
michael-hawker commented 3 years ago

FYI @JustinXinLiu @Sergio0694