第二十四章:页面导航(四)

简介: 执行模式通常,除了应用程序需要从用户获取关键信息的特殊情况外,您的应用程序可能会使用无模式页面。 然后,应用程序可以显示用户在输入此关键信息之前无法解除的模态页面。但是,一个小问题是Android或Windows Phone用户可以通过按设备上的标准后退按钮返回上一页。

执行模式
通常,除了应用程序需要从用户获取关键信息的特殊情况外,您的应用程序可能会使用无模式页面。 然后,应用程序可以显示用户在输入此关键信息之前无法解除的模态页面。
但是,一个小问题是Android或Windows Phone用户可以通过按设备上的标准后退按钮返回上一页。 要强制执行模式 - 确保用户在离开页面之前输入所需信息 - 应用程序必须禁用该按钮。
ModalEnforcement程序演示了这种技术。 主页仅包含一个Button:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ModalEnforcement.ModalEnforcementHomePage"
             Title="Main Page">
    <Button Text="Go to Modal Page"
            HorizontalOptions="Center"
            VerticalOptions="Center"
            Clicked="OnGoToButtonClicked" />
</ContentPage>

代码隐藏文件通过导航到模式页面来处理按钮的Clicked事件:

public partial class ModalEnforcementHomePage : ContentPage
{
    public ModalEnforcementHomePage()
    {
        InitializeComponent();
    }
    async void OnGoToButtonClicked(object sender, EventArgs args)
    {
        await Navigation.PushModalAsync(new ModalEnforcementModalPage());
    }
}

ModalEnforcementModalPage的XAML文件包含两个Entry元素,一个Picker元素和一个标记为Done的Button。 标记比您预期的更广泛,因为它包含一个MultiTrigger,只有当某些内容被输入到两个Entry元素中并且某些内容也被输入到Picker中时,才会将按钮的IsEnabled属性设置为True。 这个MultiTrigger需要三个隐藏的Switch元素,使用第23章“触发器和行为”中讨论的技术:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ModalEnforcement.ModalEnforcementModalPage"
             Title="Modal Page">
    <StackLayout Padding="20, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="Enter Name"
               VerticalOptions="CenterAndExpand" />
        <!-- Invisible Switch to help with MultiTrigger logic -->
        <Switch x:Name="switch1" IsVisible="False">
            <Switch.Triggers>
                <DataTrigger TargetType="Switch"
                             Binding="{Binding Source={x:Reference entry1}, Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsToggled" Value="True" />
                </DataTrigger>
            </Switch.Triggers>
        </Switch>
        <Entry x:Name="entry2"
               Text=""
               Placeholder="Enter Email Address"
               VerticalOptions="CenterAndExpand" />

        <!-- Invisible Switch to help with MultiTrigger logic -->
        <Switch x:Name="switch2" IsVisible="False">
            <Switch.Triggers>
                <DataTrigger TargetType="Switch"
                             Binding="{Binding Source={x:Reference entry2}, Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsToggled" Value="True" />
                </DataTrigger>
            </Switch.Triggers>
        </Switch>
        <Picker x:Name="picker"
                Title="Favorite Programming Language"
                VerticalOptions="CenterAndExpand">
            <Picker.Items>
                <x:String>C#</x:String>
                <x:String>F#</x:String>
                <x:String>Objective C</x:String>
                <x:String>Swift</x:String>
                <x:String>Java</x:String>
            </Picker.Items>
        </Picker>
        <!-- Invisible Switch to help with MultiTrigger logic -->
        <Switch x:Name="switch3" IsVisible="False">
            <Switch.Triggers>
                <DataTrigger TargetType="Switch"
                             Binding="{Binding Source={x:Reference picker}, Path=SelectedIndex}"
                             Value="-1">
                    <Setter Property="IsToggled" Value="True" />
                </DataTrigger>
            </Switch.Triggers>
        </Switch>
        <Button x:Name="doneButton"
 Text="Done"
 IsEnabled="False"
 HorizontalOptions="Center"
 VerticalOptions="CenterAndExpand"
 Clicked="OnDoneButtonClicked">
            <Button.Triggers>
                <MultiTrigger TargetType="Button">
                    <MultiTrigger.Conditions>
                        <BindingCondition Binding="{Binding Source={x:Reference switch1},
                                                                      Path=IsToggled}"
                                                    Value="False" />
                        <BindingCondition Binding="{Binding Source={x:Reference switch2},
                                                                      Path=IsToggled}"
                                                    Value="False" />

                        Chapter 24 Page navigation 950
                        <BindingCondition Binding="{Binding Source={x:Reference switch3},
                                                                      Path=IsToggled}"
                                                    Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter Property="IsEnabled" Value="True" />
                </MultiTrigger>
            </Button.Triggers>
        </Button>
    </StackLayout>
</ContentPage>

