美文网首页程序员
UWP不归路——自定义后退事件管理

UWP不归路——自定义后退事件管理

作者: anyesu | 来源:发表于2016-07-13 01:57 被阅读801次
双击退出

在UWP中可以调用如下方法对后退按钮进行事件处理,比如页面导航,退出全屏,双击退出等等。

// 注册后退事件
SystemNavigationManager.GetForCurrentView().BackRequested += PageBackRequested;

// 后退事件处理
private void PageBackRequested(object sender, BackRequestedEventArgs e)
{
    e.Handled = true;// 阻止后面注册的事件继续执行
    // TODO
}

// 注销
SystemNavigationManager.GetForCurrentView().BackRequested -= PageBackRequested;

通过上面的调用已经可以对后退按钮定制不同的点击效果,但是,还存在一个问题。应用中肯定会有很多的页面,每个页面对于后退按钮的处理需求肯定会有所不同,多次注册后退事件(连续注册不注销)是难免的,注册后退事件实际上是把每个事件处理依次加入到一个事件队列中去,每当点击后退按钮的时候,就会按照先后顺序依次执行事件处理,因此就会存在后面处理和前面的处理产生冲突导致无法实现预期中的效果,还需要做一些费劲的特殊处理才能正常使用。

所以可以做一个简单的封装,实现只处理最后注册的事件

using System;
using System.Collections;
using Windows.UI.Core;

namespace indi.anyesu.UWP.Core.Managers
{
    //
    // 自定义后退事件管理:
    //     允许只调用最后注册的后退事件,而SystemNavigationManager的后退事件是按顺序依次执行的。
    public sealed class BackEventManager
    {

        private static Stack BackEventStack = new Stack();

        //
        // 注册后退事件
        //
        public static void Register(EventHandler<BackRequestedEventArgs> PageBackRequested)
        {
            if (BackEventStack.Count > 0)
            {
                SystemNavigationManager.GetForCurrentView().BackRequested -= BackEventStack.Peek() as EventHandler<BackRequestedEventArgs>;
            }
            SystemNavigationManager.GetForCurrentView().BackRequested += PageBackRequested;// 注册到系统自带的后退事件队列
            BackEventStack.Push(PageBackRequested);
        }

        //
        // 注销最后注册的后退事件
        //
        public static void Unregister(EventHandler<BackRequestedEventArgs> PageBackRequested)
        {
            if (BackEventStack.Count > 0)
            {
                var top = BackEventStack.Peek() as EventHandler<BackRequestedEventArgs>;
                if (PageBackRequested.Equals(top))
                {
                    SystemNavigationManager.GetForCurrentView().BackRequested -= PageBackRequested;
                    BackEventStack.Pop();
                    if (BackEventStack.Count > 0)
                    {
                        SystemNavigationManager.GetForCurrentView().BackRequested += BackEventStack.Peek() as EventHandler<BackRequestedEventArgs>;
                    }
                }
            }
        }
    }
}

主要思路是将所有注册的事件处理压入BackEventStack这个栈当中,保证系统自带的后退事件处理队列当中最多只有一个事件处理,即最后注册的那个。调用方法如下:

BackEventManager.Register(PageBackRequested);// 注册后退事件
BackEventManager.Unregister(PageBackRequested);// 注销后退事件
// *注意,最好保证注册和注销成对出现(如在页面的OnNavigatedTo方法中注册,OnNavigatedFrom方法中注销),避免出现冲突。

有了这个后退事件管理器之后,可以在APP初始化的时候完成后退事件的注册,实现一个统一的后退事件处理,特殊页面特殊处理,这样就不用到处贴代码了,维护起来更方便。
在App.xaml.cs中修改OnLaunched方法,如下所示:

/// <summary>
/// 在应用程序由最终用户正常启动时进行调用。
/// 将在启动应用程序以打开特定文件等情况下使用。
/// </summary>
/// <param name="e">有关启动请求和过程的详细信息。</param>
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;
    // 不要在窗口已包含内容时重复应用程序初始化,
    // 只需确保窗口处于活动状态
    if (rootFrame == null)
    {
        // 创建要充当导航上下文的框架,并导航到第一页
        rootFrame = new Frame();
        rootFrame.NavigationFailed += OnNavigationFailed;
        // 下面这句话是关键
        rootFrame.Navigated += OnNavigated;// 注册页面加载完毕事件
        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: 从之前挂起的应用程序加载状态
        }
        // 将框架放在当前窗口中
        Window.Current.Content = rootFrame;
    }
    if (rootFrame.Content == null)
    {
        // 当导航堆栈尚未还原时,导航到第一页,
        // 并通过将所需信息作为导航参数传入来配置
        rootFrame.Navigate(typeof(MainPage), e.Arguments);
    }
    // 如果是移动端,则设置可以设置顶部状态栏(电量、时间...)的颜色、是否显示
    if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
    {
        StatusBar statusBar = StatusBar.GetForCurrentView();
        // statusBar.ForegroundColor = Colors.White;// 设置背景色
        await statusBar.HideAsync();// 隐藏状态栏
    }
    Window.Current.Activate();// 确保当前窗口处于活动状态
}
// 在OnNavigated方法中进行事件的注册和控制后退按钮的显示与否
private void OnNavigated(object sender, NavigationEventArgs e)
{
    //显示标题栏后退按钮
    //SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = ((Frame)sender).CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;// 在PC客户端左上角显示后退按钮
    BackEventManager.Register(PageBackRequested);// 注册后退事件
}

