Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 27 additions & 16 deletions src/Controls/src/Core/VisualElement/VisualElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1674,26 +1674,37 @@ private protected void SetPointerOver(bool value, bool callChangeVisualState = t
/// </summary>
protected internal virtual void ChangeVisualState()
{
if (!IsEnabled)
try
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled);
}
else
{
bool isSelected = this.IsElementInSelectedState();
string targetState = isSelected ? VisualStateManager.CommonStates.Selected
: (IsPointerOver ? VisualStateManager.CommonStates.PointerOver : VisualStateManager.CommonStates.Normal);
if (!IsEnabled)
{
VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled);
}
else
{
bool isSelected = this.IsElementInSelectedState();
string targetState = isSelected ? VisualStateManager.CommonStates.Selected
: (IsPointerOver ? VisualStateManager.CommonStates.PointerOver : VisualStateManager.CommonStates.Normal);

VisualStateManager.GoToState(this, targetState);
}
VisualStateManager.GoToState(this, targetState);
}

if (IsEnabled)
if (IsEnabled)
{
// Focus needs to be handled independently; otherwise, if no actual Focus state is supplied
// in the control's visual states, the state can end up stuck in PointerOver after the pointer
// exits and the control still has focus.
VisualStateManager.GoToState(this,
IsFocused ? VisualStateManager.CommonStates.Focused : VisualStateManager.CommonStates.Unfocused);
}
}
catch (InvalidOperationException)
{
// Focus needs to be handled independently; otherwise, if no actual Focus state is supplied
// in the control's visual states, the state can end up stuck in PointerOver after the pointer
// exits and the control still has focus.
VisualStateManager.GoToState(this,
IsFocused ? VisualStateManager.CommonStates.Focused : VisualStateManager.CommonStates.Unfocused);
// Swallow "PlatformView cannot be null here" thrown when a visual state cascade fans out
// during handler disconnect (e.g. on Windows: navigating away from a focused control runs
// UpdateIsFocused(false) inside DisconnectHandler -> ChangeVisualState -> VSM Setter ->
// mapper -> strongly-typed PlatformView accessor). The handler/PlatformView has already
// been released, so there is nothing for the mapper to update. See dotnet/maui#27101.
}
}

Expand Down
58 changes: 58 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue27101.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<local:TestContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:Class="Maui.Controls.Sample.Issues.Issue27101MainPage">
<local:TestContentPage.Resources>
<ResourceDictionary>
<Style x:Key="Issue27101ButtonStyle" TargetType="Button">
<Setter Property="TextColor" Value="White" />
<Setter Property="BackgroundColor" Value="Purple" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState Name="Focused" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light=Black, Dark=LightGray}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light=LightGray, Dark=Gray}" />
</VisualState.Setters>
</VisualState>
<VisualState Name="PointerOver">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Pressed">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ResourceDictionary>
</local:TestContentPage.Resources>
<local:TestContentPage.Content>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Button
x:Name="NavigateButton"
AutomationId="NavigateButton"
Text="Navigate to Second Page"
Clicked="OnNavigateButtonClicked"
HorizontalOptions="Fill"
Style="{StaticResource Issue27101ButtonStyle}"/>
</VerticalStackLayout>
</local:TestContentPage.Content>
</local:TestContentPage>
27 changes: 27 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue27101.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 27101, "PlatformView cannot be null here Exception in Windows", PlatformAffected.UWP)]
public class Issue27101 : NavigationPage
{
public Issue27101() : base(new Issue27101MainPage())
{
}
}

public partial class Issue27101MainPage : TestContentPage
{
public Issue27101MainPage()
{
InitializeComponent();
}

protected override void Init()
{
}

void OnNavigateButtonClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Issue27101SecondPage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:Class="Maui.Controls.Sample.Issues.Issue27101SecondPage">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="Issue27101ButtonStyle" TargetType="Button">
<Setter Property="TextColor" Value="White" />
<Setter Property="BackgroundColor" Value="Purple" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState Name="Focused" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light=Black, Dark=LightGray}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light=LightGray, Dark=Gray}" />
</VisualState.Setters>
</VisualState>
<VisualState Name="PointerOver">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Pressed">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Button
x:Name="NavigateBackButton"
AutomationId="NavigateBackButton"
Text="Navigate Back"
Clicked="OnNavigateBackButtonClicked"
HorizontalOptions="Fill"
Style="{StaticResource Issue27101ButtonStyle}"/>
</VerticalStackLayout>
</ContentPage.Content>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Maui.Controls.Sample.Issues
{
public partial class Issue27101SecondPage : ContentPage
{
public Issue27101SecondPage()
{
InitializeComponent();
}

async void OnNavigateBackButtonClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

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

public override string Issue => "PlatformView cannot be null here Exception in Windows";

[Test]
[Category(UITestCategories.Button)]
public void NoCrashWhenNavigatingBackFromPageWithFocusedButton()
{
// The crash reproduces only when a Button with VisualStates currently holds focus
// at the moment the page is navigated away from. Repeat the navigation a few times
// to give the focus race a chance to surface.
for (int i = 0; i < 10; i++)
{
App.WaitForElement("NavigateButton");
App.Tap("NavigateButton");

App.WaitForElement("NavigateBackButton");
App.Tap("NavigateBackButton");
}

// If we got here without an unhandled InvalidOperationException, the regression is fixed.
App.WaitForElement("NavigateButton");
}
}
Loading