在现实生活中,可能还会检查电子邮件地址是否有效。 XAML文件中的简单逻辑只检查是否存在至少一个字符。
这是首次出现的模态页面,当时尚未输入任何内容。 请注意,“完成”按钮已禁用:
2019_04_16_095311
通常,用户仍然可以按Android和Windows Phone屏幕左下方的标准后退按钮返回主页面。 要禁止“后退”按钮的正常行为,模态页面必须覆盖虚拟OnBackButtonPressed方法。 您可以在此覆盖中提供自己的后退按钮处理并返回true。 要完全禁用“后退”按钮,只需返回true而不执行任何其他操作。 要允许进行默认的“后退”按钮处理,请调用基类实现。 以下是ModalEnforcementModalPage的代码隐藏文件的用法:

public partial class ModalEnforcementModalPage : ContentPage
{
    public ModalEnforcementModalPage()
    {
        InitializeComponent();
    }
    protected override bool OnBackButtonPressed()
    {
        if (doneButton.IsEnabled)
        {
            return base.OnBackButtonPressed();
        }
        return true;
    }
    async void OnDoneButtonClicked(object sender, EventArgs args)
    {
        await Navigation.PopModalAsync();
    }
}

仅当启用XAML文件中的“完成”按钮时,OnBackButtonPressed覆盖才会调用方法的基类实现并返回从该实现返回的值。 这会导致模态页面返回到调用它的页面。 如果禁用“完成”按钮,则覆盖返回true,表示它已完成执行所需的“后退”按钮所需的所有操作。
像往常一样,完成按钮的Clicked处理程序只调用PopModalAsync。
Page类还定义了一个SendBackButtonPressed,它导致调用OnBackButtonPressed方法。 应该可以通过调用此方法为“完成”按钮实现Clicked处理程序:

void OnDoneButtonClicked(object sender, EventArgs args)
{
    SendBackButtonPressed();
}

虽然这适用于iOS和Android,但它目前无法在Windows运行时平台上运行。
在实际编程中,您更有可能使用ViewModel来累积用户输入模态页面的信息。 在这种情况下,ViewModel本身可以包含一个属性,该属性指示输入的所有信息是否有效。
MvvmEnforcement程序使用这种技术,并包含一个名为LittleViewModel的ViewModel:

namespace MvvmEnforcement
{
    public class LittleViewModel : INotifyPropertyChanged
    {
        string name, email;
        string[] languages = { "C#", "F#", "Objective C", "Swift", "Java" };

        int languageIndex = -1;
        bool isValid;
        public event PropertyChangedEventHandler PropertyChanged;
        public string Name
        {
            set
            {
                if (name != value)
                {
                    name = value;
                    OnPropertyChanged("Name");
                    TestIfValid();
                }
            }
            get { return name; }
        }
        public string Email
        {
            set
            {
                if (email != value)
                {
                    email = value;
                    OnPropertyChanged("Email");
                    TestIfValid();
                }
            }
            get { return email; }
        }
        public IEnumerable<string> Languages
        {
            get { return languages; }
        }
        public int LanguageIndex
        {
            set
            {
                if (languageIndex != value)
                {
                    languageIndex = value;
                    OnPropertyChanged("LanguageIndex");
                    if (languageIndex >= 0 && languageIndex < languages.Length)
                    {
                        Language = languages[languageIndex];
                        OnPropertyChanged("Language");
                    }
                    TestIfValid();
                }

            }
            get { return languageIndex; }
        }
        public string Language { private set; get; }
        public bool IsValid
        {
            private set
            {
                if (isValid != value)
                {
                    isValid = value;
                    OnPropertyChanged("IsValid");
                }
            }
            get { return isValid; }
        }
        void TestIfValid()
        {
            IsValid = !String.IsNullOrWhiteSpace(Name) &&
            !String.IsNullOrWhiteSpace(Email) &&
           !String.IsNullOrWhiteSpace(Language);
        }
        void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Name和Email属性的类型为string,用于绑定Entry元素的Text属性。 LanguageIndex属性旨在绑定到Picker的SelectedIndex属性。 但是LanguageIndex的set访问器使用该值从Languages集合中的字符串数组中设置string类型的Language属性。
只要Name,Email或LanguageIndex属性发生更改,就会调用TestIfValid方法来设置IsValid属性。 此属性可以绑定到Button的IsEnabled属性。
MvvmEnforcement中的主页与ModalEnforcement中的主页相同,但当然模态页面的XAML文件更简单并实现了所有数据绑定:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MvvmEnforcement.MvvmEnforcementModalPage"
             Title="Modal Page">
    <StackLayout Padding="20, 0">
        <Entry Text="{Binding Name}"
               Placeholder="Enter Name"
               VerticalOptions="CenterAndExpand" />
        <Entry Text="{Binding Email}"
               Placeholder="Enter Email Address"
               VerticalOptions="CenterAndExpand" />

