maui
maui copied to clipboard
RefreshView IsEnabled Disables All Blazor Hybrid Elements on Android, Works Properly on iOS
Description
In a Blazor Hybrid app, running .NET 9, and the 9.0.61 and 9.0.70 verisons of the Hybrid WebView control, targeting Android and iOS, not Windows. On some pages, I need to have the RefreshView disabled, such as the login screen. While on other screens, I need it enabled, such as those that display data. This is done following the Microsoft documentation to use the IsEnabled field to enable/disable the pull-to-refresh feature:
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/refreshview?view=net-maui-9.0 (look for Disable a RefreshView)
And here's the code in MainPage.xaml, which contains the Blazor app:
<RefreshView x:Name ="refreshView" Refreshing="RefreshView_OnRefreshing" IsEnabled="False">
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
</RefreshView>
And here is the codebehind:
public partial class MainPage : ContentPage
{
private ApplicationStateService _appState = null!;
public MainPage(ApplicationStateService appState)
{
_appState = appState;
InitializeComponent();
// If on iOS, make sure we use the safe area to accomodate for the notch.
On<iOS>().SetUseSafeArea(true);
// Ensure external URLs open properly.
blazorWebView.UrlLoading +=
(sender, urlLoadingEventArgs) =>
{
if (urlLoadingEventArgs.Url.Host.Contains("domain.com", StringComparison.Ordinal))
{
urlLoadingEventArgs.UrlLoadingStrategy =
UrlLoadingStrategy.OpenExternally;
}
};
}
private event OnRefreshRequested OnRefreshRequested;
private void RefreshView_OnRefreshing(object? sender, EventArgs e)
{
// Notify the application state service that a refresh has been requested.
_appState.NotifyRefreshRequested();
// Notify the UI that the refresh is complete.
refreshView.IsRefreshing = false;
}
public void EnableRefresh()
{
refreshView.IsEnabled = true;
}
public void DisableRefresh()
{
refreshView.IsEnabled = false;
}
}
Here's the Home.razor that won't load if I have IsEnabled="false" on Android, but works on iOS:
@page "/"
@inject ILogger<Home> _logger
@inject NotificationService _notificationService
@inject AuthService _authService
@inject ApplicationStateService _appState
@inject NavigationManager _navManager
<div class="login-container justify-content-center align-items-center d-flex flex-column rounded-3">
@if (!IsBusy)
{
<img src="/images/logo_withwords.svg" alt="Logo" class="mb-5">
<h2 class="text-black fw-bold fs-1">@StringResources.Phrase_WelcomeBack</h2>
<p><small>@StringResources.Phrase_EnterUnPwToLogin</small></p>
<EditForm Model="@_loginRequestModel" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
<DataAnnotationsValidator/>
<ValidationSummary/>
<div class="form-group">
<InputText
type="email"
inputtype="email"
maxlength="180"
class="form-control"
id="email"
@bind-Value="_loginRequestModel.Username"
placeholder="@StringResources.Word_Email.ToLower()"/>
</div>
<div class="form-group">
<InputText
type="password"
maxlength="32"
class="form-control"
id="password"
@bind-Value="_loginRequestModel.Password"
placeholder="@StringResources.Word_Password.ToLower()"/>
</div>
<div class="form-group">
<button type="submit" class="btn-primary btn-login w-100">@StringResources.Phrase_LogIn</button>
</div>
</EditForm>
<a href="https://domain.com/help/" class="d-block mt-3">@StringResources.Phrase_NeedHelp</a>
<br />
<span class="d-block">@($"{StringResources.Word_AppVersion} {VersionTracking.CurrentVersion} {StringResources.Word_AppBuild} {VersionTracking.CurrentBuild}")</span>
}
else
{
<WaitIndicator IsBusy="@_appState.IsBusy" />
}
</div>
If I set IsEnabled to false on Android, all inputs and buttons are disabled in the BlazorWebView on Android. It appears to be partially disabling the BlazorWebView. For example, if you change focus, validation will fire. But you can't enter data via keyboard, or press the submit button in an EditForm.
However, on iOS, this works properly, and only pull-to-refresh is disabled.
This is a blocking issue because it's impossible to ship the Android version with all functionality disabled.
Simply setting IsEnabled to true on the Android version re-enables all "embedded" controls. But pull-to-refresh is there, which is a confusing user experience.
Please let me know if I can assist with more details!
Steps to Reproduce
See above. I've included all the code and walk-through.
Link to public reproduction project repository
No response
Version with bug
9.0.70 SR7
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
Unknown/Other
Affected platforms
Android
Affected platform versions
Android API 35
Did you find any workaround?
Not using a RefreshView or always having RefreshView IsEnabled="true"
Relevant log output
None. This does not cause a compilation error or exceptions.
The provided code has assembly reference errors during compilation. Could you share a reproduction sample, ideally as a GitHub repository that we can clone for further investigation?
See the code above and use it on iOS and Android?
I'll try to get a sample project created for you today - but the code above I would imagine makes this very clear on how to repro.
I've reproduced it for you in this sample project. Enjoy 😄 https://github.com/AuriR/RefreshViewReproApp
As you will see - iOS with IsEnabled="False" lets you type in the text fields. Android does not.
Expected result: Android should allow typing in the text fields.
CC @TamilarasanSF4853
@TamilarasanSF4853 Here is a download link where you can see repro videos. iOS and Android, running from that solution I provided.
https://we.tl/t-wcJpXl5RT3
This issue has been verified in Visual Studio Code 1.100.3 with .NET MAUI versions 9.0.0 and 9.0.71. It is reproducible on the Android platform.
@AuriR thanks for contacting us.
Does this behavior reproduce in a regular webview without Blazor involved?
@javiercn I have not tried that. Of course, our entire app is MAUI/Blazor Hybrid, so it's not something I tried to troubleshoot. Can you check on your end? I gave you a repro project - maybe you can swap it out and see.
@javiercn Sorry, sent from my company account. See my answer above :) Thanks!
@AuriM3 the blazor team usually only looks at issues once we know they are Blazor specific, hence why we ask people to try out without Blazor to make sure that the behavior is not an existing webview behavior (which usually means we can't do anything about it).
I hear you. Still, since the only way to use the Refresh View with MAUI Blazor Hybrid is by placing the Blazor view inside the MAUI refresh view control. As I've shown, it works properly on iOS, and is broken on Android. It doesn't act according to documentation, which would make me think it's in your Iane to research further. You've repro'd it. I can take a look, but with respect I figured that was more in your lane than mine once I found a bug like this. Are you saying you won't look into this until I repro it another way? I just don't know that I can get to it this week, but if it's blocking you I'll try.
Maybe you need the MAUI team to look at it instead of the Blazor team, since it's the Blazor WebView control INSIDE OF the MAUI RefreshView control? Not sure how you share tickets between each other... I thought MAUI Blazor Hybrid would be a collab of sorts.
@javiercn I've modified the repro project to help you sort this out. Take a look at the branch CheckWebViewForSame and you will see the RefreshView also disables the WebView. So it appears to be a bug with the RefreshView and its child controls. You'll see you cannot enter any information into text fields, but button clicks appear to work. I hope that helps!
@mattleibow Is this something you could take a look at to ensure its Blazor specific? It sounds to me that this is likely an issue with the underlying webview.
Is there anything going on with the Parent check in the WebView/BlazorWebView on Android if it's a child control of a RefreshView? Trying to look through the codebase and help out... Hard because I'm unfamiliar with all the intricacies. First time I've seen the class AWebView for example :-P
@mattleibow @javiercn Checking in on this, please. Anything I can assist with? Any ideas for a workaround, please? Much appreciated!
Team - checking on this please. It's important to me because it's blocking our Android release.
@mattleibow @javiercn Any updates, please? Did this get moved to a different team? Making sure it's not in limbo with each team waiting on the other :) Thanks in advance!
@mattleibow @javiercn Maybe this is an issue, though I need to do some testing. See below. IsEnabledCore appears to affect all children if the parent view has IsEnabled set. However, IsEnabled is a bad property name for RefreshView. It's REALLY IsRrefreshEnabled not whether the view itself is enabled. So perhaps the following would fix it? See the added type check:
In VisualElement.cs Line 644.
/// <summary>
/// This value represents the cumulative IsEnabled value.
/// All types that override this property need to also invoke
/// the RefreshIsEnabledProperty() method if the value will change.
/// </summary>
protected virtual bool IsEnabledCore
{
get
{
if (_isEnabledExplicit == false)
{
// If the explicitly set value is false, then nothing else matters
// And we can save the effort of a Parent check
return false;
}
var parent = Parent as VisualElement;
if (parent is not null
&& !parent.IsEnabled
&& parent.GetType() != typeof(RefreshView) /* because RefreshView IsEnabled / IsEnabledCore should only affect Refreshing, not children */)
return false;
return _isEnabledExplicit;
}
}
@AuriR apologies for the delay.
I'm no expert in this area, I do work on the core Blazor bits, not on the Maui specific integration. It might take us some time to get to this as we are in the latter stages of the .NET 10.0 release.
If we determine more people is being impacted by this issue, we will raise the priority accordingly.
Ping @mattleibow can you help out here?
@javiercn Thank you for the follow-up. I'd be surprised if the RefreshView wasn't being used by a lot of apps, given it's the official MAUI view for handling refreshing. Is there a best practice for handling pull-to-refresh in MAUI/Blazor Hybrid apps that doesn't involve it, please? Happy to adjust! And of course thanks for all your hard work - looking forward to 10. Problem is, I can't wait to release my app in November+ so any recommended alternative approach while the bug is being fixed will be greatly appreciated :)
To your point about working on Blazor, yeah, it may not be a Blazor issue from the bits I'm seeing.
I think this issue is sort of a duplicate of this: https://github.com/dotnet/maui/issues/22699 with some extra problems thrown in.
I have created what I think is a alternate implemntation as a spec and will try see what happens: https://github.com/dotnet/maui/issues/30690
Not quite ready to close this issue as a duplicate, but I think the main issue is that IsEnabled was maybe commandered to switch to disable refreshing instead of the children. All other controls disable the view and all subviews in MAUI. Yet refresh view was different.
Please let me know if the new spec looks to address the issue and I will try get copilot to iterate and see what comes of it. I can't say we are going to fix it for .NET 10, but I will try to see if copilot can get us closer while I work on the other things I need to get to.
Hopefully, this is straight forward and we can get a new feature into .NET 10 soon.
@mattleibow I like it. I'm very curious what Copilot comes up with. And, that's simply very cool to see it attempt addressing it.
Is this a .NET 10 "thing" because it would be a breaking change due to the property changing?
Much appreciated! I'll also check out the workarounds mentioned. Fascinated by the prompting.
@mattleibow Checking in. Fascinating to see Copilot doing its thing. Are the changes it's proposing acceptable?
I think so. Are you able to try the artifacts in the PR? https://github.com/dotnet/maui/pull/30692
I think so. Are you able to try the artifacts in the PR? #30692
I'll take a look this week. I imagine I need to update my project to .NET 10 as well? I'll need to find those artifacts - didn't see them at a cursory glance, but I'm usually in ADO, not GitHub :) Adding it to my calendar, and happy to help!
I think so. Are you able to try the artifacts in the PR? #30692
And let me laugh at myself that the one thing I missed was the most ***king obvious: 😄
@mattleibow Looks like no nuget packages were built yet, blocked by the merge conflict. Checked maui-public and there's no link to Azure artifacts/pipelines. Also possible I simply don't have access. Please let me know if I'm missing something. Much appreciated!
@mattleibow Looks like no nuget packages were built yet, blocked by the merge conflict. Checked maui-public and there's no link to Azure artifacts/pipelines. Also possible I simply don't have access. Please let me know if I'm missing something. Much appreciated!
they are built now if you want to test