MAUI Blazor学习17-NavigationLock阻止页面回退

MAUI Blazor学习17-NavigationLock阻止页面回退

 

MAUI Blazor系列目录

  1. MAUI Blazor学习1-移动客户端Shell布局 - SunnyTrudeau - 博客园 (cnblogs.com)
  2. MAUI Blazor学习2-创建移动客户端Razor页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  3. MAUI Blazor学习3-绘制ECharts图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  4. MAUI Blazor学习4-绘制BootstrapBlazor.Chart图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  5. MAUI Blazor学习5-BLE低功耗蓝牙 - SunnyTrudeau - 博客园 (cnblogs.com)
  6. MAUI Blazor学习6-扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  7. MAUI Blazor学习7-实现登录跳转页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  8. MAUI Blazor学习8-支持多语言 - SunnyTrudeau - 博客园 (cnblogs.com)
  9. MAUI Blazor学习9-VS Code开发调试MAUI入门 - SunnyTrudeau - 博客园 (cnblogs.com)
  10. MAUI Blazor学习10-BarcodeScanner扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  11. MAUI Blazor学习11-百度地图定位 - SunnyTrudeau - 博客园 (cnblogs.com)
  12. MAUI Blazor学习12-文件另存为 - SunnyTrudeau - 博客园 (cnblogs.com)
  13. MAUI Blazor学习13-打开文件 - SunnyTrudeau - 博客园 (cnblogs.com)
  14. MAUI Blazor学习14-选择目录 - SunnyTrudeau - 博客园 (cnblogs.com)
  15. MAUI Blazor学习15-采用html2pdf.js生成pdf - SunnyTrudeau - 博客园 (cnblogs.com)
  16. MAUI Blazor学习16-连续按BACK退出APP - SunnyTrudeau - 博客园 (cnblogs.com)

 

APP功能开发中,有时候要阻止页面回退,比如,当前填写的内容没有保存,长时间运行的任务没有完成等等。本来想着拦截BACK按键事件来实现,后来发现MAUI Blazor有现成的组件NavigationLock,可以非常简单的实现阻止页面回退的功能。参考微软官网:

https://learn.microsoft.com/zh-cn/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-8.0#handleprevent-location-changes

只要呈现导航事件,NavigationLock 组件就会将其拦截,除非决定继续或取消,否则将一直有效“锁定”任何给定的导航。 当导航拦截的范围可以限定为组件的生存期时,使用 NavigationLock

NavigationLock 参数:

ConfirmExternalNavigation 设置浏览器对话框以提示用户确认或取消外部导航。 默认值为 false。 显示确认对话框需要在使用浏览器地址栏中的 URL 触发外部导航之前与页面进行初始用户交互。 有关交互要求的详细信息,请参阅 Windowbeforeunload 事件(MDN 文档)。

OnBeforeInternalNavigation 为内部导航事件设置回调。

 

基于MaBlaApp项目,添加NavigationLock组件阻止页面回退功能。模拟实现一个需求,当前页面正在运行长时间任务,需要首先停止任务,才能退出页面。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Pages\LongTimeTask.razor

 

@page "/longtimetask"

<h3>阻止长时间任务页面回退</h3>

<div class="d-flex justify-content-between m-1">
    <button class="btn btn-primary mx-2 @BtnStartCss" @onclick=StartTaskAsync>开始长时间任务</button>
    <button class="btn btn-danger mx-2 @BtnStopCss" @onclick=@(() => StopTaskAsync(false))>停止长时间任务</button>
</div>
<p class="m-1">任务进度:@ProcessMsg</p>
<div class="progress m-1" style="height:10px">
    <div class="progress-bar bg-primary" style="width:@Process%"></div>
</div>

<NavigationLock ConfirmExternalNavigation="true" OnBeforeInternalNavigation="OnBeforeInternalNavigation" />

@code {
    private string BtnStartCss => IsBusy ? "disabled" : "";
    private string BtnStopCss => IsBusy ? "" : "disabled";

    private bool IsBusy = false;
    private CancellationTokenSource Cts;

    private int Process = 0;
    private string ProcessMsg = "";

    //开始长时间任务
    private async Task StartTaskAsync()
    {
        if (IsBusy)
            return;

        if (Cts is not null)
            return;

        try
        {
            IsBusy = true;
            Cts = new CancellationTokenSource();

            var startTime = DateTimeOffset.Now;

            Process = 0;
            ProcessMsg = "...";
            StateHasChanged();

            using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
            while (await timer.WaitForNextTickAsync(Cts.Token))
            {
                Process += 10;

                ProcessMsg = $"耗时{DateTimeOffset.Now.Subtract(startTime).TotalSeconds:N0}秒,进度{Process}%";
                StateHasChanged();

                if (Process >= 100)
                {
                    ProcessMsg += ",任务圆满完成";
                    break;
                }
            }
        }
        catch (OperationCanceledException)
        {
            ProcessMsg += ",任务提前终止";
            System.Diagnostics.Debug.WriteLine($"{DateTimeOffset.Now:G}, {ProcessMsg}");
        }
        finally
        {
            Cts.Dispose();
            Cts = null;
            IsBusy = false;
        }

        StateHasChanged();
    }

    //停止长时间任务
    private async Task StopTaskAsync(bool forceStop = false)
    {
        if (!IsBusy)
            return;

        if (!forceStop)
        {
            //询问用户是否结束任务
            bool isStopTask = await App.Current.MainPage.DisplayAlert("警告", "任务进行中,是否结束任务?", "结束任务", "继续任务");
            if (!isStopTask)
                return;
        }

        if (Cts is not null)
            Cts.Cancel();
    }

    //检查能否安全退出页面
    private async Task OnBeforeInternalNavigation(LocationChangingContext context)
    {
        //如果当前任务正在运行中,请用户先停止任务,才能退出页面
        if (IsBusy)
        {
            bool isStopTask = await App.Current.MainPage.DisplayAlert("警告", "任务进行中,是否结束任务?", "结束任务", "继续任务");

            if (isStopTask)
            {
                //强制结束任务
                await StopTaskAsync(true);
            }
            else
            {
                //阻止页面退出
                context.PreventNavigation();
            }
        }
    }
}

 

VS2022自带的安卓模拟器上调试运行,开始长时间任务后,点击BACK键退出页面,弹窗询问用户,如果选择继续任务,则阻止页面退出,测试效果符合预期。

DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp

 

posted on 2024-08-11 20:33  SunnyTrudeau  阅读(23)  评论(0编辑  收藏  举报