        <Picker x:Name="picker"
                Title="Favorite Programming Language"
                SelectedIndex="{Binding LanguageIndex}"
                VerticalOptions="CenterAndExpand" />
        <Button Text="Done"
                IsEnabled="{Binding IsValid}"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                Clicked="OnDoneButtonClicked" />
    </StackLayout>
</ContentPage>

标记包含对ViewModel中的属性的四个绑定。
模态页面的代码隐藏文件负责实例化LittleViewModel并将对象设置为页面的BindingContext属性,它在构造函数中执行。 构造函数还访问ViewModel的Languages集合以设置Picker的Items属性。 (遗憾的是,Items属性不受可绑定属性的支持,因此不可绑定。)
该文件的其余部分与ModalEnforcement中的模态页面非常相似,只是OnBackButtonPressed覆盖访问LittleViewModel的IsValid属性以确定是调用基类实现还是返回true:

public partial class MvvmEnforcementModalPage : ContentPage
{
    public MvvmEnforcementModalPage()
    {
        InitializeComponent();
        LittleViewModel viewModel = new LittleViewModel();
        BindingContext = viewModel;
        // Populate Picker Items list.
        foreach (string language in viewModel.Languages)
        {
            picker.Items.Add(language);
        }
    }
    protected override bool OnBackButtonPressed()
    {
        LittleViewModel viewModel = (LittleViewModel)BindingContext;
        return viewModel.IsValid ? base.OnBackButtonPressed() : true;
    }
    async void OnDoneButtonClicked(object sender, EventArgs args)
    {
        await Navigation.PopModalAsync();
    }
}
目录
相关文章
|
2月前
|
小程序 API
【微信小程序】-- 页面导航 -- 声明式导航(二十二)
【微信小程序】-- 页面导航 -- 声明式导航(二十二)
|
9月前
|
小程序 算法 前端开发
小程序之移花宫-自定义底部标签图标---【浅入深出系列005】
小程序之移花宫-自定义底部标签图标---【浅入深出系列005】
|
JSON 小程序 JavaScript
走进小程序【四】小程序自定义Component如何使用,手把手封装一个底部Tabbar栏
走进小程序【四】小程序自定义Component如何使用,手把手封装一个底部Tabbar栏
168 0
走进小程序【四】小程序自定义Component如何使用,手把手封装一个底部Tabbar栏
|
Android开发 索引 iOS开发
第二十四章:页面导航(十七)
像现实生活中的应用程序理想情况下,用户在终止并重新启动应用程序时不应该知道。应用程序体验应该是连续且无缝的。即使程序没有一直运行,一个半月进入的条目从未完成也应该在一周后处于相同的状态。NoteTaker程序允许用户记录由标题和一些文本组成的注释。
525 0
|
JavaScript 前端开发 Android开发
第二十四章:页面导航(十六)
保存和恢复导航堆栈 许多多页面应用程序的页面体系结构比DataTransfer6更复杂,您需要一种通用的方法来保存和恢复整个导航堆栈。此外,您可能希望将导航堆栈的保存与系统方式集成,以保存和恢复每个页面的状态,特别是如果您不使用MVVM。
464 0
|
XML JSON Android开发
第二十四章:页面导航(十五)
保存和恢复页面状态特别是当您开始使用多页面应用程序时,将应用程序的页面视为数据的主要存储库非常有用,而仅仅是作为底层数据的临时可视化和交互式视图。这里的关键词是暂时的。如果您在用户与之交互时保持基础数据是最新的,那么页面可以显示和消失而不必担心。
629 0
|
JavaScript Android开发
第二十四章:页面导航(十四)
切换到ViewModel此时应该很明显,Information类应该真正实现INotifyPropertyChanged。 在DataTransfer5中,Information类已成为InformationViewModel类。
574 0
|
JavaScript Android开发 索引
第二十四章:页面导航(十)
属性和方法调用调用PushAsync或PushModalAsync的页面显然可以直接访问它导航到的类,因此它可以设置属性或调用该页面对象中的方法以将信息传递给它。但是,调用PopAsync或PopModalAsync的页面还有一些工作要做,以确定它返回的页面。
524 0
|
JavaScript Android开发
第二十四章:页面导航(九)
数据传输模式 多页面应用程序中的页面通常需要共享数据,特别是一页面将信息传递到另一页面。 有时此过程类似于函数调用:当HomePage显示项目列表并导航到DetailPage以显示其中一个项目的详细视图时,HomePage必须将该特定项目传递给DetailPage。
606 0
|
Android开发
第二十四章:页面导航(八)
动态页面生成BuildAPage程序是一个多页面应用程序,但BuildAPage项目只包含一个名为BuildAPageHomePage的页面类。 顾名思义,该程序从代码构造一个新页面,然后导航到它。XAML文件允许您在此构造的页面上指定所需内容: <ContentPage xmlns="http://xamarin.
651 0