0

can you please explain how to use shadows in a desktop application on WinUI 3? I have looked at the MSDN documentation and it suggests using the ThemeShadow and DropShadow classes. I figured out the ThemeShadow class and it works, but it does not allow you to customize the shadow. Therefore, I need the DropShadow class, and with it, I could no longer figure it out. MSDN gives this simple example of using a class:

private async void InitComposition()
{
  _compositor = ElementCompositionPreview.GetElementVisual(MyGrid).Compositor;
  _imageLoader = ImageLoaderFactory.CreateImageLoader(_compositor);

  //Create surface brush and load image
  CompositionSurfaceBrush surfaceBrush = _compositor.CreateSurfaceBrush();
  surfaceBrush.Surface = await _imageLoader.LoadImageFromUriAsync(new Uri("ms-appx:///Assets/cat.jpg"));

  //Create sprite visual
  SpriteVisual visual = _compositor.CreateSpriteVisual();
  visual.Brush = surfaceBrush;
  visual.Size = new Vector2(270, 200);

  //Create drop shadow
  DropShadow shadow = _compositor.CreateDropShadow();
  shadow.BlurRadius = 5;
  shadow.Offset = new Vector3(15, 15, -10);
  shadow.Color = Colors.DarkGray;

  //Associate shadow with visual
  visual.Shadow = shadow;
}      

I copied this code to myself and it did not work for me for the reason that my project does not understand what kind of ImageLoaderFactory class it is. As I understand it, this class loads a picture, which, in principle, I don’t need. I removed this line, but nothing seems to work without it. I do not understand the purpose of the classes that are used in this example and do not understand the principle of creating a shadow, so I could not adapt the example to my needs. It would be great if someone could explain to me how to use all this. I used to work on WPF and adding a shadow there was something trivial. It was only necessary to add an effect to the Effect property of any element and the shadow worked perfectly. But here everything is different. Somehow too difficult. I would like to know if there is some easier official way to enable the shadow. And I would still like to understand how to work with the DropShadow class and include shadows on any element, and in particular, at the moment my task is to add a shadow to TextBlock, in order to increase the readability of text in bright areas.

Among other things, a bonus question that is slightly related to the previous one. I also couldn't find a way to set the text stroke for the same readability in the light areas. On WPF, there was a special property for this, but on WinUI 3, this simply does not exist. Help me please.

UPDATE:

At the moment my code looks like this:

<Grid x:Name="_MainGrid">
    <Grid.RowDefinitions>
        <RowDefinition x:Name="_TitleBarGridRow" Height="32"/>
        <RowDefinition x:Name="_MainFrameRow" Height="*"/>
    </Grid.RowDefinitions>
    <Grid x:Name="_TitleBarGrid" Grid.Row="0"/>
    <Rectangle x:Name="MyRect1" Grid.Row="1" Width="500" Height="500" Fill="White"></Rectangle>
    <Rectangle x:Name="MyRect2" Grid.Row="1" Width="250" Height="250" Fill="Green"></Rectangle>
</Grid>
var compositor = ElementCompositionPreview.GetElementVisual(MyRect2).Compositor;

SpriteVisual visual = compositor.CreateSpriteVisual();
visual.Size = new Vector2(250, 250);

DropShadow dropShadow = compositor.CreateDropShadow();
dropShadow.BlurRadius = 50;
dropShadow.Offset = new Vector3(0, 0, 0);
dropShadow.Color = Colors.Black;

visual.Shadow = dropShadow;

// Apply the drop shadow to a UI element
((ContainerVisual)ElementCompositionPreview.GetElementVisual(MyRect2)).Children.InsertAtTop(visual);

Thanks to this, the shadow is displayed but it is displayed on top of the rectangle2, while I expect it to be displayed below it.

enter image description here

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • You must add any visual you've created to the tree, so cast `ElementCompositionPreview.GetElementVisual(MyGrid)` into a `ContainerVisual` and do `containerVisual.Children.InsertAtTop(visual)` for example. As for `ImageLoaderFactory` I don't know what this is, just use `LoadedImageSurface.StartLoadFromUri` (ex: https://stackoverflow.com/a/76760568/403671) – Simon Mourier Aug 17 '23 at 21:07
  • As far as I'm concerned, if you want to use the `DropShadow`, you need to add the [ImageLoader.cs](https://github.com/microsoft/uwp-experiences/blob/master/apps/News/News/Helpers/Composition/ImageLoader/ImageLoader.cs). – Jeaninez - MSFT Aug 18 '23 at 06:00
  • @SimonMourier, i did as you said and the shadow actually became visible, but there was another problem: it is displayed above my rectangle, although it is obvious that I expect a shadow below it – Георгий Попов Aug 18 '23 at 12:59
  • A shadow should be under the visual (here not under `MyGrid` but under `visual`). Or please share a fully reproducible sample – Simon Mourier Aug 18 '23 at 13:16
  • @SimonMourier, I have updated the question, please see my code, maybe I don't understand something about the use of this class – Георгий Попов Aug 18 '23 at 13:19
  • It's because your added visual is transparent, add this `visual.Brush = compositor.CreateColorBrush(Colors.Blue);` for example – Simon Mourier Aug 18 '23 at 13:27
  • @SimonMourier, I did that and it really gave me the effect I wanted. But this has me stumped. Do I understand correctly that the style specified in this line is `` won't be saved? It after all can be more difficult, than Fill="Green". Then it will completely overlap with what is defined in SpriteVisual and I again have to rewrite the entire style? – Георгий Попов Aug 18 '23 at 13:33
  • Not sure I understand your questions. Here you're talking to the Visual Layer (aka Windows.Composition aka Direct Composition https://learn.microsoft.com/en-us/windows/uwp/composition/visual-layer) which lives a level below XAML *technically* (*not* below visually). Think about it like this: *a XAML element has a Visual, but a Visual doesn't necessarily have a XAML element*. So, the XAML rectangle has a (container) visual, but you can tweak this visual and add sub visual like you want (w/o XAML existence) for example – Simon Mourier Aug 18 '23 at 13:40
  • @SimonMourier, I think now I understand everything. Thank you so much for your help! – Георгий Попов Aug 18 '23 at 13:46

1 Answers1

0

The code in the link you provided, shows a simple way to create shadows. This is an example but with Buttons.

<Grid>
    <Grid.Resources>
        <ThemeShadow x:Name="SharedShadow" />
    </Grid.Resources>
    <Grid
        x:Name="BackgroundGrid"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
    <Button
        x:Name="Rectangle1"
        Width="100"
        Height="100"
        HorizontalAlignment="Center"
        Shadow="{StaticResource SharedShadow}" />
    <Button
        x:Name="Rectangle2"
        Width="100"
        Height="100"
        HorizontalAlignment="Center"
        Shadow="{StaticResource SharedShadow}" />
</Grid>
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
    }

    private void MainPage_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
    {
        SharedShadow.Receivers.Add(BackgroundGrid);
        Rectangle1.Translation += new Vector3(0, 0, 16);
        Rectangle2.Translation += new Vector3(120, 0, 32);
    }
}
Andrew KeepCoding
  • 7,040
  • 2
  • 14
  • 21
  • Yes, using a shadows with the ThemeShadow class works for me.. But the problem is that this class does not allow you to customize the shadows, for example, set the shadow color, transparency, blur, etc. In the attached link there is also a section using the DropShadow class, which just allows you to customize the shadow, and I had problems with it :( – Георгий Попов Aug 17 '23 at 12:33