这里最关键的一句就是rootFrame.Navigated += OnNavigated;,在PageBackRequested方法中做了统一的后退事件处理,比如在我的应用中,我希望显示MainPage时后退按钮能够双击彻底退出应用,显示其他页面时后退按钮能够处理正常的页面后退导航,具体处理如下:

private void PageBackRequested(object sender, BackRequestedEventArgs e)
{
    e.Handled = true;
    GoBack();
}
/// <summary>
/// 自定义全局后退事件处理
/// </summary>
public static async void GoBack()
{
    var rootFrame = Window.Current.Content as Frame;// App的根Frame
    if (rootFrame == null)
    {
        return;
    }
    if (rootFrame.CurrentSourcePageType == typeof(MainPage))// 判断rootFrame 当前页面类型是否为MainPage
    {
        if (!IsQuit)// IsQuit表示是否已点击了后退按钮,用来处理双击事件
        {
            IsQuit = true;
            await (rootFrame.Content as MainPage).showMessage("再按一次返回键退出", Colors.Red);// 异步调出页面的提示框
            IsQuit = false;
        }
        else
        {
            Current.Exit();// 彻底退出App
        }
    }
    else if (rootFrame.CanGoBack)
    {
        rootFrame.GoBack();
    }
}

在MainPage中

public async Task showMessage(string msg, Color color)
{
    Hint.Text = msg;// 设置提示框文本内容
    if (color != null)
    {
        if (color == Colors.Green)
        {
            color = Color.FromArgb(255, 91, 159, 82);
        }
        messageBorder.Background = new SolidColorBrush(color);// 设置提示框背景色
    }
    message.Visibility = Visibility.Visible;// 显示提示框
    await Task.Delay(1500);// 延时1500ms
    message.Visibility = Visibility.Collapsed;// 隐藏提示框
}

//以下为xaml内容,一个自定义的文本提示框
<Grid x:Name="message" RelativePanel.AlignVerticalCenterWithPanel="True" RelativePanel.AlignHorizontalCenterWithPanel="True" Visibility="Collapsed">
    <Border x:Name="messageBorder" CornerRadius="10" Background="#5B9F52"></Border>
    <ScrollViewer VerticalAlignment="Center" MaxHeight="120" VerticalScrollBarVisibility="Auto" BorderThickness="0">
        <TextBlock x:Name="Hint" Foreground="White" RelativePanel.AlignHorizontalCenterWithPanel="True" RelativePanel.AlignVerticalCenterWithPanel="True" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0" TextWrapping="Wrap" Padding="10" MinWidth="120" MinHeight="30" FontFamily="Resources/FontAwesome.otf#FontAwesome" FontSize="16"/>
    </ScrollViewer>
</Grid>

总结


在我的开发过程中,这个后退事件管理器已经基本满足所有需求了,不过每次执行后退事件的时候都很任性的抛弃了之前注册的事件,之后会考虑加入“与前面注册的事件共存”的方法并做一些优化,以便灵活地适应更多的通用需求。

相关文章

  • UWP不归路——自定义后退事件管理

    在UWP中可以调用如下方法对后退按钮进行事件处理,比如页面导航,退出全屏,双击退出等等。 通过上面的调用已经可以对...

  • UWP不归路——学习资源

    前言 故事要从那个键盘机的时代说起,塞班系统可谓是碾压一切,可以安装各种狂拽酷炫吊炸天的应用,论坛上各种刷机,破解...

  • UWP应用中处理返回事件

    UWP应用中处理返回事件 UWP应用由于能被窗口化,我们能够在一些系统自带应用如:设置、Groove Music中...

  • vue兄弟组件间的通信

    1.vuex2.自定义公共事件管理通信

  • Vue中非父子组件通信

    空实例与自定义事件$emit$on Vuex状态管理statemutationscommit

  • 第9章-Spring的事件机制

    Spring 容器提供了事件管理机制,Spring 容器内部很多节点都会发布事件,也支持自定义事件。 一、事件机制...

  • 不后退

    右脚受伤的我,依旧在死撑着,还是那么倔强。不是不懂得爱惜自己,却只是不想留有遗憾,在我离开的时候。这是最后一次参加...

  • 不后退

    完美并不美, 说的很憔悴 小酒有点烈 少喝也会醉 肘子有点肥 好吃不腻嘴 虽然有些累 但是不后悔 真心比钱贵 有钱...

  • node 内置的模块 - 事件对象 events

    事件对象 events 通过事件监听、派发的方式来更加优化的管理代码,使代码更加简洁,高效 基本使用 自定义事件对...

  • 自定义事件js

    title: 自定义事件date: 2017-06-06 15:36:04tags: 自定义事件 js的自定义事件...

网友评论

    本文标题:UWP不归路——自定义后退事件管理

    本文链接:https://www.haomeiwen.com/subject/fpysjttx.html