Skip to content
Draft
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2fac694
Fix shadows perf issues on Windows
jsuarezruiz Oct 23, 2023
3670485
Fix shadow with clipped views
jsuarezruiz Oct 24, 2023
2cb1c4c
Added more tests
jsuarezruiz Oct 24, 2023
c298d5a
More changes
jsuarezruiz Oct 24, 2023
43d751b
Changes in ShadowExtensions
jsuarezruiz Oct 25, 2023
0c0755b
More changes
jsuarezruiz Oct 25, 2023
4e819a9
More changes
jsuarezruiz Oct 25, 2023
a295f42
More changes
jsuarezruiz Oct 25, 2023
2ac51e9
Updated test
jsuarezruiz Oct 26, 2023
53c95af
Updated test
jsuarezruiz Oct 27, 2023
f53996b
Updated snapshot
jsuarezruiz Oct 27, 2023
5845cac
Updated test
jsuarezruiz Oct 27, 2023
08b6d15
Fix build error
jsuarezruiz Nov 23, 2023
872bb6c
Merge branch 'main' into fix-18205
jsuarezruiz Feb 8, 2024
5d7c443
Merge branch 'main' into fix-18205
jsuarezruiz Feb 27, 2024
a47b833
Merge branch 'main' into fix-18205
jsuarezruiz Mar 15, 2024
b12e282
Merge branch 'main' into fix-18205
jsuarezruiz Apr 11, 2024
7a3d90f
Fixes the build
jsuarezruiz Apr 11, 2024
8e40516
Merge branch 'main' into fix-18205
jsuarezruiz Apr 25, 2024
37bc0a5
More fixes
jsuarezruiz Apr 25, 2024
c1e73b3
Merge branch 'main' into fix-18205
jsuarezruiz May 16, 2024
40f3783
Merge branch 'main' into fix-18205
jsuarezruiz May 31, 2024
ba066df
Merge branch 'main' into fix-18205
jsuarezruiz Jun 3, 2024
41b7a9d
Merge branch 'main' into fix-18205
jsuarezruiz Feb 5, 2025
acce651
Update WrapperView.cs
jsuarezruiz Feb 5, 2025
7356397
More changes
jsuarezruiz Feb 5, 2025
8b22f84
Merge branch 'main' into fix-18205
jsuarezruiz Mar 7, 2025
710365e
Fix test path
jsuarezruiz Mar 7, 2025
9d50db7
Fix wrong namespace
jsuarezruiz Mar 7, 2025
f01d721
Merge branch 'main' into fix-18205
jsuarezruiz Mar 10, 2025
bc5a06d
Fix build error
jsuarezruiz Mar 16, 2025
66da9cb
Updated test
jsuarezruiz Mar 17, 2025
1780640
More changes
jsuarezruiz Mar 17, 2025
87ce964
Added pending snapshot
jsuarezruiz Mar 23, 2025
2011139
Updated snapshots
jsuarezruiz Mar 26, 2025
6823f8b
Merge branch 'main' into fix-18205
jsuarezruiz Apr 16, 2025
9aa9385
fixed nits
mattleibow May 23, 2025
4adf5a8
Merge branch 'main' into fix-18205
mattleibow May 23, 2025
b03f828
less clutter in the namespace
mattleibow May 23, 2025
3e5fc68
Merge branch 'main' into fix-18205
jfversluis Aug 28, 2025
97cfbb1
Update src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18…
jfversluis Aug 28, 2025
98e73ad
Update snapshots
jfversluis Sep 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;

namespace Maui.Controls.Sample.Pages;

