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

执行模式
通常,除了应用程序需要从用户获取关键信息的特殊情况外,您的应用程序可能会使用无模式页面。 然后,应用程序可以显示用户在输入此关键信息之前无法解除的模态页面。
但是,一个小问题是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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值