public class ShadowBenchmark : ContentPage
{
event Action timerUpdateEvent;

public ShadowBenchmark()
{
Title = "Shadow Benchmark";

double fps = 60;

var timer = Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromSeconds(1 / fps);
double time = 0;
DateTime currentTickDateTime = DateTime.Now;
double deltaTime = 0;

timer.Tick += delegate
{
deltaTime = (DateTime.Now - currentTickDateTime).TotalSeconds;
currentTickDateTime = DateTime.Now;
time += deltaTime;
timerUpdateEvent?.Invoke();
};

timer.Start();

AbsoluteLayout rootAbs = new AbsoluteLayout();
Content = rootAbs;

Label fpsLabel = new Label
{
BackgroundColor = Colors.Black,
TextColor = Colors.White
};

AbsoluteLayout abs = new AbsoluteLayout();
rootAbs.Add(abs);
rootAbs.Add(fpsLabel);

VerticalStackLayout vert = new VerticalStackLayout();
vert.BackgroundColor = Colors.LightGray;
abs.Add(vert);

AbsoluteLayout abs2 = new AbsoluteLayout();
vert.Add(abs2);

int numBorders = 3;
List<Border> borders = new List<Border>();

for (int i = 0; i < numBorders; i++)
{
Border border = new Border
{
HeightRequest = 200,
WidthRequest = 500,
BackgroundColor = Colors.DimGray
};
abs2.Add(border);
borders.Add(border);
border.Shadow = new Shadow() { Brush = new SolidColorBrush(Colors.Black), Offset = new Point(5, 5), Radius = 5 };
}

Border borderBot = new Border
{
HeightRequest = 200,
WidthRequest = 50,
BackgroundColor = Colors.DarkGray
};
vert.Add(borderBot);

timerUpdateEvent += delegate
{
double sinVal = (Math.Sin(time) + 1) * 0.5;
double height = 20 + sinVal * 800;
for (int i = 0; i < borders.Count; i++)
{
borders[i].HeightRequest = height;
}
};

SizeChanged += delegate
{
if (Width > 0)
{
abs.WidthRequest = Width;
abs.HeightRequest = Height;
vert.WidthRequest = Width;

for (int i = 0; i < borders.Count; i++)
{
borders[i].WidthRequest = Width;
borders[i].TranslationX = Width * i;
}
}
};

DateTime lastUpdateDateTime = DateTime.Now;
abs2.SizeChanged += delegate
{
if (lastUpdateDateTime != DateTime.Now)
{
string fps = "FPS " + 1 / (DateTime.Now - lastUpdateDateTime).TotalSeconds;
fpsLabel.Text = fps;

lastUpdateDateTime = DateTime.Now;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Pages.ShadowMaskPage"
xmlns:views="clr-namespace:Maui.Controls.Sample.Pages.Base"
Title="Shadow Mask - Shadows respect control shape">
<VerticalStackLayout
Padding="12">
<Button
HorizontalOptions="Center"
Text="Shadow"
CornerRadius="60"
HeightRequest="120"
WidthRequest="120">
<Button.Shadow>
<Shadow
Brush="Black"
Opacity="1"
Radius="10"
Offset="0, 0" />
</Button.Shadow>
</Button>
</VerticalStackLayout>
</views:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Maui.Controls.Sample.Pages;

public partial class ShadowMaskPage
{
public ShadowMaskPage()
{
InitializeComponent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public ShadowPage()
GalleryBuilder.NavButton("Shadow Playground", () =>
new ShadowPlaygroundPage(), Navigation),
GalleryBuilder.NavButton("Invalidate Shadow Host", () =>
new InvalidateShadowHostPage(), Navigation),
new InvalidateShadowHostPage(), Navigation),
GalleryBuilder.NavButton("Shadow Mask", () =>
new ShadowMaskPage(), Navigation),
GalleryBuilder.NavButton("Shadow Benchmark", () =>
new ShadowBenchmark(), Navigation),
}
}
};
Expand Down
134 changes: 134 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue18172.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Microsoft.Maui.Controls.Shapes;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 18172, "Shadows not drawing/updating correctly in Windows & cover entire screen", PlatformAffected.UWP)]
public class Issue18172 : TestContentPage
{
protected override void Init()
{
Title = "Issue 18172";

BackgroundColor = Colors.LightPink;

ScreenSizeMonitor.Instance.setContentPage(this);

VerticalStackLayout verticalStackLayout = new VerticalStackLayout();
Content = verticalStackLayout;

AbsoluteLayout absoluteLayout = new AbsoluteLayout();
verticalStackLayout.Add(absoluteLayout);
absoluteLayout.Add(TopObjectWithShadow.Instance);
}
}

public class TopObjectWithShadow : AbsoluteLayout
{
public static TopObjectWithShadow Instance { get { return lazy.Value; } }
static readonly Lazy<TopObjectWithShadow> lazy = new Lazy<TopObjectWithShadow>(() => new TopObjectWithShadow());

public TopSubComponent topMenuFrameComponent;

private TopObjectWithShadow()
{
IgnoreSafeArea = true;

topMenuFrameComponent = new TopSubComponent();
Add(topMenuFrameComponent);

topMenuFrameComponent.Shadow = new Shadow()
{
Offset = new Point(0, 5),
Radius = 5
};

ScreenSizeMonitor.Instance.ScreenSizeChanged += OnScreenSizeChanged;
}

void OnScreenSizeChanged()
{
if (topMenuFrameComponent != null)
{
WidthRequest = ScreenSizeMonitor.Instance.screenWidth;
HeightRequest = ScreenSizeMonitor.Instance.screenHeight;

topMenuFrameComponent.resizeFunction();
}
}
}

public class TopSubComponent : AbsoluteLayout
{
readonly Border _frameBorder;
readonly double _frameBaseHeight = 68;

public TopSubComponent()
{
_frameBorder = new Border
{
AutomationId = "BorderControl"
};

Add(_frameBorder);
IgnoreSafeArea = true;
_frameBorder.HeightRequest = _frameBaseHeight;
_frameBorder.BackgroundColor = Colors.White;
_frameBorder.Margin = new Thickness(0, 20, 0, 0);
_frameBorder.StrokeShape = new RoundRectangle() { CornerRadius = new CornerRadius(30, 30, 30, 30) };
_frameBorder.StrokeThickness = 0;
}
public void resizeFunction()
{
WidthRequest = ScreenSizeMonitor.Instance.screenWidth;
HeightRequest = ScreenSizeMonitor.Instance.screenHeight;

_frameBorder.WidthRequest = ScreenSizeMonitor.Instance.screenWidth;
_frameBorder.HeightRequest = _frameBaseHeight;
}
}

class ScreenSizeMonitor
{
public ContentPage pageToMonitor;

private static readonly Lazy<ScreenSizeMonitor> lazy = new Lazy<ScreenSizeMonitor>(() => new ScreenSizeMonitor());
public static ScreenSizeMonitor Instance { get { return lazy.Value; } }

public double screenWidth = 0;
public double screenHeight = 0;
public event Action ScreenSizeChanged = null;

public void setContentPage(ContentPage contentPageToMonitor)
{
pageToMonitor = contentPageToMonitor;
StartScreenMonitor();
}

void StartScreenMonitor()
{
UpdateFunction();

pageToMonitor.SizeChanged += delegate
{
UpdateFunction();
};
}

void UpdateFunction()
{
if (pageToMonitor.Width > 0 && pageToMonitor.Height > 0)
{
screenWidth = pageToMonitor.Width;
screenHeight = pageToMonitor.Height;

invokeScreenSizeChanged();
}
}
public void invokeScreenSizeChanged()
{
MainThread.BeginInvokeOnMainThread(() =>
{
ScreenSizeChanged?.Invoke();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#if WINDOWS // Issue only happens on Windows
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

class Issue18172 : _IssuesUITest
{
public Issue18172(TestDevice device)
: base(device)
{ }

public override string Issue => "Shadows not drawing/updating correctly in Windows & cover entire screen";

[Test]
[Category(UITestCategories.GraphicsView)]
public async Task Issue18172Test()
{
await Task.Delay(500);

VerifyScreenshot();
}
}
#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsuarezruiz I know this was wrong even before, but there should be no shadows here given that the code does

case 5:
    border.Shadow = null;
    break;

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 11 additions & 7 deletions src/Core/src/Platform/Windows/ShadowExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,32 @@ public static async Task<CompositionBrush> GetAlphaMaskAsync(this UIElement elem

try
{
//For some reason, using TextBlock and getting the AlphaMask
//generates a shadow with a size more smaller than the control size.
if (element is TextBlock textElement)
var visual = ElementCompositionPreview.GetElementVisual(element);
var isClipped = visual.Clip is not null;

if (!isClipped && element is TextBlock textElement)
{
return textElement.GetAlphaMask();
}
if (element is Image image)
if (!isClipped && element is Image image)
{
return image.GetAlphaMask();
}
if (element is Shape shape)
if (!isClipped && element is Shape shape)
{
return shape.GetAlphaMask();
}
else if (element is FrameworkElement frameworkElement)
if (!isClipped && element is ContentPanel contentPanel)
{
return contentPanel.BorderPath?.GetAlphaMask();
Copy link

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method returns a CompositionBrush but the call to GetAlphaMask() could return null due to the null-conditional operator. This will cause a type mismatch since the method signature expects a non-null CompositionBrush return value.

Suggested change
return contentPanel.BorderPath?.GetAlphaMask();
mask = contentPanel.BorderPath?.GetAlphaMask();
break;

Copilot uses AI. Check for mistakes.
}
if (element is FrameworkElement frameworkElement)
{
var height = (int)frameworkElement.ActualHeight;
var width = (int)frameworkElement.ActualWidth;

if (height > 0 && width > 0)
{
var visual = ElementCompositionPreview.GetElementVisual(element);
var elementVisual = visual.Compositor.CreateSpriteVisual();
elementVisual.Size = element.RenderSize.ToVector2();
var bitmap = new RenderTargetBitmap();
Expand Down
Loading